Contract 0xf3e339d8a0b989114412fa157cc846ebaf4bcbd8 1

Contract Overview

Balance:
25.880614563 MATIC
Txn Hash
Method
Block
From
To
Value [Txn Fee]
0x115ca423890904fa12d420ee55d01b936fc2563102f5cbff3b21dacbab7c065cMarket Order336667702023-03-28 9:42:536 mins ago0xbcddf73ccf1997773daf1a6dc7f698b311dad7fa IN  0xf3e339d8a0b989114412fa157cc846ebaf4bcbd80 MATIC0.000127221001 1.500000016
0x29fe9c29fb4c428900c81a30eb87d4a9e05c340af3b3b9f541a9053cf24e831aMarket Order336667452023-03-28 9:41:597 mins ago0xbcddf73ccf1997773daf1a6dc7f698b311dad7fa IN  0xf3e339d8a0b989114412fa157cc846ebaf4bcbd80 MATIC0.000127101001 1.500000016
0x98ad0f42fde901395560660ab86f72d8e31ab239bb5495934333b4f3270b0ebaMarket Order336667022023-03-28 9:40:278 mins ago0xbcddf73ccf1997773daf1a6dc7f698b311dad7fa IN  0xf3e339d8a0b989114412fa157cc846ebaf4bcbd80 MATIC0.000127101001 1.500000016
0xe9f9c5c084b8297f553c96d0d1948911e47dcba5882ec943dd59473c7a82b130Market Order336666812023-03-28 9:39:439 mins ago0xbcddf73ccf1997773daf1a6dc7f698b311dad7fa IN  0xf3e339d8a0b989114412fa157cc846ebaf4bcbd80 MATIC0.000127101001 1.500000016
0x833bee75d6002b67f42b4e950c01f0eca56b2e65bd12d8bfbd66bf5bd484c247Market Order336572872023-03-28 4:07:015 hrs 41 mins ago0x38dc0109ca66985d154ff47353a4084c55721f48 IN  0xf3e339d8a0b989114412fa157cc846ebaf4bcbd80 MATIC0.00137129993 5.172706195
0xeb4062f8f32e37c116689275b48c523246e7ff78262f5a46e8426667743a74dfMarket Order336572812023-03-28 4:06:495 hrs 42 mins ago0x81e35f765aedeb407c8c717a6c7bfd14afea5429 IN  0xf3e339d8a0b989114412fa157cc846ebaf4bcbd80 MATIC0.001300462202 4.86699926
0xf8932e5717390cd90767ac4a0e792c8a3714749cdcb0b974118462113e733419Market Order336572612023-03-28 4:06:055 hrs 42 mins ago0x38dc0109ca66985d154ff47353a4084c55721f48 IN  0xf3e339d8a0b989114412fa157cc846ebaf4bcbd80 MATIC0.001341033909 5.017525011
0xd5d254ccfebb827db9610c223814c3677cba68d2c324f5dac262a82f3bce9564Market Order336572592023-03-28 4:06:015 hrs 42 mins ago0x81e35f765aedeb407c8c717a6c7bfd14afea5429 IN  0xf3e339d8a0b989114412fa157cc846ebaf4bcbd80 MATIC0.001382509185 5.172706196
0x8ae8afd4fbd3f0a9afc08c8e66e36f1c6fd5b0114c78e885702ca3a6952135feMarket Order336572382023-03-28 4:05:175 hrs 43 mins ago0x81e35f765aedeb407c8c717a6c7bfd14afea5429 IN  0xf3e339d8a0b989114412fa157cc846ebaf4bcbd80 MATIC0.0007777557 2.91
0xb0b6804cd4c88511c28fdfe5de3f05fea96c1ea00b9e65d189fa8b7328a6ef24Market Order336572362023-03-28 4:05:135 hrs 43 mins ago0x38dc0109ca66985d154ff47353a4084c55721f48 IN  0xf3e339d8a0b989114412fa157cc846ebaf4bcbd80 MATIC0.002132956089 5.689976816
0xb2e38b33955a9d3d40cd4365c9e213700569b8ce6bda73ba7d65a4efcab47d52Market Order336572192023-03-28 4:04:375 hrs 44 mins ago0x81e35f765aedeb407c8c717a6c7bfd14afea5429 IN  0xf3e339d8a0b989114412fa157cc846ebaf4bcbd80 MATIC0.000245186273 5.172706196
0x89048e4ae893d8aee028ce486e700be7fd2cb59b278296ce08db690ade6cbd99Market Order336572172023-03-28 4:04:335 hrs 44 mins ago0x38dc0109ca66985d154ff47353a4084c55721f48 IN  0xf3e339d8a0b989114412fa157cc846ebaf4bcbd80 MATIC0.002249329736 6.034823895
0x85e4bd4250042c08939d7de158e619fbe6543f2ff355ed35cbf52d054b5e4b2bMarket Order336572012023-03-28 4:03:595 hrs 45 mins ago0x38dc0109ca66985d154ff47353a4084c55721f48 IN  0xf3e339d8a0b989114412fa157cc846ebaf4bcbd80 MATIC0.001376348491 5.172706196
0xba3b9bd38887f4b535ea1578b570970c9f463cd5afc0da36bc5e0a84f314472dMarket Order336571852023-03-28 4:03:255 hrs 45 mins ago0x81e35f765aedeb407c8c717a6c7bfd14afea5429 IN  0xf3e339d8a0b989114412fa157cc846ebaf4bcbd80 MATIC0.000751061197 2.822700015
0x1400976c346274550866b26483e3abc3d178297ff49ff7c32502c3904ce080cfMarket Order336571842023-03-28 4:03:235 hrs 45 mins ago0x38dc0109ca66985d154ff47353a4084c55721f48 IN  0xf3e339d8a0b989114412fa157cc846ebaf4bcbd80 MATIC0.000741147874 2.822700015
0x55f18d8c8d83f1bc1597bfe0ce343ef0e45caddac42a1e5fd22c23b262b2109eMarket Order336571682023-03-28 4:02:495 hrs 46 mins ago0x81e35f765aedeb407c8c717a6c7bfd14afea5429 IN  0xf3e339d8a0b989114412fa157cc846ebaf4bcbd80 MATIC0.000721746172 2.748807629
0xbe34c809c0d4450bce8928a2aa791887adab77b9921912c695dc0ef3d45a01ebMarket Order336571482023-03-28 4:02:055 hrs 46 mins ago0x81e35f765aedeb407c8c717a6c7bfd14afea5429 IN  0xf3e339d8a0b989114412fa157cc846ebaf4bcbd80 MATIC0.000679090973 2.586353098
0xf977d2a4cc3ecd58b47a189800f9495a97a469dfc2267e12d49905349109c1c4Market Order336571472023-03-28 4:02:035 hrs 46 mins ago0x38dc0109ca66985d154ff47353a4084c55721f48 IN  0xf3e339d8a0b989114412fa157cc846ebaf4bcbd80 MATIC0.000679090973 2.586353098
0xbbb3539142d282d9958345a7beb462622b4c8df1e9909b7947c3f4e32ba330e3Market Order336571342023-03-28 4:01:355 hrs 47 mins ago0x81e35f765aedeb407c8c717a6c7bfd14afea5429 IN  0xf3e339d8a0b989114412fa157cc846ebaf4bcbd80 MATIC0.00063399372 2.382727386
0x6a9d6e25b9f1e90bf75557d5df3800f42081521df6aeaf23171700c842f82c6bMarket Order336571322023-03-28 4:01:315 hrs 47 mins ago0x38dc0109ca66985d154ff47353a4084c55721f48 IN  0xf3e339d8a0b989114412fa157cc846ebaf4bcbd80 MATIC0.00063399372 2.382727386
0x930417282d1ac7f3f98a4ca56d1ef8350de9bdf937183675cdead59f951ccc1bMarket Order336571262023-03-28 4:01:195 hrs 47 mins ago0x9f4f34ce715de1f3c05b2ff80ba86f50a3a637d1 IN  0xf3e339d8a0b989114412fa157cc846ebaf4bcbd80 MATIC0.000650377111 2.433499632
0xeadf66b958dfc0e47e6260e3c7c0b710bf333c7642524d6240ca0d5e8773a0cbMarket Order336571132023-03-28 4:00:515 hrs 48 mins ago0x9f4f34ce715de1f3c05b2ff80ba86f50a3a637d1 IN  0xf3e339d8a0b989114412fa157cc846ebaf4bcbd80 MATIC0.000400771504 1.500000016
0xe63647e43abb0ea5a7e374e00f6aec93954a3a95d7997b0a9d3e4f1dfe2a8fc9Market Order336571092023-03-28 4:00:435 hrs 48 mins ago0x38dc0109ca66985d154ff47353a4084c55721f48 IN  0xf3e339d8a0b989114412fa157cc846ebaf4bcbd80 MATIC0.000688197523 2.586353098
0xa7d875e36d61bd5ea6a20acfef482bc3f2ab0b0650ccb7f5ff0730cfd7fa0ff0Market Order336571062023-03-28 4:00:375 hrs 48 mins ago0x81e35f765aedeb407c8c717a6c7bfd14afea5429 IN  0xf3e339d8a0b989114412fa157cc846ebaf4bcbd80 MATIC0.000688174245 2.586353098
0xcf973b91ee758a273014f7a8a9ab8a29fe99a9e07fb19ed3c042f4c7f397b03fMarket Order336570952023-03-28 4:00:135 hrs 48 mins ago0x9f4f34ce715de1f3c05b2ff80ba86f50a3a637d1 IN  0xf3e339d8a0b989114412fa157cc846ebaf4bcbd80 MATIC0.000118388645 2.498916026
[ Download CSV Export 
Latest 25 internal transaction
Parent Txn Hash Block From To Value
0x15ebdf917e13fdcde504f4a7cabc4164d4ee9a7941888908122b86fa4cdc4250326157362023-03-02 13:13:3325 days 20 hrs ago 0xf3e339d8a0b989114412fa157cc846ebaf4bcbd80xeb0b664f1c5f171bbc1c45e332a753f45bbc56d20.002971692 MATIC
0x9365a50ea85e031cd0777fee185fb94178a954adce531be99f1fa0bab9b730e5326156712023-03-02 13:11:1525 days 20 hrs ago 0xf3e339d8a0b989114412fa157cc846ebaf4bcbd80xeb0b664f1c5f171bbc1c45e332a753f45bbc56d20.000713376 MATIC
0xefb882b7f86befb5872ff4d40c82e5bfe68634b10b45a7c037ff07522e48fbde326156642023-03-02 13:11:0125 days 20 hrs ago 0xf3e339d8a0b989114412fa157cc846ebaf4bcbd80x4284a0e68681052013abeddad6ade212a1b839bd0.001842528 MATIC
0x7f4f47b0741dd73d65ede16bf4143ebc666fa73cde5fd8a94ec59f49b085c5cb326155782023-03-02 13:07:5725 days 20 hrs ago 0xf3e339d8a0b989114412fa157cc846ebaf4bcbd80xe8e804e453ffe018f5fc249fb62c69cd37d57b6f0.000713376 MATIC
0xbea780b709ace975590ab613aea42ddaa30872752d0d570612f05a271489d612326147892023-03-02 12:40:0125 days 21 hrs ago 0xf3e339d8a0b989114412fa157cc846ebaf4bcbd80x45208c10e922d932c0a638e7d8e92307fc73b40b0.00339771 MATIC
0xa740355e0e3b95e1053e5ded94ad7b7b2d05c58f6771f271b812619fa2a6f131326142412023-03-02 12:20:3725 days 21 hrs ago 0xf3e339d8a0b989114412fa157cc846ebaf4bcbd80x1b48ab69711b6a54fced6e63acb7ac37b0545b330.001139376 MATIC
0xbeafafdeaef9e3f6773622e671fce65296aafb4a33fa721bf441df11e40c02bc326142372023-03-02 12:20:2725 days 21 hrs ago 0xf3e339d8a0b989114412fa157cc846ebaf4bcbd80x06b6a2d959d90a83257dbecb816387b7ac53d6af0.000713376 MATIC
0xd35190ebed2992246136bfb81a98ccc1d1624d59e95d092b10d59c9d85d13411326142252023-03-02 12:20:0325 days 21 hrs ago 0xf3e339d8a0b989114412fa157cc846ebaf4bcbd80x21d94f21d0d1c140501da92d0ca3a884d60daf1a0.000426 MATIC
0x020a76dbc257b799fb88d64e43daf6b8e55209d111da42c2f7acb854dca70fa2326142062023-03-02 12:19:2125 days 21 hrs ago 0xf3e339d8a0b989114412fa157cc846ebaf4bcbd80x4a34b4a37d67ba66d11294b7f56f01de1315312f0.000713376 MATIC
0x239225f3beb127483014157f401b20813a099fadef46fccc88558113542e1f7c326141882023-03-02 12:18:4325 days 21 hrs ago 0xf3e339d8a0b989114412fa157cc846ebaf4bcbd80xf44aaca1c61b91919bcb0e417f71193d29b80b1f0.00422919 MATIC
0xfe5afb68e7503763531eef019696a07d5375609fb21139d183a26a9a08c835a2326141782023-03-02 12:18:2325 days 21 hrs ago 0xf3e339d8a0b989114412fa157cc846ebaf4bcbd80x5a5687fdd05b1acc9d0465cf5b1774419c54e8dc0.000426 MATIC
0x024856c064bd7fbf6d761d0fb82b3b532304def9f66f85ed6e964c562922e46c326141292023-03-02 12:16:3925 days 21 hrs ago 0xf3e339d8a0b989114412fa157cc846ebaf4bcbd80x66887d33da51f913123e1df3d68cc8fd6bc426840.007883754 MATIC
0x3670483f40b63b032ade5d2f26ca0e70a88d0616dff00fe5ef1c6c8a8b7f7b97326141172023-03-02 12:16:1325 days 21 hrs ago 0xf3e339d8a0b989114412fa157cc846ebaf4bcbd80x100975b30c19b21dcfb348dcd80807b44201b6e90.000713376 MATIC
0xe74c171717f8e463f65d2752ca852bc5c7066877080f67555a351061df12fe67326140942023-03-02 12:15:2325 days 21 hrs ago 0xf3e339d8a0b989114412fa157cc846ebaf4bcbd80x47e693bfff5bdd6e668d7af27d4ac03d11c699060.000852 MATIC
0x8ee7bdbc8b7bb49ed580769c27c2a35f41657d31f3ce24d967d020c59e6806a1326140932023-03-02 12:15:2125 days 21 hrs ago 0xf3e339d8a0b989114412fa157cc846ebaf4bcbd80x09023e88a53735caaab239a48ae65a7ac571dbfb0.001416528 MATIC
0x2b41f1f4fd4ec721471c15e8346a18d37e3fada3f7e516523513ca3d52e04f64326140592023-03-02 12:14:0925 days 21 hrs ago 0xf3e339d8a0b989114412fa157cc846ebaf4bcbd80x6e9cea97e447426540d72238bd62c1355c5268160.007062444 MATIC
0x1bcfb968610bb2568771ec62677f8dab59e08ffc0b9f219549d09665e748e177326140572023-03-02 12:14:0525 days 21 hrs ago 0xf3e339d8a0b989114412fa157cc846ebaf4bcbd80x03ba2167ed2809ccb6f760c5bcbefd1633a78c6b0.005497008 MATIC
0xae9b55964571a606314f095846fca2439af2d5c5e901cf0c7c2f8062836b8c4d326140552023-03-02 12:14:0125 days 21 hrs ago 0xf3e339d8a0b989114412fa157cc846ebaf4bcbd80xaad064eb6f54ab4a32554a1ac7d567fa4ab84df10.000426 MATIC
0xb5e44f6b1d426f0159cc4d3c10f01b1569d3029adbe77eec29c231561cd6d7ea326140172023-03-02 12:12:4125 days 21 hrs ago 0xf3e339d8a0b989114412fa157cc846ebaf4bcbd80xc0e96c8f27c70b2e9bd692957b92f7c4fee1a3190.000713376 MATIC
0x46ab904f99fb7a6a08c1ab5264fd178979820ab6b23ff1f5a5aa259d60aba259326140112023-03-02 12:12:2725 days 21 hrs ago 0xf3e339d8a0b989114412fa157cc846ebaf4bcbd80x9e4ffcaa1a6c59c00db07f713e3f9a765cd1945c0.000713376 MATIC
0x62cbdaf66d1b762a54d25468ef8cf95c8a26c2c6e9b5b26b4dcb3e15d18f144e326140012023-03-02 12:12:0725 days 21 hrs ago 0xf3e339d8a0b989114412fa157cc846ebaf4bcbd80x95628e642df11a7d223eeb3d9733a0e597d1809f0.003248844 MATIC
0x75b33150c0316c6fba383f6b92163be8280e419b0b803af5a8906b32fe07ce76326140002023-03-02 12:12:0525 days 21 hrs ago 0xf3e339d8a0b989114412fa157cc846ebaf4bcbd80xe3c2c3675797a875d1319de44ea761268bf7de5b0.002545686 MATIC
0xed7eb0a0450d046eac9a9205116f80c99a7842406905a382640f519207abe9f6326139932023-03-02 12:11:4925 days 21 hrs ago 0xf3e339d8a0b989114412fa157cc846ebaf4bcbd80x499e03c932ced340916cae5aa0493c2e643460c30.00508119 MATIC
0x8b2439c90ef3d34307025e96a630b89188fc394c7e9bf21cc55904f5010f8bf6326127902023-03-02 11:29:1325 days 22 hrs ago 0xf3e339d8a0b989114412fa157cc846ebaf4bcbd80x7cb990767e31cbdabf4d2eec887d44a84b7aa1a50.000426 MATIC
0x7ff49adc1228e7fb1fc0660e8a5049fac66c830825d0c8f6abbbfe9a36282cb9326127292023-03-02 11:27:0325 days 22 hrs ago 0xf3e339d8a0b989114412fa157cc846ebaf4bcbd80x821f958cc05f28237ddbe715a0feb31923f71af70.014125482 MATIC
[ Download CSV Export 
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
Mangrove

Compiler Version
v0.8.10+commit.fc410830

Optimization Enabled:
Yes with 20000 runs

Other Settings:
default evmVersion, GNU AGPLv3 license

Contract Source Code (Solidity Standard Json-Input format)

File 1 of 11 : AbstractMangrove.sol
// SPDX-License-Identifier:	AGPL-3.0

// AbstractMangrove.sol

// Copyright (C) 2021 Giry SAS.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published
// by the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program.  If not, see <https://www.gnu.org/licenses/>.
pragma solidity ^0.8.10;
pragma abicoder v2;
import {MgvLib as ML} from "./MgvLib.sol";

import {MgvOfferMaking} from "./MgvOfferMaking.sol";
import {MgvOfferTakingWithPermit} from "./MgvOfferTakingWithPermit.sol";
import {MgvGovernable} from "./MgvGovernable.sol";

/* `AbstractMangrove` inherits the three contracts that implement generic Mangrove functionality (`MgvGovernable`,`MgvOfferTakingWithPermit` and `MgvOfferMaking`) but does not implement the abstract functions. */
abstract contract AbstractMangrove is
  MgvGovernable,
  MgvOfferTakingWithPermit,
  MgvOfferMaking
{
  constructor(
    address governance,
    uint gasprice,
    uint gasmax,
    string memory contractName
  )
    MgvOfferTakingWithPermit(contractName)
    MgvGovernable(governance, gasprice, gasmax)
  {}
}

File 2 of 11 : IERC20.sol
// SPDX-License-Identifier: Unlicense

// IERC20.sol

// This is free and unencumbered software released into the public domain.

// Anyone is free to copy, modify, publish, use, compile, sell, or distribute this software, either in source code form or as a compiled binary, for any purpose, commercial or non-commercial, and by any means.

// In jurisdictions that recognize copyright laws, the author or authors of this software dedicate any and all copyright interest in the software to the public domain. We make this dedication for the benefit of the public at large and to the detriment of our heirs and successors. We intend this dedication to be an overt act of relinquishment in perpetuity of all present and future rights to this software under copyright law.

// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

// For more information, please refer to <https://unlicense.org/>

/* `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;
pragma abicoder v2;

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

  /// for wETH contract
  function deposit() external payable;

  function withdraw(uint) external;

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

File 3 of 11 : Mangrove.sol
// SPDX-License-Identifier:	AGPL-3.0

// Mangrove.sol

// Copyright (C) 2021 Giry SAS.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published
// by the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program.  If not, see <https://www.gnu.org/licenses/>.
pragma solidity ^0.8.10;
pragma abicoder v2;
import {MgvLib as ML, P} from "./MgvLib.sol";

import {AbstractMangrove} from "./AbstractMangrove.sol";

/* <a id="Mangrove"></a> The `Mangrove` contract implements the "normal" version of Mangrove, where the taker flashloans the desired amount to each maker. Each time, makers are called after the loan. When the order is complete, each maker is called once again (with the orderbook unlocked). */
contract Mangrove is AbstractMangrove {
  using P.OfferDetail for P.OfferDetail.t;

  constructor(
    address governance,
    uint gasprice,
    uint gasmax
  ) AbstractMangrove(governance, gasprice, gasmax, "Mangrove") {}

  function executeEnd(MultiOrder memory mor, ML.SingleOrder memory sor)
    internal
    override
  {}

  function beforePosthook(ML.SingleOrder memory sor) internal override {}

  /* ## Flashloan */
  /*
     `flashloan` is for the 'normal' mode of operation. It:
     1. Flashloans `takerGives` `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.
   */
  function flashloan(ML.SingleOrder calldata sor, address taker)
    external
    override
    returns (uint gasused)
  {
    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 a pair forever; if we incorrectly blame the maker, a blacklisted taker can unfairly make makers fail all the time. Of course we assume the Mangrove is not blacklisted. Also note that this setup doesn't not work well with tokens that take fees or recompute balances at transfer time. */
      if (transferTokenFrom(sor.inbound_tkn, taker, address(this), sor.gives)) {
        if (
          transferToken(sor.inbound_tkn, sor.offerDetail.maker(), sor.gives)
        ) {
          gasused = makerExecute(sor);
        } else {
          innerRevert([bytes32("mgv/makerReceiveFail"), bytes32(0), ""]);
        }
      } else {
        innerRevert([bytes32("mgv/takerTransferFail"), "", ""]);
      }
    }
  }
}

File 4 of 11 : MgvGovernable.sol
// SPDX-License-Identifier:	AGPL-3.0

// MgvGovernable.sol

// Copyright (C) 2021 Giry SAS.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published
// by the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program.  If not, see <https://www.gnu.org/licenses/>.
pragma solidity ^0.8.10;
pragma abicoder v2;
import {HasMgvEvents, P} from "./MgvLib.sol";
import {MgvRoot} from "./MgvRoot.sol";

contract MgvGovernable is MgvRoot {
  // using P.Offer for P.Offer.t;
  // using P.OfferDetail for P.OfferDetail.t;
  using P.Global for P.Global.t;
  using P.Local for P.Local.t;
  /* The `governance` address. Governance is the only address that can configure parameters. */
  address public governance;

  constructor(
    address _governance,
    uint _gasprice,
    uint gasmax
  ) MgvRoot() {
    unchecked {
      emit NewMgv();

      /* Initially, governance is open to anyone. */

      /* Initialize vault to governance address, and set initial gasprice and gasmax. */
      setVault(_governance);
      setGasprice(_gasprice);
      setGasmax(gasmax);
      /* Initialize governance to `_governance` after parameter setting. */
      setGovernance(_governance);
    }
  }

  /* ## `authOnly` check */

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

  /* # Set configuration and Mangrove state */

  /* ## Locals */
  /* ### `active` */
  function activate(
    address outbound_tkn,
    address inbound_tkn,
    uint fee,
    uint density,
    uint offer_gasbase
  ) public {
    unchecked {
      authOnly();
      locals[outbound_tkn][inbound_tkn] = locals[outbound_tkn][inbound_tkn]
        .active(true);
      emit SetActive(outbound_tkn, inbound_tkn, true);
      setFee(outbound_tkn, inbound_tkn, fee);
      setDensity(outbound_tkn, inbound_tkn, density);
      setGasbase(outbound_tkn, inbound_tkn, offer_gasbase);
    }
  }

  function deactivate(address outbound_tkn, address inbound_tkn) public {
    authOnly();
    locals[outbound_tkn][inbound_tkn] = locals[outbound_tkn][inbound_tkn]
      .active(false);
    emit SetActive(outbound_tkn, inbound_tkn, false);
  }

  /* ### `fee` */
  function setFee(
    address outbound_tkn,
    address inbound_tkn,
    uint fee
  ) public {
    unchecked {
      authOnly();
      /* `fee` is in basis points, i.e. in percents of a percent. */
      require(fee <= 500, "mgv/config/fee/<=500"); // at most 5%
      locals[outbound_tkn][inbound_tkn] = locals[outbound_tkn][inbound_tkn].fee(
        fee
      );
      emit SetFee(outbound_tkn, inbound_tkn, fee);
    }
  }

  /* ### `density` */
  /* Useless if `global.useOracle != 0` */
  function setDensity(
    address outbound_tkn,
    address inbound_tkn,
    uint density
  ) public {
    unchecked {
      authOnly();

      require(checkDensity(density), "mgv/config/density/112bits");
      //+clear+
      locals[outbound_tkn][inbound_tkn] = locals[outbound_tkn][inbound_tkn]
        .density(density);
      emit SetDensity(outbound_tkn, inbound_tkn, density);
    }
  }

  /* ### `gasbase` */
  function setGasbase(
    address outbound_tkn,
    address inbound_tkn,
    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(
        uint24(offer_gasbase) == offer_gasbase,
        "mgv/config/offer_gasbase/24bits"
      );
      //+clear+
      locals[outbound_tkn][inbound_tkn] = locals[outbound_tkn][inbound_tkn]
        .offer_gasbase(offer_gasbase);
      emit SetGasbase(outbound_tkn, inbound_tkn, 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(checkGasprice(gasprice), "mgv/config/gasprice/16bits");

      //+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(uint24(gasmax) == gasmax, "mgv/config/gasmax/24bits");
      //+clear+
      internal_global = internal_global.gasmax(gasmax);
      emit SetGasmax(gasmax);
    }
  }

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

  /* ### `vault` */
  function setVault(address vaultAddress) public {
    unchecked {
      authOnly();
      vault = vaultAddress;
      emit SetVault(vaultAddress);
    }
  }

  /* ### `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 5 of 11 : MgvHasOffers.sol
// SPDX-License-Identifier:	AGPL-3.0

// MgvHasOffers.sol

// Copyright (C) 2021 Giry SAS.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published
// by the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program.  If not, see <https://www.gnu.org/licenses/>.
pragma solidity ^0.8.10;
pragma abicoder v2;
import {MgvLib as ML, HasMgvEvents, IMgvMonitor, P} from "./MgvLib.sol";
import {MgvRoot} from "./MgvRoot.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 MgvRoot {
  using P.Offer for P.Offer.t;
  using P.OfferDetail for P.OfferDetail.t;
  using P.Local for P.Local.t;
  /* # State variables */
  /* Given a `outbound_tkn`,`inbound_tkn` pair, the mappings `offers` and `offerDetails` associate two 256 bits words to each offer id. Those words encode information detailed in [`structs.js`](#structs.js).

     The mappings are `outbound_tkn => inbound_tkn => offerId => P.Offer.t|P.OfferDetail.t`.
   */
  mapping(address => mapping(address => mapping(uint => P.Offer.t)))
    public offers;
  mapping(address => mapping(address => mapping(uint => P.OfferDetail.t)))
    public offerDetails;

  /* 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 a *penalty*, which depends on their `gasreq` and on the pair'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 retribution. The exact amount depends on the gas used by the offer before failing.

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

  /* # Read functions */
  /* Convenience function to get best offer of the given pair */
  function best(address outbound_tkn, address inbound_tkn)
    external
    view
    returns (uint)
  {
    unchecked {
      P.Local.t local = locals[outbound_tkn][inbound_tkn];
      return local.best();
    }
  }

  /* Returns information about an offer in ABI-compatible structs. Do not use internally, would be a huge memory-copying waste. Use `offers[outbound_tkn][inbound_tkn]` and `offerDetails[outbound_tkn][inbound_tkn]` instead. */
  function offerInfo(
    address outbound_tkn,
    address inbound_tkn,
    uint offerId
  )
    external
    view
    returns (P.OfferStruct memory offer, P.OfferDetailStruct memory offerDetail)
  {
    unchecked {
      P.Offer.t _offer = offers[outbound_tkn][inbound_tkn][offerId];
      offer = _offer.to_struct();

      P.OfferDetail.t _offerDetail = offerDetails[outbound_tkn][inbound_tkn][
        offerId
      ];
      offerDetail = _offerDetail.to_struct();
    }
  }

  /* # 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. Note that provision accounting in the Mangrove aims to minimize writes. Each maker `fund`s the 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 * (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(
    address outbound_tkn,
    address inbound_tkn,
    uint offerId,
    P.Offer.t offer,
    P.OfferDetail.t offerDetail,
    bool deprovision
  ) internal {
    unchecked {
      offer = offer.gives(0);
      if (deprovision) {
        offerDetail = offerDetail.gasprice(0);
      }
      offers[outbound_tkn][inbound_tkn][offerId] = offer;
      offerDetails[outbound_tkn][inbound_tkn][offerId] = offerDetail;
    }
  }

  /* ## Stitching the orderbook */

  /* Connect the offers `betterId` and `worseId` through their `next`/`prev` pointers. For more on the book structure, see [`structs.js`](#structs.js). Used after executing an offer (or a segment of offers), after removing an offer, or moving an offer.

  **Warning**: calling with `betterId = 0` will set `worseId` as the best. So with `betterId = 0` and `worseId = 0`, it sets the book to empty and loses track of existing offers.

  **Warning**: may make memory copy of `local.best` stale. Returns new `local`. */
  function stitchOffers(
    address outbound_tkn,
    address inbound_tkn,
    uint betterId,
    uint worseId,
    P.Local.t local
  ) internal returns (P.Local.t) {
    unchecked {
      if (betterId != 0) {
        offers[outbound_tkn][inbound_tkn][betterId] = offers[outbound_tkn][
          inbound_tkn
        ][betterId].next(worseId);
      } else {
        local = local.best(worseId);
      }

      if (worseId != 0) {
        offers[outbound_tkn][inbound_tkn][worseId] = offers[outbound_tkn][
          inbound_tkn
        ][worseId].prev(betterId);
      }

      return local;
    }
  }

  /* ## Check offer is live */
  /* Check whether an offer is 'live', that is: inserted in the order book. The Mangrove holds a `outbound_tkn => inbound_tkn => id => P.Offer.t` mapping in storage. Offer ids that are not yet assigned or that point to since-deleted offer will point to an offer with `gives` field at 0. */
  function isLive(P.Offer.t offer) public pure returns (bool) {
    unchecked {
      return offer.gives() > 0;
    }
  }
}

File 6 of 11 : MgvLib.sol
// SPDX-License-Identifier: Unlicense

// MgvLib.sol

// This is free and unencumbered software released into the public domain.

// Anyone is free to copy, modify, publish, use, compile, sell, or distribute this software, either in source code form or as a compiled binary, for any purpose, commercial or non-commercial, and by any means.

// In jurisdictions that recognize copyright laws, the author or authors of this software dedicate any and all copyright interest in the software to the public domain. We make this dedication for the benefit of the public at large and to the detriment of our heirs and successors. We intend this dedication to be an overt act of relinquishment in perpetuity of all present and future rights to this software under copyright law.

// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

// For more information, please refer to <https://unlicense.org/>

/* `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;
pragma abicoder v2;

import "./IERC20.sol";
import "./MgvPack.sol" as P;

/* # 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 `internalSnipes` (and some of their nested functions) to avoid stack too deep errors. */
  struct SingleOrder {
    address outbound_tkn;
    address inbound_tkn;
    uint offerId;
    P.Offer.t offer;
    /* `wants`/`gives` mutate over execution. Initially the `wants`/`gives` from the taker's pov, then actual `wants`/`gives` adjusted by offer's price and volume. */
    uint wants;
    uint gives;
    /* `offerDetail` is only populated when necessary. */
    P.OfferDetail.t offerDetail;
    P.Global.t global;
    P.Local.t 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 the 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;
  }
}

/* # Events
The events emitted for use by bots are listed here: */
contract HasMgvEvents {
  /* * Emitted at the creation of the new Mangrove contract on the pair (`inbound_tkn`, `outbound_tkn`)*/
  event NewMgv();

  /* Mangrove adds or removes wei from `maker`'s account */
  /* * Credit event occurs when an offer is removed from the Mangrove or when the `fund` function is called*/
  event Credit(address indexed maker, uint amount);
  /* * Debit event occurs when an offer is posted or when the `withdraw` function is called */
  event Debit(address indexed maker, uint amount);

  /* * Mangrove reconfiguration */
  event SetActive(
    address indexed outbound_tkn,
    address indexed inbound_tkn,
    bool value
  );
  event SetFee(
    address indexed outbound_tkn,
    address indexed inbound_tkn,
    uint value
  );
  event SetGasbase(
    address indexed outbound_tkn,
    address indexed inbound_tkn,
    uint offer_gasbase
  );
  event SetGovernance(address value);
  event SetMonitor(address value);
  event SetVault(address value);
  event SetUseOracle(bool value);
  event SetNotify(bool value);
  event SetGasmax(uint value);
  event SetDensity(
    address indexed outbound_tkn,
    address indexed inbound_tkn,
    uint value
  );
  event SetGasprice(uint value);

  /* Market order execution */
  event OrderStart();
  event OrderComplete(
    address indexed outbound_tkn,
    address indexed inbound_tkn,
    address indexed taker,
    uint takerGot,
    uint takerGave,
    uint penalty,
    uint feePaid
  );

  /* * Offer execution */
  event OfferSuccess(
    address indexed outbound_tkn,
    address indexed inbound_tkn,
    uint id,
    // `maker` is not logged because it can be retrieved from the state using `(outbound_tkn,inbound_tkn,id)`.
    address taker,
    uint takerWants,
    uint takerGives
  );

  /* Log information when a trade execution reverts or returns a non empty bytes32 word */
  event OfferFail(
    address indexed outbound_tkn,
    address indexed inbound_tkn,
    uint id,
    // `maker` is not logged because it can be retrieved from the state using `(outbound_tkn,inbound_tkn,id)`.
    address taker,
    uint takerWants,
    uint takerGives,
    // `mgvData` may only be `"mgv/makerRevert"`, `"mgv/makerTransferFail"` or `"mgv/makerReceiveFail"`
    bytes32 mgvData
  );

  /* Log information when a posthook reverts */
  event PosthookFail(
    address indexed outbound_tkn,
    address indexed inbound_tkn,
    uint offerId,
    bytes32 posthookData
  );

  /* * After `permit` and `approve` */
  event Approval(
    address indexed outbound_tkn,
    address indexed inbound_tkn,
    address owner,
    address spender,
    uint value
  );

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

  /* * An offer was created or updated.
  A few words about why we include a `prev` field, and why we don't include a
  `next` field: in theory clients should need neither `prev` nor a `next` field.
  They could just 1. Read the order book state at a given block `b`.  2. On
  every event, update a local copy of the orderbook.  But in practice, we do not
  want to force clients to keep a copy of the *entire* orderbook. There may be a
  long tail of spam. Now if they only start with the first $N$ offers and
  receive a new offer that goes to the end of the book, they cannot tell if
  there are missing offers between the new offer and the end of the local copy
  of the book.
  
  So we add a prev pointer so clients with only a prefix of the book can receive
  out-of-prefix offers and know what to do with them. The `next` pointer is an
  optimization useful in Solidity (we traverse fewer memory locations) but
  useless in client code.
  */
  event OfferWrite(
    address indexed outbound_tkn,
    address indexed inbound_tkn,
    address maker,
    uint wants,
    uint gives,
    uint gasprice,
    uint gasreq,
    uint id,
    uint prev
  );

  /* * `offerId` was present and is now removed from the book. */
  event OfferRetract(
    address indexed outbound_tkn,
    address indexed inbound_tkn,
    uint id
  );
}

/* # IMaker interface */
interface IMaker {
  /* Called upon offer execution. 
  - If the call fails, Mangrove will not try to transfer funds.
  - If the call succeeds but returndata's first 32 bytes are not 0, Mangrove will not try to transfer funds either.
  - If the call succeeds and returndata's first 32 bytes are 0, Mangrove will try to transfer funds.
  In other words, you may declare failure by reverting or by returning nonzero data. In both cases, those 32 first bytes will be passed back to you during the call to `makerPosthook` in the `result.mgvData` field.
     ```
     function tradeRevert(bytes32 data) internal pure {
       bytes memory revData = new bytes(32);
         assembly {
           mstore(add(revData, 32), data)
           revert(add(revData, 32), 32)
         }
     }
     ```
     */
  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 the 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;
}

/* # ITaker interface */
interface ITaker {
  /* Inverted mangrove only: call to taker after loans went through */
  function takerTrade(
    address outbound_tkn,
    address inbound_tkn,
    // total amount of outbound_tkn token that was flashloaned to the taker
    uint totalGot,
    // total amount of inbound_tkn token that should be made available
    uint totalGives
  ) external;
}

/* # Monitor interface
If enabled, the monitor receives notification after each offer execution and is read for each pair'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(address outbound_tkn, address inbound_tkn)
    external
    view
    returns (uint gasprice, uint density);
}

File 7 of 11 : MgvOfferMaking.sol
// SPDX-License-Identifier:	AGPL-3.0

// MgvOfferMaking.sol

// Copyright (C) 2021 Giry SAS.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published
// by the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program.  If not, see <https://www.gnu.org/licenses/>.
pragma solidity ^0.8.10;
pragma abicoder v2;
import {IMaker, HasMgvEvents, P} from "./MgvLib.sol";
import {MgvHasOffers} from "./MgvHasOffers.sol";

/* `MgvOfferMaking` contains market-making-related functions. */
contract MgvOfferMaking is MgvHasOffers {
  using P.Offer for P.Offer.t;
  using P.OfferDetail for P.OfferDetail.t;
  using P.Global for P.Global.t;
  using P.Local for P.Local.t;
  /* # Public Maker operations
     ## New Offer */
  //+clear+
  /* In the 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 {
    address outbound_tkn;
    address inbound_tkn;
    uint wants;
    uint gives;
    uint id;
    uint gasreq;
    uint gasprice;
    uint pivotId;
    P.Global.t global;
    P.Local.t local;
    // used on update only
    P.Offer.t oldOffer;
  }

  /* The function `newOffer` is for market makers only; no match with the existing book is done. A maker specifies how much `inbound_tkn` it `wants` and how much `outbound_tkn` it `gives`.

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

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

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

  Offers are always inserted at the correct place in the book. This requires walking through offers to find the correct insertion point. As in [Oasis](https://github.com/daifoundation/maker-otc/blob/f2060c5fe12fe3da71ac98e8f6acc06bca3698f5/src/matching_market.sol#L493), the maker should find the id of an offer close to its own and provide it as `pivotId`.

  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_tkn`,`inbound_tkn` pair.

  The actual contents of the function is in `writeOffer`, which is called by both `newOffer` and `updateOffer`.
  */
  function newOffer(
    address outbound_tkn,
    address inbound_tkn,
    uint wants,
    uint gives,
    uint gasreq,
    uint gasprice,
    uint pivotId
  ) external payable returns (uint) {
    unchecked {
      /* In preparation for calling `writeOffer`, we read the `outbound_tkn`,`inbound_tkn` pair configuration, check for reentrancy and market liveness, fill the `OfferPack` struct and increment the `outbound_tkn`,`inbound_tkn` pair's `last`. */
      OfferPack memory ofp;
      (ofp.global, ofp.local) = config(outbound_tkn, inbound_tkn);
      unlockedMarketOnly(ofp.local);
      activeMarketOnly(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.outbound_tkn = outbound_tkn;
      ofp.inbound_tkn = inbound_tkn;
      ofp.wants = wants;
      ofp.gives = gives;
      ofp.gasreq = gasreq;
      ofp.gasprice = gasprice;
      ofp.pivotId = pivotId;

      /* The second parameter to writeOffer indicates that we are creating a new offer, not updating an existing one. */
      writeOffer(ofp, 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 current `best` offer. */
      locals[ofp.outbound_tkn][ofp.inbound_tkn] = ofp.local;
      return ofp.id;
    }
  }

  /* ## 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.

     A `pivotId` should still be given to minimise reads in the offer book. It is OK to give the offers' own id as a pivot.


     Gas use is minimal when:
     1. The offer does not move in the book
     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 the Mangrove's gasprice estimation
  */
  function updateOffer(
    address outbound_tkn,
    address inbound_tkn,
    uint wants,
    uint gives,
    uint gasreq,
    uint gasprice,
    uint pivotId,
    uint offerId
  ) external payable {
    unchecked {
      OfferPack memory ofp;
      (ofp.global, ofp.local) = config(outbound_tkn, inbound_tkn);
      unlockedMarketOnly(ofp.local);
      activeMarketOnly(ofp.global, ofp.local);
      if (msg.value > 0) {
        creditWei(msg.sender, msg.value);
      }
      ofp.outbound_tkn = outbound_tkn;
      ofp.inbound_tkn = inbound_tkn;
      ofp.wants = wants;
      ofp.gives = gives;
      ofp.id = offerId;
      ofp.gasreq = gasreq;
      ofp.gasprice = gasprice;
      ofp.pivotId = pivotId;
      ofp.oldOffer = offers[outbound_tkn][inbound_tkn][offerId];
      // Save local config
      P.Local.t oldLocal = ofp.local;
      /* The second argument indicates that we are updating an existing offer, not creating a new one. */
      writeOffer(ofp, true);
      /* We saved the current pair's configuration before calling `writeOffer`, since that function may update the current `best` offer. We now check for any change to the configuration and update it if needed. */
      if (!oldLocal.eq(ofp.local)) {
        locals[ofp.outbound_tkn][ofp.inbound_tkn] = ofp.local;
      }
    }
  }

  /* ## 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(
    address outbound_tkn,
    address inbound_tkn,
    uint offerId,
    bool deprovision
  ) external returns (uint provision) {
    unchecked {
      (, P.Local.t local) = config(outbound_tkn, inbound_tkn);
      unlockedMarketOnly(local);
      P.Offer.t offer = offers[outbound_tkn][inbound_tkn][offerId];
      P.OfferDetail.t offerDetail = offerDetails[outbound_tkn][inbound_tkn][
        offerId
      ];
      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 book by stitching together its previous and next offers. Note that unconditionally calling `stitchOffers` would break the book since it would connect offers that may have since moved. */
      if (isLive(offer)) {
        P.Local.t oldLocal = local;
        local = stitchOffers(
          outbound_tkn,
          inbound_tkn,
          offer.prev(),
          offer.next(),
          local
        );
        /* If calling `stitchOffers` has changed the current `best` offer, we update the storage. */
        if (!oldLocal.eq(local)) {
          locals[outbound_tkn][inbound_tkn] = local;
        }
      }
      /* Set `gives` to 0. Moreover, the last argument depends on whether the user wishes to get their provision back (if true, `gasprice` will be set to 0 as well). */
      dirtyDeleteOffer(
        outbound_tkn,
        inbound_tkn,
        offerId,
        offer,
        offerDetail,
        deprovision
      );

      /* If the user wants to get their provision back, we compute its provision from the offer's `gasprice`, `offer_gasbase` and `gasreq`. */
      if (deprovision) {
        provision =
          10**9 *
          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(outbound_tkn, inbound_tkn, offerId);
    }
  }

  /* ## Provisioning
  Market makers must have enough provisions for possible penalties. These provisions are in ETH. 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 {
      (P.Global.t _global, ) = config(address(0), address(0));
      liveMgvOnly(_global);
      creditWei(maker, msg.value);
    }
  }

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

  /* A transfer with enough gas to the Mangrove will increase the caller's available `balanceOf` balance. _You should send enough gas to execute this function when sending money to the 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}("");
    }
  }

  /* # Low-level Maker functions */

  /* ## Write Offer */

  function writeOffer(OfferPack memory ofp, bool update) internal {
    unchecked {
      /* `gasprice`'s floor is Mangrove's own gasprice estimate, `ofp.global.gasprice`. We first check that gasprice fits in 16 bits. Otherwise it could be that `uint16(gasprice) < global_gasprice < gasprice`, and the actual value we store is `uint16(gasprice)`. */
      require(checkGasprice(ofp.gasprice), "mgv/writeOffer/gasprice/16bits");

      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.gasreq + ofp.local.offer_gasbase()) * ofp.local.density(),
        "mgv/writeOffer/density/tooLow"
      );

      /* The following checks are for the maker's convenience only. */
      require(uint96(ofp.gives) == ofp.gives, "mgv/writeOffer/gives/96bits");
      require(uint96(ofp.wants) == ofp.wants, "mgv/writeOffer/wants/96bits");

      /* The position of the new or updated offer is found using `findPosition`. If the offer is the best one, `prev == 0`, and if it's the last in the book, `next == 0`.

       `findPosition` is only ever called here, but exists as a separate function to make the code easier to read.

    **Warning**: `findPosition` will call `better`, which may read the offer's `offerDetails`. So it is important to find the offer position _before_ we update its `offerDetail` in storage. We waste 1 (hot) read in that case but we deem that the code would get too ugly if we passed the old `offerDetail` as argument to `findPosition` and to `better`, just to save 1 hot read in that specific case.  */
      (uint prev, uint next) = findPosition(ofp);

      /* Log the write offer event. */
      emit OfferWrite(
        ofp.outbound_tkn,
        ofp.inbound_tkn,
        msg.sender,
        ofp.wants,
        ofp.gives,
        ofp.gasprice,
        ofp.gasreq,
        ofp.id,
        prev
      );

      /* 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;
      {
        P.OfferDetail.t offerDetail = offerDetails[ofp.outbound_tkn][
          ofp.inbound_tkn
        ][ofp.id];
        if (update) {
          require(
            msg.sender == offerDetail.maker(),
            "mgv/updateOffer/unauthorized"
          );
          oldProvision =
            10**9 *
            offerDetail.gasprice() *
            (offerDetail.gasreq() + offerDetail.offer_gasbase());
        }

        /* If the offer is new, has a new `gasprice`, `gasreq`, or if the 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();
          offerDetails[ofp.outbound_tkn][ofp.inbound_tkn][ofp.id] = P
            .OfferDetail
            .pack({
              __maker: msg.sender,
              __gasreq: ofp.gasreq,
              __offer_gasbase: offer_gasbase,
              __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 *
          10**9;
        if (provision > oldProvision) {
          debitWei(msg.sender, provision - oldProvision);
        } else if (provision < oldProvision) {
          creditWei(msg.sender, oldProvision - provision);
        }
      }
      /* We now place the offer in the book at the position found by `findPosition`. */

      /* First, we test if the offer has moved in the book or is not currently in the book. If `!isLive(ofp.oldOffer)`, we must update its prev/next. If it is live but its prev has changed, we must also update them. Note that checking both `prev = oldPrev` and `next == oldNext` would be redundant. If either is true, then the updated offer has not changed position and there is nothing to update.

    As a note for future changes, there is a tricky edge case where `prev == oldPrev` yet the prev/next should be changed: a previously-used offer being brought back in the book, and ending with the same prev it had when it was in the book. In that case, the neighbor is currently pointing to _another_ offer, and thus must be updated. With the current code structure, this is taken care of as a side-effect of checking `!isLive`, but should be kept in mind. The same goes in the `next == oldNext` case. */
      if (!isLive(ofp.oldOffer) || prev != ofp.oldOffer.prev()) {
        /* * If the offer is not the best one, we update its predecessor; otherwise we update the `best` value. */
        if (prev != 0) {
          offers[ofp.outbound_tkn][ofp.inbound_tkn][prev] = offers[
            ofp.outbound_tkn
          ][ofp.inbound_tkn][prev].next(ofp.id);
        } else {
          ofp.local = ofp.local.best(ofp.id);
        }

        /* * If the offer is not the last one, we update its successor. */
        if (next != 0) {
          offers[ofp.outbound_tkn][ofp.inbound_tkn][next] = offers[
            ofp.outbound_tkn
          ][ofp.inbound_tkn][next].prev(ofp.id);
        }

        /* * Recall that in this branch, the offer has changed location, or is not currently in the book. If the offer is not new and already in the book, we must remove it from its previous location by stitching its previous prev/next. */
        if (update && isLive(ofp.oldOffer)) {
          ofp.local = stitchOffers(
            ofp.outbound_tkn,
            ofp.inbound_tkn,
            ofp.oldOffer.prev(),
            ofp.oldOffer.next(),
            ofp.local
          );
        }
      }

      /* With the `prev`/`next` in hand, we finally store the offer in the `offers` map. */
      P.Offer.t ofr = P.Offer.pack({
        __prev: prev,
        __next: next,
        __wants: ofp.wants,
        __gives: ofp.gives
      });
      offers[ofp.outbound_tkn][ofp.inbound_tkn][ofp.id] = ofr;
    }
  }

  /* ## Find Position */
  /* `findPosition` takes a price in the form of a (`ofp.wants`,`ofp.gives`) pair, an offer id (`ofp.pivotId`) and walks the book from that offer (backward or forward) until the right position for the price is found. The position is returned as a `(prev,next)` pair, with `prev` or `next` at 0 to mark the beginning/end of the book (no offer ever has id 0).

  If prices are equal, `findPosition` will put the newest offer last. */
  function findPosition(OfferPack memory ofp)
    internal
    view
    returns (uint, uint)
  {
    unchecked {
      uint prevId;
      uint nextId;
      uint pivotId = ofp.pivotId;
      /* Get `pivot`, optimizing for the case where pivot info is already known */
      P.Offer.t pivot = pivotId == ofp.id
        ? ofp.oldOffer
        : offers[ofp.outbound_tkn][ofp.inbound_tkn][pivotId];

      /* In case pivotId is not an active offer, it is unusable (since it is out of the book). We default to the current best offer. If the book is empty pivot will be 0. That is handled through a test in the `better` comparison function. */
      if (!isLive(pivot)) {
        pivotId = ofp.local.best();
        pivot = offers[ofp.outbound_tkn][ofp.inbound_tkn][pivotId];
      }

      /* * Pivot is better than `wants/gives`, we follow `next`. */
      if (better(ofp, pivot, pivotId)) {
        P.Offer.t pivotNext;
        while (pivot.next() != 0) {
          uint pivotNextId = pivot.next();
          pivotNext = offers[ofp.outbound_tkn][ofp.inbound_tkn][pivotNextId];
          if (better(ofp, pivotNext, pivotNextId)) {
            pivotId = pivotNextId;
            pivot = pivotNext;
          } else {
            break;
          }
        }
        // gets here on empty book
        (prevId, nextId) = (pivotId, pivot.next());

        /* * Pivot is strictly worse than `wants/gives`, we follow `prev`. */
      } else {
        P.Offer.t pivotPrev;
        while (pivot.prev() != 0) {
          uint pivotPrevId = pivot.prev();
          pivotPrev = offers[ofp.outbound_tkn][ofp.inbound_tkn][pivotPrevId];
          if (better(ofp, pivotPrev, pivotPrevId)) {
            break;
          } else {
            pivotId = pivotPrevId;
            pivot = pivotPrev;
          }
        }

        (prevId, nextId) = (pivot.prev(), pivotId);
      }

      return (
        prevId == ofp.id ? ofp.oldOffer.prev() : prevId,
        nextId == ofp.id ? ofp.oldOffer.next() : nextId
      );
    }
  }

  /* ## Better */
  /* The utility method `better` takes an offer represented by `ofp` and another represented by `offer1`. It returns true iff `offer1` is better or as good as `ofp`.
    "better" is defined on the lexicographic order $\textrm{price} \times_{\textrm{lex}} \textrm{density}^{-1}$. This means that for the same price, offers that deliver more volume per gas are taken first.

      In addition to `offer1`, we also provide its id, `offerId1` in order to save gas. If necessary (ie. if the prices `wants1/gives1` and `wants2/gives2` are the same), we read storage to get `gasreq1` at `offerDetails[...][offerId1]. */
  function better(
    OfferPack memory ofp,
    P.Offer.t offer1,
    uint offerId1
  ) internal view returns (bool) {
    unchecked {
      if (offerId1 == 0) {
        /* Happens on empty book. Returning `false` would work as well due to specifics of `findPosition` but true is more consistent. Here we just want to avoid reading `offerDetail[...][0]` for nothing. */
        return true;
      }
      uint wants1 = offer1.wants();
      uint gives1 = offer1.gives();
      uint wants2 = ofp.wants;
      uint gives2 = ofp.gives;
      uint weight1 = wants1 * gives2;
      uint weight2 = wants2 * gives1;
      if (weight1 == weight2) {
        uint gasreq1 = offerDetails[ofp.outbound_tkn][ofp.inbound_tkn][offerId1]
          .gasreq();
        uint gasreq2 = ofp.gasreq;
        return (gives1 * gasreq2 >= gives2 * gasreq1);
      } else {
        return weight1 < weight2;
      }
    }
  }
}

File 8 of 11 : MgvOfferTaking.sol
// SPDX-License-Identifier:	AGPL-3.0

// MgvOfferTaking.sol

// Copyright (C) 2021 Giry SAS.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published
// by the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program.  If not, see <https://www.gnu.org/licenses/>.
pragma solidity ^0.8.10;
pragma abicoder v2;
import {IERC20, HasMgvEvents, IMaker, IMgvMonitor, MgvLib as ML, P} from "./MgvLib.sol";
import {MgvHasOffers} from "./MgvHasOffers.sol";

abstract contract MgvOfferTaking is MgvHasOffers {
  using P.Offer for P.Offer.t;
  using P.OfferDetail for P.OfferDetail.t;
  using P.Global for P.Global.t;
  using P.Local for P.Local.t;
  /* # MultiOrder struct */
  /* The `MultiOrder` struct is used by market orders and snipes. Some of its fields are only used by market orders (`initialWants, initialGives`). We need a common data structure for both since low-level calls are shared between market orders and snipes. The struct is helpful in decreasing stack use. */
  struct MultiOrder {
    uint initialWants; // used globally by market order, not used by snipes
    uint initialGives; // used globally by market order, not used by snipes
    uint totalGot; // used globally by market order, per-offer by snipes
    uint totalGave; // used globally by market order, per-offer by snipes
    uint totalPenalty; // used globally
    address taker; // used globally
    bool fillWants; // used globally
    uint feePaid; // used globally
  }

  /* # Market Orders */

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

  /* A market order specifies a (`outbound_tkn`,`inbound_tkn`) pair, a desired total amount of `outbound_tkn` (`takerWants`), and an available total amount of `inbound_tkn` (`takerGives`). It returns two `uint`s: the total amount of `outbound_tkn` received and the total amount of `inbound_tkn` spent.

     The `takerGives/takerWants` ratio induces a maximum average price that the taker is ready to pay across all offers that will be executed during the market order. It is thus possible to execute an offer with a price worse than the initial (`takerGives`/`takerWants`) ratio given as argument to `marketOrder` if some cheaper offers were executed earlier in the market order.

  The market order stops when the price has become too high, or when the end of the book has been reached, or:
  * If `fillWants` is true, the market order stops when `takerWants` units of `outbound_tkn` have been obtained. With `fillWants` set to true, to buy a specific volume of `outbound_tkn` at any price, set `takerWants` to the amount desired and `takerGives` to $2^{160}-1$.
  * If `fillWants` is false, the taker is filling `gives` instead: the market order stops when `takerGives` units of `inbound_tkn` have been sold. With `fillWants` set to false, to sell a specific volume of `inbound_tkn` at any price, set `takerGives` to the amount desired and `takerWants` to $0$. */
  function marketOrder(
    address outbound_tkn,
    address inbound_tkn,
    uint takerWants,
    uint takerGives,
    bool fillWants
  )
    external
    returns (
      uint,
      uint,
      uint,
      uint
    )
  {
    unchecked {
      return
        generalMarketOrder(
          outbound_tkn,
          inbound_tkn,
          takerWants,
          takerGives,
          fillWants,
          msg.sender
        );
    }
  }

  /* # 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(
    address outbound_tkn,
    address inbound_tkn,
    uint takerWants,
    uint takerGives,
    bool fillWants,
    address taker
  )
    internal
    returns (
      uint,
      uint,
      uint,
      uint
    )
  {
    unchecked {
      /* Since amounts stored in offers are 96 bits wide, checking that `takerWants` and `takerGives` fit in 160 bits prevents overflow during the main market order loop. */
      require(
        uint160(takerWants) == takerWants,
        "mgv/mOrder/takerWants/160bits"
      );
      require(
        uint160(takerGives) == takerGives,
        "mgv/mOrder/takerGives/160bits"
      );

      /* `SingleOrder` is defined in `MgvLib.sol` and holds information for ordering the execution of one offer. */
      ML.SingleOrder memory sor;
      sor.outbound_tkn = outbound_tkn;
      sor.inbound_tkn = inbound_tkn;
      (sor.global, sor.local) = config(outbound_tkn, inbound_tkn);
      /* 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). */
      sor.offerId = sor.local.best();
      sor.offer = offers[outbound_tkn][inbound_tkn][sor.offerId];
      /* `sor.wants` and `sor.gives` may evolve, but they are initially however much remains in the market order. */
      sor.wants = takerWants;
      sor.gives = takerGives;

      /* `MultiOrder` (defined above) maintains information related to the entire market order. During the order, initial `wants`/`gives` values minus the accumulated amounts traded so far give the amounts that remain to be traded. */
      MultiOrder memory mor;
      mor.initialWants = takerWants;
      mor.initialGives = takerGives;
      mor.taker = taker;
      mor.fillWants = fillWants;

      /* For the market order to even start, the market needs to be both active, and not currently protected from reentrancy. */
      activeMarketOnly(sor.global, sor.local);
      unlockedMarketOnly(sor.local);

      /* ### Initialization */
      /* The market order will operate as follows : it will go through offers from best to worse, starting from `offerId`, and: */
      /* * will maintain remaining `takerWants` and `takerGives` values. The initial `takerGives/takerWants` ratio is the average price the taker will accept. Better prices may be found early in the book, and worse ones later.
       * will 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 be enabling the reentrancy lock for this (`outbound_tkn`,`inbound_tkn`) pair. */
      sor.local = sor.local.lock(true);
      locals[outbound_tkn][inbound_tkn] = sor.local;

      emit OrderStart();

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

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

      emit OrderComplete(
        outbound_tkn,
        inbound_tkn,
        taker,
        mor.totalGot,
        mor.totalGave,
        mor.totalPenalty,
        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 book). Then the [reentrancy lock is lifted](#internalMarketOrder/liftReentrancy). Going upward, each offer's `maker` contract is called again with its remaining gas and given the chance to update its offers on the book.

    The last argument is a boolean named `proceed`. If an offer was not executed, it means the price has become too high. In that case, we notify the next recursive call that the market order should end. In this initial call, no offer has been executed yet so `proceed` is true. */
  function internalMarketOrder(
    MultiOrder memory mor,
    ML.SingleOrder memory sor,
    bool proceed
  ) internal {
    unchecked {
      /* #### Case 1 : End of order */
      /* We execute the offer currently stored in `sor`. */
      if (
        proceed &&
        (mor.fillWants ? sor.wants > 0 : sor.gives > 0) &&
        sor.offerId > 0
      ) {
        uint gasused; // gas used by `makerExecute`
        bytes32 makerData; // data returned by maker

        /* <a id="MgvOfferTaking/statusCodes"></a> `mgvData` is an internal Mangrove status code. It may appear in an [`OrderResult`](#MgvLib/OrderResult). Its possible values are:
      * `"mgv/notExecuted"`: offer was not executed.
      * `"mgv/tradeSuccess"`: offer execution succeeded. Will appear in `OrderResult`.
      * `"mgv/notEnoughGasForMakerTrade"`: cannot give maker close enough to `gasreq`. Triggers a revert of the entire order.
      * `"mgv/makerRevert"`: execution of `makerExecute` reverted. Will appear in `OrderResult`.
      * `"mgv/makerTransferFail"`: maker could not send outbound_tkn tokens. Will appear in `OrderResult`.
      * `"mgv/makerReceiveFail"`: maker could not receive inbound_tkn tokens. Will appear in `OrderResult`.
      * `"mgv/takerTransferFail"`: taker could not send inbound_tkn tokens. Triggers a revert of the entire order.

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

        /* Load additional information about the offer. We don't do it earlier to save one storage read in case `proceed` was false. */
        sor.offerDetail = offerDetails[sor.outbound_tkn][sor.inbound_tkn][
          sor.offerId
        ];

        /* `execute` will adjust `sor.wants`,`sor.gives`, and may attempt to execute the offer if its price is low enough. It is crucial that an error due to `taker` triggers a revert. That way, [`mgvData`](#MgvOfferTaking/statusCodes) not in `["mgv/notExecuted","mgv/tradeSuccess"]` means the failure is the maker's fault. */
        /* Post-execution, `sor.wants`/`sor.gives` 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 `offerId`, `sor.offer` and `sor.offerDetail`. */

        (gasused, makerData, mgvData) = execute(mor, sor);

        /* Keep cached copy of current `sor` values. */
        uint takerWants = sor.wants;
        uint takerGives = sor.gives;
        uint offerId = sor.offerId;
        P.Offer.t offer = sor.offer;
        P.OfferDetail.t offerDetail = sor.offerDetail;

        /* If an execution was attempted, we move `sor` to the next offer. Note that the current state is inconsistent, since we have not yet updated `sor.offerDetails`. */
        if (mgvData != "mgv/notExecuted") {
          sor.wants = mor.initialWants > mor.totalGot
            ? mor.initialWants - mor.totalGot
            : 0;
          /* It is known statically that `mor.initialGives - mor.totalGave` does not underflow since
           1. `mor.totalGave` was increased by `sor.gives` during `execute`,
           2. `sor.gives` was at most `mor.initialGives - mor.totalGave` from earlier step,
           3. `sor.gives` may have been clamped _down_ during `execute` (to "`offer.wants`" if the offer is entirely consumed, or to `makerWouldWant`, cf. code of `execute`).
        */
          sor.gives = mor.initialGives - mor.totalGave;
          sor.offerId = sor.offer.next();
          sor.offer = offers[sor.outbound_tkn][sor.inbound_tkn][sor.offerId];
        }

        /* note that internalMarketOrder may be called twice with same offerId, but in that case `proceed` will be false! */
        internalMarketOrder(
          mor,
          sor,
          /* `proceed` value for next call. Currently, when an offer did not execute, it's because the offer's price was too high. In that case we interrupt the loop and let the taker leave with less than they asked for (but at a correct price). We could also revert instead of breaking; this could be a configurable flag for the taker to pick. */
          mgvData != "mgv/notExecuted"
        );

        /* Restore `sor` values from to before recursive call */
        sor.offerId = offerId;
        sor.wants = takerWants;
        sor.gives = takerGives;
        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 snipes, it lives in its own `postExecute` function. */
        if (mgvData != "mgv/notExecuted") {
          postExecute(mor, sor, gasused, makerData, mgvData);
        }

        /* #### Case 2 : End of market order */
        /* If `proceed` is false, the taker has gotten its requested volume, or we have reached the end of the book, we conclude the market order. */
      } else {
        /* During the market order, all executed offers have been removed from the book. We end by stitching together the `best` offer pointer and the new best offer. */
        sor.local = stitchOffers(
          sor.outbound_tkn,
          sor.inbound_tkn,
          0,
          sor.offerId,
          sor.local
        );
        /* <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);
        locals[sor.outbound_tkn][sor.inbound_tkn] = sor.local;

        /* `payTakerMinusFees` sends the fee to the vault, proportional to the amount purchased, and gives the rest to the taker */
        payTakerMinusFees(mor, sor);

        /* In an inverted Mangrove, amounts have been lent by each offer's maker to the taker. We now call the taker. This is a noop in a normal Mangrove. */
        executeEnd(mor, sor);
      }
    }
  }

  /* # Sniping */
  /* ## Snipes */
  //+clear+

  /* `snipes` executes multiple offers. It takes a `uint[4][]` as penultimate argument, with each array element of the form `[offerId,takerWants,takerGives,offerGasreq]`. The return parameters are of the form `(successes,snipesGot,snipesGave,bounty,feePaid)`. 
  Note that we do not distinguish further between mismatched arguments/offer fields on the one hand, and an execution failure on the other. Still, a failed offer has to pay a penalty, and ultimately transaction logs explicitly mention execution failures (see `MgvLib.sol`). */
  function snipes(
    address outbound_tkn,
    address inbound_tkn,
    uint[4][] calldata targets,
    bool fillWants
  )
    external
    returns (
      uint,
      uint,
      uint,
      uint,
      uint
    )
  {
    unchecked {
      return
        generalSnipes(
          outbound_tkn,
          inbound_tkn,
          targets,
          fillWants,
          msg.sender
        );
    }
  }

  /*
     From an array of _n_ `[offerId, takerWants,takerGives,gasreq]` elements, execute each snipe in sequence. Returns `(successes, takerGot, takerGave, bounty, feePaid)`. 

     Note that if this function is not internal, anyone can make anyone use Mangrove.
     Note that unlike general market order, the returned total values are _not_ `mor.totalGot` and `mor.totalGave`, since those are reset at every iteration of the `targets` array. Instead, accumulators `snipesGot` and `snipesGave` are used. */
  function generalSnipes(
    address outbound_tkn,
    address inbound_tkn,
    uint[4][] calldata targets,
    bool fillWants,
    address taker
  )
    internal
    returns (
      uint successCount,
      uint snipesGot,
      uint snipesGave,
      uint totalPenalty,
      uint feePaid
    )
  {
    unchecked {
      ML.SingleOrder memory sor;
      sor.outbound_tkn = outbound_tkn;
      sor.inbound_tkn = inbound_tkn;
      (sor.global, sor.local) = config(outbound_tkn, inbound_tkn);

      MultiOrder memory mor;
      mor.taker = taker;
      mor.fillWants = fillWants;

      /* For the snipes to even start, the market needs to be both active and not currently protected from reentrancy. */
      activeMarketOnly(sor.global, sor.local);
      unlockedMarketOnly(sor.local);

      emit OrderStart();

      /* ### Main loop */
      //+clear+

      /* Call `internalSnipes` function. */
      (successCount, snipesGot, snipesGave) = internalSnipes(mor, sor, targets);

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

      emit OrderComplete(
        sor.outbound_tkn,
        sor.inbound_tkn,
        taker,
        snipesGot,
        snipesGave,
        mor.totalPenalty,
        mor.feePaid
      );
      totalPenalty = mor.totalPenalty;
      feePaid = mor.feePaid;
    }
  }

  /* ## Internal snipes */
  //+clear+
  /* `internalSnipes` works by looping over targets. Each successive offer is executed under a [reentrancy lock](#internalSnipes/liftReentrancy), then its posthook is called. Going upward, each offer's `maker` contract is called again with its remaining gas and given the chance to update its offers on the book. */
  function internalSnipes(
    MultiOrder memory mor,
    ML.SingleOrder memory sor,
    uint[4][] calldata targets
  )
    internal
    returns (
      uint successCount,
      uint snipesGot,
      uint snipesGave
    )
  {
    unchecked {
      for (uint i = 0; i < targets.length; i++) {
        /* Reset these amounts since every snipe is treated individually. Only the total penalty is sent at the end of all snipes. */
        mor.totalGot = 0;
        mor.totalGave = 0;

        /* Initialize single order struct. */
        sor.offerId = targets[i][0];
        sor.offer = offers[sor.outbound_tkn][sor.inbound_tkn][sor.offerId];
        sor.offerDetail = offerDetails[sor.outbound_tkn][sor.inbound_tkn][
          sor.offerId
        ];

        /* If we removed the `isLive` conditional, a single expired or nonexistent offer in `targets` would revert the entire transaction (by the division by `offer.gives` below since `offer.gives` would be 0). We also check that `gasreq` is not worse than specified. A taker who does not care about `gasreq` can specify any amount larger than $2^{24}-1$. A mismatched price will be detected by `execute`. */
        if (!isLive(sor.offer) || sor.offerDetail.gasreq() > targets[i][3]) {
          /* We move on to the next offer in the array. */
          continue;
        } else {
          require(
            uint96(targets[i][1]) == targets[i][1],
            "mgv/snipes/takerWants/96bits"
          );
          require(
            uint96(targets[i][2]) == targets[i][2],
            "mgv/snipes/takerGives/96bits"
          );
          sor.wants = targets[i][1];
          sor.gives = targets[i][2];

          /* We start be enabling the reentrancy lock for this (`outbound_tkn`,`inbound_tkn`) pair. */
          sor.local = sor.local.lock(true);
          locals[sor.outbound_tkn][sor.inbound_tkn] = sor.local;

          /* `execute` will adjust `sor.wants`,`sor.gives`, and may attempt to execute the offer if its price is low enough. It is crucial that an error due to `taker` triggers a revert. That way [`mgvData`](#MgvOfferTaking/statusCodes) not in `["mgv/tradeSuccess","mgv/notExecuted"]` means the failure is the maker's fault. */
          /* Post-execution, `sor.wants`/`sor.gives` reflect how much was sent/taken by the offer. */
          (uint gasused, bytes32 makerData, bytes32 mgvData) = execute(
            mor,
            sor
          );

          if (mgvData == "mgv/tradeSuccess") {
            successCount += 1;
          }

          /* In the market order, we were able to avoid stitching back offers after every `execute` since we knew a continuous segment starting at best would be consumed. Here, we cannot do this optimisation since offers in the `targets` array may be anywhere in the book. So we stitch together offers immediately after each `execute`. */
          if (mgvData != "mgv/notExecuted") {
            sor.local = stitchOffers(
              sor.outbound_tkn,
              sor.inbound_tkn,
              sor.offer.prev(),
              sor.offer.next(),
              sor.local
            );
          }

          /* <a id="internalSnipes/liftReentrancy"></a> Now that the current snipe 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);
          locals[sor.outbound_tkn][sor.inbound_tkn] = sor.local;

          /* `payTakerMinusFees` sends the fee to the vault, proportional to the amount purchased, and gives the rest to the taker */
          payTakerMinusFees(mor, sor);

          /* In an inverted Mangrove, amounts have been lent by each offer's maker to the taker. We now call the taker. This is a noop in a normal Mangrove. */
          executeEnd(mor, sor);

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

          snipesGot += mor.totalGot;
          snipesGave += mor.totalGave;
        }
      }
    }
  }

  /* # General execution */
  /* During a market order or a snipes, 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 */
  /* This function will compare `sor.wants` `sor.gives` with `sor.offer.wants` and `sor.offer.gives`. If the price of the offer is low enough, an execution will be attempted (with volume limited by the offer's advertised volume).

     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
    * `mgvData` is an [internal Mangrove status code](#MgvOfferTaking/statusCodes).
  */
  function execute(MultiOrder memory mor, ML.SingleOrder memory sor)
    internal
    returns (
      uint gasused,
      bytes32 makerData,
      bytes32 mgvData
    )
  {
    unchecked {
      /* #### `Price comparison` */
      //+clear+
      /* The current offer has a price `p = offerWants ÷ offerGives` and the taker is ready to accept a price up to `p' = takerGives ÷ takerWants`. Comparing `offerWants * takerWants` and `offerGives * takerGives` tels us whether `p < p'`.
       */
      {
        uint offerWants = sor.offer.wants();
        uint offerGives = sor.offer.gives();
        uint takerWants = sor.wants;
        uint takerGives = sor.gives;
        /* <a id="MgvOfferTaking/checkPrice"></a>If the price is too high, we return early.

         Otherwise we now know we'll execute the offer. */
        if (offerWants * takerWants > offerGives * takerGives) {
          return (0, bytes32(0), "mgv/notExecuted");
        }

        /* ### Specification of value transfers:

      Let $o_w$ be `offerWants`, $o_g$ be `offerGives`, $t_w$ be `takerWants`, $t_g$ be `takerGives`, and `f ∈ {w,g}` be $w$ if `fillWants` is true, $g$ otherwise.

      Let $\textrm{got}$ be the amount that the taker will receive, and $\textrm{gave}$ be the amount that the taker will pay.

      #### Case $f = w$

      If $f = w$, let $\textrm{got} = \min(o_g,t_w)$, and let $\textrm{gave} = \left\lceil\dfrac{o_w \textrm{got}}{o_g}\right\rceil$. This is well-defined since, for live offers, $o_g > 0$.

      In plain english, we only give to the taker up to what they wanted (or what the offer has to give), and follow the offer price to determine what the taker will give.

      Since $\textrm{gave}$ is rounded up, the price might be overevaluated. Still, we cannot spend more than what the taker specified as `takerGives`. At this point [we know](#MgvOfferTaking/checkPrice) that $o_w t_w \leq o_g t_g$, so since $t_g$ is an integer we have
      
      $t_g \geq \left\lceil\dfrac{o_w t_w}{o_g}\right\rceil \geq \left\lceil\dfrac{o_w \textrm{got}}{o_g}\right\rceil = \textrm{gave}$.


      #### Case $f = g$

      If $f = g$, let $\textrm{gave} = \min(o_w,t_g)$, and $\textrm{got} = o_g$ if $o_w = 0$, $\textrm{got} = \left\lfloor\dfrac{o_g \textrm{gave}}{o_w}\right\rfloor$ otherwise.

      In plain english, we spend up to what the taker agreed to pay (or what the offer wants), and follow the offer price to determine what the taker will get. This may exceed $t_w$.

      #### Price adjustment

      Prices are rounded up to ensure maker is not drained on small amounts. It's economically unlikely, but `density` protects the taker from being drained anyway so it is better to default towards protecting the maker here.
      */

        /*
      ### Implementation

      First we check the cases $(f=w \wedge o_g < t_w)\vee(f_g \wedge o_w < t_g)$, in which case the above spec simplifies to $\textrm{got} = o_g, \textrm{gave} = o_w$.

      Otherwise the offer may be partially consumed.
      
      In the case $f=w$ we don't touch $\textrm{got}$ (which was initialized to $t_w$) and compute $\textrm{gave} = \left\lceil\dfrac{o_w t_w}{o_g}\right\rceil$. As shown above we have $\textrm{gave} \leq t_g$.

      In the case $f=g$ we don't touch $\textrm{gave}$ (which was initialized to $t_g$) and compute $\textrm{got} = o_g$ if $o_w = 0$, and $\textrm{got} = \left\lfloor\dfrac{o_g t_g}{o_w}\right\rfloor$ otherwise.
      */
        if (
          (mor.fillWants && offerGives < takerWants) ||
          (!mor.fillWants && offerWants < takerGives)
        ) {
          sor.wants = offerGives;
          sor.gives = offerWants;
        } else {
          if (mor.fillWants) {
            uint product = offerWants * takerWants;
            sor.gives =
              product /
              offerGives +
              (product % offerGives == 0 ? 0 : 1);
          } else {
            if (offerWants == 0) {
              sor.wants = offerGives;
            } else {
              sor.wants = (offerGives * takerGives) / offerWants;
            }
          }
        }
      }
      /* The flashloan is executed by call to `flashloan`. If the call reverts, it means the maker failed to send back `sor.wants` `outbound_tkn` to the taker. Notes :
       * `msg.sender` is the 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) = address(this).call(
        abi.encodeWithSelector(this.flashloan.selector, sor, mor.taker)
      );

      /* `success` is true: trade is complete */
      if (success) {
        /* In case of success, `retdata` encodes the gas used by the offer. */
        gasused = abi.decode(retdata, (uint));
        /* `mgvData` indicates trade success */
        mgvData = bytes32("mgv/tradeSuccess");
        emit OfferSuccess(
          sor.outbound_tkn,
          sor.inbound_tkn,
          sor.offerId,
          mor.taker,
          sor.wants,
          sor.gives
        );

        /* If configured to do so, the 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 multiorder based on the adjusted `sor.wants`/`sor.gives`. */
        /* overflow: sor.{wants,gives} are on 96bits, sor.total{Got,Gave} are on 256 bits. */
        mor.totalGot += sor.wants;
        mor.totalGave += sor.gives;
      } else {
        /* In case of failure, `retdata` encodes a short [status code](#MgvOfferTaking/statusCodes), the gas used by the offer, and an arbitrary 256 bits word sent by the maker.  */
        (mgvData, gasused, makerData) = innerDecode(retdata);
        /* Note that in the `if`s, the literals are bytes32 (stack values), while as revert arguments, they are strings (memory pointers). */
        if (
          mgvData == "mgv/makerRevert" ||
          mgvData == "mgv/makerTransferFail" ||
          mgvData == "mgv/makerReceiveFail"
        ) {
          emit OfferFail(
            sor.outbound_tkn,
            sor.inbound_tkn,
            sor.offerId,
            mor.taker,
            sor.wants,
            sor.gives,
            mgvData
          );

          /* If configured to do so, the 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 `mgvData` not in `["mgv/notExecuted","mgv/tradeSuccess"]` should be blamed on the maker. */
        } else if (mgvData == "mgv/notEnoughGasForMakerTrade") {
          revert("mgv/notEnoughGasForMakerTrade");
        } else if (mgvData == "mgv/takerTransferFail") {
          revert("mgv/takerTransferFail");
        } else {
          /* This code must be unreachable. **Danger**: if a well-crafted offer/maker pair can force a revert of `flashloan`, the 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 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 price 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(
        sor.outbound_tkn,
        sor.inbound_tkn,
        sor.offerId,
        sor.offer,
        sor.offerDetail,
        mgvData != "mgv/tradeSuccess"
      );
    }
  }

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

  All `flashloan` implementations must `require(msg.sender) == address(this))`. */
  function flashloan(ML.SingleOrder calldata sor, address taker)
    external
    virtual
    returns (uint gasused);

  /* ## 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(ML.SingleOrder calldata sor)
    internal
    returns (uint gasused)
  {
    unchecked {
      bytes memory cd = abi.encodeWithSelector(
        IMaker.makerExecute.selector,
        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. */
      /* Note : as a possible future feature, we could stop an order when there's not enough gas left to continue processing offers. This could be done safely by checking, as soon as we start processing an offer, whether `63/64(gasleft-offer_gasbase) > gasreq`. If no, we could stop and know by induction that there is enough gas left to apply fees, stitch offers, etc for the offers already executed. */
      if (!(oldGas - oldGas / 64 >= gasreq)) {
        innerRevert([bytes32("mgv/notEnoughGasForMakerTrade"), "", ""]);
      }

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

      gasused = oldGas - gasleft();

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

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

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

  /* ## executeEnd (abstract) */
  /* Called by `internalSnipes` and `internalMarketOrder`, `executeEnd` may run implementation-specific code after all makers have been called once. In [`InvertedMangrove`](#InvertedMangrove), the function calls the taker once so they can act on their flashloan. In [`Mangrove`], it does nothing. */
  function executeEnd(MultiOrder memory mor, ML.SingleOrder memory sor)
    internal
    virtual;

  /* ## Post execute */
  /* At this point, we know `mgvData != "mgv/notExecuted"`. After executing an offer (whether in a market order or in snipes), we
     1. Call the maker's posthook and sum the total gas used.
     2. If offer failed: sum total penalty due to taker and give remainder to maker.
   */
  function postExecute(
    MultiOrder memory mor,
    ML.SingleOrder memory sor,
    uint gasused,
    bytes32 makerData,
    bytes32 mgvData
  ) internal {
    unchecked {
      if (mgvData == "mgv/tradeSuccess") {
        beforePosthook(sor);
      }

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

      gasused =
        gasused +
        makerPosthook(sor, gasreq - gasused, makerData, mgvData);

      if (mgvData != "mgv/tradeSuccess") {
        mor.totalPenalty += applyPenalty(sor, gasused);
      }
    }
  }

  /* ## beforePosthook (abstract) */
  /* Called by `makerPosthook`, this function can run implementation-specific code before calling the maker has been called a second time. In [`InvertedMangrove`](#InvertedMangrove), all makers are called once so the taker gets all of its money in one shot. Then makers are traversed again and the money is sent back to each taker using `beforePosthook`. In [`Mangrove`](#Mangrove), `beforePosthook` does nothing. */

  function beforePosthook(ML.SingleOrder memory sor) internal virtual;

  /* ## Maker Posthook */
  function makerPosthook(
    ML.SingleOrder memory sor,
    uint gasLeft,
    bytes32 makerData,
    bytes32 mgvData
  ) internal returns (uint gasused) {
    unchecked {
      /* At this point, mgvData can only be `"mgv/tradeSuccess"`, `"mgv/makerRevert"`, `"mgv/makerTransferFail"` or `"mgv/makerReceiveFail"` */
      bytes memory cd = abi.encodeWithSelector(
        IMaker.makerPosthook.selector,
        sor,
        ML.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");
      }

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

      gasused = oldGas - gasleft();

      if (!callSuccess) {
        emit PosthookFail(
          sor.outbound_tkn,
          sor.inbound_tkn,
          sor.offerId,
          posthookData
        );
      }
    }
  }

  /* ## `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 automaticaly 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 {
        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 accoridng to wasted gas.

     Under normal circumstances, we should expect to see bots with a profit expectation dry-running offers locally and executing `snipe` 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 the Mangrove attractive and maintain their offer flow.
     3. Mangrove governance (who may collect a fee) wants to keep the 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:

   * If the transaction was a success, we entirely refund the maker and send nothing to the taker.
   * 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 the 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(ML.SingleOrder memory sor, uint gasused)
    internal
    returns (uint)
  {
    unchecked {
      uint gasreq = sor.offerDetail.gasreq();

      uint provision = 10**9 *
        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 not in `["mgv/notExecuted","mgv/tradeSuccess"]` */
      uint penalty = 10**9 *
        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 the rest (the fees) to the vault. 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, ML.SingleOrder memory sor)
    internal
  {
    unchecked {
      /* Should be statically provable that the 2 transfers below cannot return false under well-behaved ERC20s and a non-blacklisted, non-0 target. */

      uint concreteFee = (mor.totalGot * sor.local.fee()) / 10_000;
      if (concreteFee > 0) {
        mor.totalGot -= concreteFee;
        mor.feePaid = concreteFee;
        require(
          transferToken(sor.outbound_tkn, vault, concreteFee),
          "mgv/feeTransferFail"
        );
      }
      if (mor.totalGot > 0) {
        require(
          transferToken(sor.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 {
        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 {
        revert(data, 96)
      }
    }
  }

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

File 9 of 11 : MgvOfferTakingWithPermit.sol
// SPDX-License-Identifier:	AGPL-3.0

// MgvOfferTakingWithPermit.sol

// Copyright (C) 2021 Giry SAS.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published
// by the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program.  If not, see <https://www.gnu.org/licenses/>.

pragma solidity ^0.8.10;
pragma abicoder v2;
import {HasMgvEvents} from "./MgvLib.sol";

import {MgvOfferTaking} from "./MgvOfferTaking.sol";

abstract contract MgvOfferTakingWithPermit is MgvOfferTaking {
  /* Takers may provide allowances on specific pairs, 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` */
  mapping(address => mapping(address => mapping(address => mapping(address => uint))))
    public allowances;
  /* Storing nonces avoids replay attacks. */
  mapping(address => uint) public 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 public constant PERMIT_TYPEHASH =
    0xb7bf278e51ab1478b10530c0300f911d9ed3562fc93ab5e6593368fe23c077a2;
  /* Initialized in the constructor, `DOMAIN_SEPARATOR` avoids cross-application permit reuse. */
  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"
      );

      allowances[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 {
      allowances[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 `snipesFor` may emit ERC20 `Transfer` events of value 0 from `taker`, but that's already the case with common ERC20 implementations. */
  function marketOrderFor(
    address outbound_tkn,
    address inbound_tkn,
    uint takerWants,
    uint takerGives,
    bool fillWants,
    address taker
  )
    external
    returns (
      uint takerGot,
      uint takerGave,
      uint bounty,
      uint feePaid
    )
  {
    unchecked {
      (takerGot, takerGave, bounty, feePaid) = generalMarketOrder(
        outbound_tkn,
        inbound_tkn,
        takerWants,
        takerGives,
        fillWants,
        taker
      );
      /* 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(outbound_tkn, inbound_tkn, taker, takerGave);
    }
  }

  /* The delegate version of `snipes` is `snipesFor`, which takes a `taker` address as additional argument. */
  function snipesFor(
    address outbound_tkn,
    address inbound_tkn,
    uint[4][] calldata targets,
    bool fillWants,
    address taker
  )
    external
    returns (
      uint successes,
      uint takerGot,
      uint takerGave,
      uint bounty,
      uint feePaid
    )
  {
    unchecked {
      (successes, takerGot, takerGave, bounty, feePaid) = generalSnipes(
        outbound_tkn,
        inbound_tkn,
        targets,
        fillWants,
        taker
      );
      /* The sender's allowance is verified after the order complete so that the actual amounts are checked against the allowance, instead of the declared `takerGives`. The former may be lower.
    
    An immediate consequence is that any funds availale to Mangrove through `approve` can be used to clean offers. After a `snipesFor` where all offers have failed, all token transfers have been reverted, so `takerGave=0` and the check will succeed -- but the sender will still have received the bounty of the failing offers. */
      deductSenderAllowance(outbound_tkn, inbound_tkn, taker, takerGave);
    }
  }

  /* # Misc. low-level functions */

  /* Used by `*For` functions, its 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 {
      uint allowed = allowances[outbound_tkn][inbound_tkn][owner][msg.sender];
      require(allowed >= amount, "mgv/lowAllowance");
      allowances[outbound_tkn][inbound_tkn][owner][msg.sender] =
        allowed -
        amount;

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

File 10 of 11 : MgvPack.sol
pragma solidity ^0.8.10;

// SPDX-License-Identifier: Unlicense

// MgvPack.sol

// This is free and unencumbered software released into the public domain.

// Anyone is free to copy, modify, publish, use, compile, sell, or distribute this software, either in source code form or as a compiled binary, for any purpose, commercial or non-commercial, and by any means.

// In jurisdictions that recognize copyright laws, the author or authors of this software dedicate any and all copyright interest in the software to the public domain. We make this dedication for the benefit of the public at large and to the detriment of our heirs and successors. We intend this dedication to be an overt act of relinquishment in perpetuity of all present and future rights to this software under copyright law.

// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

// For more information, please refer to <https://unlicense.org/>

/* ************************************************** *
            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 {
    u := b
  }
}

// fields are of the form [name,bits,type]

// Can't put all structs under a 'Structs' library due to bad variable shadowing rules in Solidity
// (would generate lots of spurious warnings about a nameclash between Structs.Offer and library Offer for instance)
// struct_defs are of the form [name,obj]
struct OfferStruct {
  uint prev;
  uint next;
  uint wants;
  uint gives;
}
struct OfferDetailStruct {
  address maker;
  uint gasreq;
  uint offer_gasbase;
  uint gasprice;
}
struct GlobalStruct {
  address monitor;
  bool useOracle;
  bool notify;
  uint gasprice;
  uint gasmax;
  bool dead;
}
struct LocalStruct {
  bool active;
  uint fee;
  uint density;
  uint offer_gasbase;
  bool lock;
  uint best;
  uint last;
}

library Offer {
  //some type safety for each struct
  type t is uint;

  uint constant prev_bits = 32;
  uint constant next_bits = 32;
  uint constant wants_bits = 96;
  uint constant gives_bits = 96;

  uint constant prev_before = 0;
  uint constant next_before = prev_before + prev_bits;
  uint constant wants_before = next_before + next_bits;
  uint constant gives_before = wants_before + wants_bits;

  uint constant prev_mask =
    0x00000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffff;
  uint constant next_mask =
    0xffffffff00000000ffffffffffffffffffffffffffffffffffffffffffffffff;
  uint constant wants_mask =
    0xffffffffffffffff000000000000000000000000ffffffffffffffffffffffff;
  uint constant gives_mask =
    0xffffffffffffffffffffffffffffffffffffffff000000000000000000000000;

  function to_struct(t __packed)
    internal
    pure
    returns (OfferStruct memory __s)
  {
    unchecked {
      __s.prev = (t.unwrap(__packed) << prev_before) >> (256 - prev_bits);
      __s.next = (t.unwrap(__packed) << next_before) >> (256 - next_bits);
      __s.wants = (t.unwrap(__packed) << wants_before) >> (256 - wants_bits);
      __s.gives = (t.unwrap(__packed) << gives_before) >> (256 - gives_bits);
    }
  }

  function t_of_struct(OfferStruct memory __s) internal pure returns (t) {
    unchecked {
      return pack(__s.prev, __s.next, __s.wants, __s.gives);
    }
  }

  function eq(t __packed1, t __packed2) internal pure returns (bool) {
    unchecked {
      return t.unwrap(__packed1) == t.unwrap(__packed2);
    }
  }

  function pack(
    uint __prev,
    uint __next,
    uint __wants,
    uint __gives
  ) internal pure returns (t) {
    unchecked {
      return
        t.wrap(
          ((((0 | ((__prev << (256 - prev_bits)) >> prev_before)) |
            ((__next << (256 - next_bits)) >> next_before)) |
            ((__wants << (256 - wants_bits)) >> wants_before)) |
            ((__gives << (256 - gives_bits)) >> gives_before))
        );
    }
  }

  function unpack(t __packed)
    internal
    pure
    returns (
      uint __prev,
      uint __next,
      uint __wants,
      uint __gives
    )
  {
    unchecked {
      __prev = (t.unwrap(__packed) << prev_before) >> (256 - prev_bits);
      __next = (t.unwrap(__packed) << next_before) >> (256 - next_bits);
      __wants = (t.unwrap(__packed) << wants_before) >> (256 - wants_bits);
      __gives = (t.unwrap(__packed) << gives_before) >> (256 - gives_bits);
    }
  }

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

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

  function next(t __packed) internal pure returns (uint) {
    unchecked {
      return (t.unwrap(__packed) << next_before) >> (256 - next_bits);
    }
  }

  function next(t __packed, uint val) internal pure returns (t) {
    unchecked {
      return
        t.wrap(
          (t.unwrap(__packed) & next_mask) |
            (((val << (256 - next_bits)) >> next_before))
        );
    }
  }

  function wants(t __packed) internal pure returns (uint) {
    unchecked {
      return (t.unwrap(__packed) << wants_before) >> (256 - wants_bits);
    }
  }

  function wants(t __packed, uint val) internal pure returns (t) {
    unchecked {
      return
        t.wrap(
          (t.unwrap(__packed) & wants_mask) |
            (((val << (256 - wants_bits)) >> wants_before))
        );
    }
  }

  function gives(t __packed) internal pure returns (uint) {
    unchecked {
      return (t.unwrap(__packed) << gives_before) >> (256 - gives_bits);
    }
  }

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

library OfferDetail {
  //some type safety for each struct
  type t is uint;

  uint constant maker_bits = 160;
  uint constant gasreq_bits = 24;
  uint constant offer_gasbase_bits = 24;
  uint constant gasprice_bits = 16;

  uint constant maker_before = 0;
  uint constant gasreq_before = maker_before + maker_bits;
  uint constant offer_gasbase_before = gasreq_before + gasreq_bits;
  uint constant gasprice_before = offer_gasbase_before + offer_gasbase_bits;

  uint constant maker_mask =
    0x0000000000000000000000000000000000000000ffffffffffffffffffffffff;
  uint constant gasreq_mask =
    0xffffffffffffffffffffffffffffffffffffffff000000ffffffffffffffffff;
  uint constant offer_gasbase_mask =
    0xffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffff;
  uint constant gasprice_mask =
    0xffffffffffffffffffffffffffffffffffffffffffffffffffff0000ffffffff;

  function to_struct(t __packed)
    internal
    pure
    returns (OfferDetailStruct memory __s)
  {
    unchecked {
      __s.maker = address(
        uint160((t.unwrap(__packed) << maker_before) >> (256 - maker_bits))
      );
      __s.gasreq = (t.unwrap(__packed) << gasreq_before) >> (256 - gasreq_bits);
      __s.offer_gasbase =
        (t.unwrap(__packed) << offer_gasbase_before) >>
        (256 - offer_gasbase_bits);
      __s.gasprice =
        (t.unwrap(__packed) << gasprice_before) >>
        (256 - gasprice_bits);
    }
  }

  function t_of_struct(OfferDetailStruct memory __s) internal pure returns (t) {
    unchecked {
      return pack(__s.maker, __s.gasreq, __s.offer_gasbase, __s.gasprice);
    }
  }

  function eq(t __packed1, t __packed2) internal pure returns (bool) {
    unchecked {
      return t.unwrap(__packed1) == t.unwrap(__packed2);
    }
  }

  function pack(
    address __maker,
    uint __gasreq,
    uint __offer_gasbase,
    uint __gasprice
  ) internal pure returns (t) {
    unchecked {
      return
        t.wrap(
          ((((0 |
            ((uint(uint160(__maker)) << (256 - maker_bits)) >> maker_before)) |
            ((__gasreq << (256 - gasreq_bits)) >> gasreq_before)) |
            ((__offer_gasbase << (256 - offer_gasbase_bits)) >>
              offer_gasbase_before)) |
            ((__gasprice << (256 - gasprice_bits)) >> gasprice_before))
        );
    }
  }

  function unpack(t __packed)
    internal
    pure
    returns (
      address __maker,
      uint __gasreq,
      uint __offer_gasbase,
      uint __gasprice
    )
  {
    unchecked {
      __maker = address(
        uint160((t.unwrap(__packed) << maker_before) >> (256 - maker_bits))
      );
      __gasreq = (t.unwrap(__packed) << gasreq_before) >> (256 - gasreq_bits);
      __offer_gasbase =
        (t.unwrap(__packed) << offer_gasbase_before) >>
        (256 - offer_gasbase_bits);
      __gasprice =
        (t.unwrap(__packed) << gasprice_before) >>
        (256 - gasprice_bits);
    }
  }

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

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

  function gasreq(t __packed) internal pure returns (uint) {
    unchecked {
      return (t.unwrap(__packed) << gasreq_before) >> (256 - gasreq_bits);
    }
  }

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

  function offer_gasbase(t __packed) internal pure returns (uint) {
    unchecked {
      return
        (t.unwrap(__packed) << offer_gasbase_before) >>
        (256 - offer_gasbase_bits);
    }
  }

  function offer_gasbase(t __packed, uint val) internal pure returns (t) {
    unchecked {
      return
        t.wrap(
          (t.unwrap(__packed) & offer_gasbase_mask) |
            (((val << (256 - offer_gasbase_bits)) >> offer_gasbase_before))
        );
    }
  }

  function gasprice(t __packed) internal pure returns (uint) {
    unchecked {
      return (t.unwrap(__packed) << gasprice_before) >> (256 - gasprice_bits);
    }
  }

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

library Global {
  //some type safety for each struct
  type t is uint;

  uint constant monitor_bits = 160;
  uint constant useOracle_bits = 8;
  uint constant notify_bits = 8;
  uint constant gasprice_bits = 16;
  uint constant gasmax_bits = 24;
  uint constant dead_bits = 8;

  uint constant monitor_before = 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 monitor_mask =
    0x0000000000000000000000000000000000000000ffffffffffffffffffffffff;
  uint constant useOracle_mask =
    0xffffffffffffffffffffffffffffffffffffffff00ffffffffffffffffffffff;
  uint constant notify_mask =
    0xffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffffffff;
  uint constant gasprice_mask =
    0xffffffffffffffffffffffffffffffffffffffffffff0000ffffffffffffffff;
  uint constant gasmax_mask =
    0xffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffff;
  uint constant dead_mask =
    0xffffffffffffffffffffffffffffffffffffffffffffffffffffff00ffffffff;

  function to_struct(t __packed)
    internal
    pure
    returns (GlobalStruct memory __s)
  {
    unchecked {
      __s.monitor = address(
        uint160((t.unwrap(__packed) << monitor_before) >> (256 - monitor_bits))
      );
      __s.useOracle = (((t.unwrap(__packed) << useOracle_before) >>
        (256 - useOracle_bits)) > 0);
      __s.notify = (((t.unwrap(__packed) << notify_before) >>
        (256 - notify_bits)) > 0);
      __s.gasprice =
        (t.unwrap(__packed) << gasprice_before) >>
        (256 - gasprice_bits);
      __s.gasmax = (t.unwrap(__packed) << gasmax_before) >> (256 - gasmax_bits);
      __s.dead = (((t.unwrap(__packed) << dead_before) >> (256 - dead_bits)) >
        0);
    }
  }

  function t_of_struct(GlobalStruct memory __s) internal pure returns (t) {
    unchecked {
      return
        pack(
          __s.monitor,
          __s.useOracle,
          __s.notify,
          __s.gasprice,
          __s.gasmax,
          __s.dead
        );
    }
  }

  function eq(t __packed1, t __packed2) internal pure returns (bool) {
    unchecked {
      return t.unwrap(__packed1) == t.unwrap(__packed2);
    }
  }

  function pack(
    address __monitor,
    bool __useOracle,
    bool __notify,
    uint __gasprice,
    uint __gasmax,
    bool __dead
  ) internal pure returns (t) {
    unchecked {
      return
        t.wrap(
          ((((((0 |
            ((uint(uint160(__monitor)) << (256 - monitor_bits)) >>
              monitor_before)) |
            ((uint_of_bool(__useOracle) << (256 - useOracle_bits)) >>
              useOracle_before)) |
            ((uint_of_bool(__notify) << (256 - notify_bits)) >>
              notify_before)) |
            ((__gasprice << (256 - gasprice_bits)) >> gasprice_before)) |
            ((__gasmax << (256 - gasmax_bits)) >> gasmax_before)) |
            ((uint_of_bool(__dead) << (256 - dead_bits)) >> dead_before))
        );
    }
  }

  function unpack(t __packed)
    internal
    pure
    returns (
      address __monitor,
      bool __useOracle,
      bool __notify,
      uint __gasprice,
      uint __gasmax,
      bool __dead
    )
  {
    unchecked {
      __monitor = address(
        uint160((t.unwrap(__packed) << monitor_before) >> (256 - monitor_bits))
      );
      __useOracle = (((t.unwrap(__packed) << useOracle_before) >>
        (256 - useOracle_bits)) > 0);
      __notify = (((t.unwrap(__packed) << notify_before) >>
        (256 - notify_bits)) > 0);
      __gasprice =
        (t.unwrap(__packed) << gasprice_before) >>
        (256 - gasprice_bits);
      __gasmax = (t.unwrap(__packed) << gasmax_before) >> (256 - gasmax_bits);
      __dead = (((t.unwrap(__packed) << dead_before) >> (256 - dead_bits)) > 0);
    }
  }

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

  function monitor(t __packed, address val) internal pure returns (t) {
    unchecked {
      return
        t.wrap(
          (t.unwrap(__packed) & monitor_mask) |
            (((uint(uint160(val)) << (256 - monitor_bits)) >> monitor_before))
        );
    }
  }

  function useOracle(t __packed) internal pure returns (bool) {
    unchecked {
      return (((t.unwrap(__packed) << useOracle_before) >>
        (256 - useOracle_bits)) > 0);
    }
  }

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

  function notify(t __packed) internal pure returns (bool) {
    unchecked {
      return (((t.unwrap(__packed) << notify_before) >> (256 - notify_bits)) >
        0);
    }
  }

  function notify(t __packed, bool val) internal pure returns (t) {
    unchecked {
      return
        t.wrap(
          (t.unwrap(__packed) & notify_mask) |
            (((uint_of_bool(val) << (256 - notify_bits)) >> notify_before))
        );
    }
  }

  function gasprice(t __packed) internal pure returns (uint) {
    unchecked {
      return (t.unwrap(__packed) << gasprice_before) >> (256 - gasprice_bits);
    }
  }

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

  function gasmax(t __packed) internal pure returns (uint) {
    unchecked {
      return (t.unwrap(__packed) << gasmax_before) >> (256 - gasmax_bits);
    }
  }

  function gasmax(t __packed, uint val) internal pure returns (t) {
    unchecked {
      return
        t.wrap(
          (t.unwrap(__packed) & gasmax_mask) |
            (((val << (256 - gasmax_bits)) >> gasmax_before))
        );
    }
  }

  function dead(t __packed) internal pure returns (bool) {
    unchecked {
      return (((t.unwrap(__packed) << dead_before) >> (256 - dead_bits)) > 0);
    }
  }

  function dead(t __packed, bool val) internal pure returns (t) {
    unchecked {
      return
        t.wrap(
          (t.unwrap(__packed) & dead_mask) |
            (((uint_of_bool(val) << (256 - dead_bits)) >> dead_before))
        );
    }
  }
}

library Local {
  //some type safety for each struct
  type t is uint;

  uint constant active_bits = 8;
  uint constant fee_bits = 16;
  uint constant density_bits = 112;
  uint constant offer_gasbase_bits = 24;
  uint constant lock_bits = 8;
  uint constant best_bits = 32;
  uint constant last_bits = 32;

  uint constant active_before = 0;
  uint constant fee_before = active_before + active_bits;
  uint constant density_before = fee_before + fee_bits;
  uint constant offer_gasbase_before = density_before + density_bits;
  uint constant lock_before = offer_gasbase_before + offer_gasbase_bits;
  uint constant best_before = lock_before + lock_bits;
  uint constant last_before = best_before + best_bits;

  uint constant active_mask =
    0x00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff;
  uint constant fee_mask =
    0xff0000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffff;
  uint constant density_mask =
    0xffffff0000000000000000000000000000ffffffffffffffffffffffffffffff;
  uint constant offer_gasbase_mask =
    0xffffffffffffffffffffffffffffffffff000000ffffffffffffffffffffffff;
  uint constant lock_mask =
    0xffffffffffffffffffffffffffffffffffffffff00ffffffffffffffffffffff;
  uint constant best_mask =
    0xffffffffffffffffffffffffffffffffffffffffff00000000ffffffffffffff;
  uint constant last_mask =
    0xffffffffffffffffffffffffffffffffffffffffffffffffff00000000ffffff;

  function to_struct(t __packed)
    internal
    pure
    returns (LocalStruct memory __s)
  {
    unchecked {
      __s.active = (((t.unwrap(__packed) << active_before) >>
        (256 - active_bits)) > 0);
      __s.fee = (t.unwrap(__packed) << fee_before) >> (256 - fee_bits);
      __s.density =
        (t.unwrap(__packed) << density_before) >>
        (256 - density_bits);
      __s.offer_gasbase =
        (t.unwrap(__packed) << offer_gasbase_before) >>
        (256 - offer_gasbase_bits);
      __s.lock = (((t.unwrap(__packed) << lock_before) >> (256 - lock_bits)) >
        0);
      __s.best = (t.unwrap(__packed) << best_before) >> (256 - best_bits);
      __s.last = (t.unwrap(__packed) << last_before) >> (256 - last_bits);
    }
  }

  function t_of_struct(LocalStruct memory __s) internal pure returns (t) {
    unchecked {
      return
        pack(
          __s.active,
          __s.fee,
          __s.density,
          __s.offer_gasbase,
          __s.lock,
          __s.best,
          __s.last
        );
    }
  }

  function eq(t __packed1, t __packed2) internal pure returns (bool) {
    unchecked {
      return t.unwrap(__packed1) == t.unwrap(__packed2);
    }
  }

  function pack(
    bool __active,
    uint __fee,
    uint __density,
    uint __offer_gasbase,
    bool __lock,
    uint __best,
    uint __last
  ) internal pure returns (t) {
    unchecked {
      return
        t.wrap(
          (((((((0 |
            ((uint_of_bool(__active) << (256 - active_bits)) >>
              active_before)) | ((__fee << (256 - fee_bits)) >> fee_before)) |
            ((__density << (256 - density_bits)) >> density_before)) |
            ((__offer_gasbase << (256 - offer_gasbase_bits)) >>
              offer_gasbase_before)) |
            ((uint_of_bool(__lock) << (256 - lock_bits)) >> lock_before)) |
            ((__best << (256 - best_bits)) >> best_before)) |
            ((__last << (256 - last_bits)) >> last_before))
        );
    }
  }

  function unpack(t __packed)
    internal
    pure
    returns (
      bool __active,
      uint __fee,
      uint __density,
      uint __offer_gasbase,
      bool __lock,
      uint __best,
      uint __last
    )
  {
    unchecked {
      __active = (((t.unwrap(__packed) << active_before) >>
        (256 - active_bits)) > 0);
      __fee = (t.unwrap(__packed) << fee_before) >> (256 - fee_bits);
      __density =
        (t.unwrap(__packed) << density_before) >>
        (256 - density_bits);
      __offer_gasbase =
        (t.unwrap(__packed) << offer_gasbase_before) >>
        (256 - offer_gasbase_bits);
      __lock = (((t.unwrap(__packed) << lock_before) >> (256 - lock_bits)) > 0);
      __best = (t.unwrap(__packed) << best_before) >> (256 - best_bits);
      __last = (t.unwrap(__packed) << last_before) >> (256 - last_bits);
    }
  }

  function active(t __packed) internal pure returns (bool) {
    unchecked {
      return (((t.unwrap(__packed) << active_before) >> (256 - active_bits)) >
        0);
    }
  }

  function active(t __packed, bool val) internal pure returns (t) {
    unchecked {
      return
        t.wrap(
          (t.unwrap(__packed) & active_mask) |
            (((uint_of_bool(val) << (256 - active_bits)) >> active_before))
        );
    }
  }

  function fee(t __packed) internal pure returns (uint) {
    unchecked {
      return (t.unwrap(__packed) << fee_before) >> (256 - fee_bits);
    }
  }

  function fee(t __packed, uint val) internal pure returns (t) {
    unchecked {
      return
        t.wrap(
          (t.unwrap(__packed) & fee_mask) |
            (((val << (256 - fee_bits)) >> fee_before))
        );
    }
  }

  function density(t __packed) internal pure returns (uint) {
    unchecked {
      return (t.unwrap(__packed) << density_before) >> (256 - density_bits);
    }
  }

  function density(t __packed, uint val) internal pure returns (t) {
    unchecked {
      return
        t.wrap(
          (t.unwrap(__packed) & density_mask) |
            (((val << (256 - density_bits)) >> density_before))
        );
    }
  }

  function offer_gasbase(t __packed) internal pure returns (uint) {
    unchecked {
      return
        (t.unwrap(__packed) << offer_gasbase_before) >>
        (256 - offer_gasbase_bits);
    }
  }

  function offer_gasbase(t __packed, uint val) internal pure returns (t) {
    unchecked {
      return
        t.wrap(
          (t.unwrap(__packed) & offer_gasbase_mask) |
            (((val << (256 - offer_gasbase_bits)) >> offer_gasbase_before))
        );
    }
  }

  function lock(t __packed) internal pure returns (bool) {
    unchecked {
      return (((t.unwrap(__packed) << lock_before) >> (256 - lock_bits)) > 0);
    }
  }

  function lock(t __packed, bool val) internal pure returns (t) {
    unchecked {
      return
        t.wrap(
          (t.unwrap(__packed) & lock_mask) |
            (((uint_of_bool(val) << (256 - lock_bits)) >> lock_before))
        );
    }
  }

  function best(t __packed) internal pure returns (uint) {
    unchecked {
      return (t.unwrap(__packed) << best_before) >> (256 - best_bits);
    }
  }

  function best(t __packed, uint val) internal pure returns (t) {
    unchecked {
      return
        t.wrap(
          (t.unwrap(__packed) & best_mask) |
            (((val << (256 - best_bits)) >> best_before))
        );
    }
  }

  function last(t __packed) internal pure returns (uint) {
    unchecked {
      return (t.unwrap(__packed) << last_before) >> (256 - last_bits);
    }
  }

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

File 11 of 11 : MgvRoot.sol
// SPDX-License-Identifier:	AGPL-3.0

// MgvRoot.sol

// Copyright (C) 2021 Giry SAS.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published
// by the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program.  If not, see <https://www.gnu.org/licenses/>.

/* `MgvRoot` and its descendants describe an orderbook-based exchange ("the Mangrove") where market makers *do not have to provision their offer*. See `structs.js` for a longer introduction. In a nutshell: each offer created by a maker specifies an address (`maker`) to call upon offer execution by a taker. In the normal mode of operation, the 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.

   There is one Mangrove contract that manages all tradeable pairs. This reduces deployment costs for new pairs and lets market makers have all their provision for all pairs in the same place.

   The interaction map between the different actors is as follows:
   <img src="./contactMap.png" width="190%"></img>

   The sequence diagram of a market order is as follows:
   <img src="./sequenceChart.png" width="190%"></img>

   There is a secondary mode of operation in which the _maker_ flashloans the sold amount to the taker.

   The Mangrove contract is `abstract` and accomodates both modes. Two contracts, `Mangrove` and `InvertedMangrove` inherit from it, one per mode of operation.

   The contract structure is as follows:
   <img src="./modular_mangrove.svg" width="180%"> </img>
 */

pragma solidity ^0.8.10;
pragma abicoder v2;
import {MgvLib as ML, HasMgvEvents, IMgvMonitor, P} from "./MgvLib.sol";

/* `MgvRoot` contains state variables used everywhere in the operation of the Mangrove and their related function. */
contract MgvRoot is HasMgvEvents {
  using P.Global for P.Global.t;
  using P.Local for P.Local.t;

  /* # State variables */
  //+clear+
  /* The `vault` address. If a pair has fees >0, those fees are sent to the vault. */
  address public vault;

  /* Global mgv configuration, encoded in a 256 bits word. The information encoded is detailed in [`structs.js`](#structs.js). */
  P.Global.t internal internal_global;
  /* Configuration mapping for each token pair of the form `outbound_tkn => inbound_tkn => P.Local.t`. The structure of each `P.Local.t` value is detailed in [`structs.js`](#structs.js). It fits in one word. */
  mapping(address => mapping(address => P.Local.t)) internal locals;

  /* Checking the size of `density` is necessary to prevent overflow when `density` is used in calculations. */
  function checkDensity(uint density) internal pure returns (bool) {
    unchecked {
      return uint112(density) == density;
    }
  }

  /* Checking the size of `gasprice` is necessary to prevent a) data loss when `gasprice` is copied to an `OfferDetail` struct, and b) overflow when `gasprice` is used in calculations. */
  function checkGasprice(uint gasprice) internal pure returns (bool) {
    unchecked {
      return uint16(gasprice) == gasprice;
    }
  }

  /* # Configuration Reads */
  /* Reading the configuration for a pair involves reading the config global to all pairs and the local one. In addition, a global parameter (`gasprice`) and a local one (`density`) may be read from the oracle. */
  function config(address outbound_tkn, address inbound_tkn)
    public
    view
    returns (P.Global.t _global, P.Local.t _local)
  {
    unchecked {
      _global = internal_global;
      _local = locals[outbound_tkn][inbound_tkn];
      if (_global.useOracle()) {
        (uint gasprice, uint density) = IMgvMonitor(_global.monitor()).read(
          outbound_tkn,
          inbound_tkn
        );
        if (checkGasprice(gasprice)) {
          _global = _global.gasprice(gasprice);
        }
        if (checkDensity(density)) {
          _local = _local.density(density);
        }
      }
    }
  }

  /* Returns the configuration in an ABI-compatible struct. Should not be called internally, would be a huge memory copying waste. Use `config` instead. */
  function configInfo(address outbound_tkn, address inbound_tkn)
    external
    view
    returns (P.GlobalStruct memory global, P.LocalStruct memory local)
  {
    unchecked {
      (P.Global.t _global, P.Local.t _local) = config(
        outbound_tkn,
        inbound_tkn
      );
      global = _global.to_struct();
      local = _local.to_struct();
    }
  }

  /* Convenience function to check whether given pair is locked */
  function locked(address outbound_tkn, address inbound_tkn)
    external
    view
    returns (bool)
  {
    P.Local.t local = locals[outbound_tkn][inbound_tkn];
    return local.lock();
  }

  /*
  # Gatekeeping

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

  /* `unlockedMarketOnly` protects modifying the market 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 market.  */
  function unlockedMarketOnly(P.Local.t local) internal pure {
    require(!local.lock(), "mgv/reentrancyLocked");
  }

  /* <a id="Mangrove/definition/liveMgvOnly"></a>
     In case of emergency, the 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 the 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(P.Global.t _global) internal pure {
    require(!_global.dead(), "mgv/dead");
  }

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

Settings
{
  "evmVersion": "london",
  "libraries": {},
  "metadata": {
    "bytecodeHash": "ipfs",
    "useLiteralContent": true
  },
  "optimizer": {
    "enabled": true,
    "runs": 20000
  },
  "remappings": [],
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "devdoc",
        "userdoc",
        "metadata",
        "abi"
      ]
    }
  }
}

Contract ABI

[{"inputs":[{"internalType":"address","name":"governance","type":"address"},{"internalType":"uint256","name":"gasprice","type":"uint256"},{"internalType":"uint256","name":"gasmax","type":"uint256"}],"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":false,"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":[{"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":"address","name":"outbound_tkn","type":"address"},{"indexed":true,"internalType":"address","name":"inbound_tkn","type":"address"},{"indexed":false,"internalType":"uint256","name":"id","type":"uint256"},{"indexed":false,"internalType":"address","name":"taker","type":"address"},{"indexed":false,"internalType":"uint256","name":"takerWants","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"takerGives","type":"uint256"},{"indexed":false,"internalType":"bytes32","name":"mgvData","type":"bytes32"}],"name":"OfferFail","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"outbound_tkn","type":"address"},{"indexed":true,"internalType":"address","name":"inbound_tkn","type":"address"},{"indexed":false,"internalType":"uint256","name":"id","type":"uint256"}],"name":"OfferRetract","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"outbound_tkn","type":"address"},{"indexed":true,"internalType":"address","name":"inbound_tkn","type":"address"},{"indexed":false,"internalType":"uint256","name":"id","type":"uint256"},{"indexed":false,"internalType":"address","name":"taker","type":"address"},{"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":"address","name":"outbound_tkn","type":"address"},{"indexed":true,"internalType":"address","name":"inbound_tkn","type":"address"},{"indexed":false,"internalType":"address","name":"maker","type":"address"},{"indexed":false,"internalType":"uint256","name":"wants","type":"uint256"},{"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"},{"indexed":false,"internalType":"uint256","name":"prev","type":"uint256"}],"name":"OfferWrite","type":"event"},{"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":"taker","type":"address"},{"indexed":false,"internalType":"uint256","name":"takerGot","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"takerGave","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"penalty","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"feePaid","type":"uint256"}],"name":"OrderComplete","type":"event"},{"anonymous":false,"inputs":[],"name":"OrderStart","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"outbound_tkn","type":"address"},{"indexed":true,"internalType":"address","name":"inbound_tkn","type":"address"},{"indexed":false,"internalType":"uint256","name":"offerId","type":"uint256"},{"indexed":false,"internalType":"bytes32","name":"posthookData","type":"bytes32"}],"name":"PosthookFail","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"outbound_tkn","type":"address"},{"indexed":true,"internalType":"address","name":"inbound_tkn","type":"address"},{"indexed":false,"internalType":"bool","name":"value","type":"bool"}],"name":"SetActive","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"outbound_tkn","type":"address"},{"indexed":true,"internalType":"address","name":"inbound_tkn","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"SetDensity","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"outbound_tkn","type":"address"},{"indexed":true,"internalType":"address","name":"inbound_tkn","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"SetFee","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"outbound_tkn","type":"address"},{"indexed":true,"internalType":"address","name":"inbound_tkn","type":"address"},{"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":"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"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"value","type":"address"}],"name":"SetVault","type":"event"},{"inputs":[],"name":"DOMAIN_SEPARATOR","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"PERMIT_TYPEHASH","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":"uint256","name":"fee","type":"uint256"},{"internalType":"uint256","name":"density","type":"uint256"},{"internalType":"uint256","name":"offer_gasbase","type":"uint256"}],"name":"activate","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"allowances","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"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":[{"internalType":"address","name":"","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"outbound_tkn","type":"address"},{"internalType":"address","name":"inbound_tkn","type":"address"}],"name":"best","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"outbound_tkn","type":"address"},{"internalType":"address","name":"inbound_tkn","type":"address"}],"name":"config","outputs":[{"internalType":"Global.t","name":"_global","type":"uint256"},{"internalType":"Local.t","name":"_local","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"outbound_tkn","type":"address"},{"internalType":"address","name":"inbound_tkn","type":"address"}],"name":"configInfo","outputs":[{"components":[{"internalType":"address","name":"monitor","type":"address"},{"internalType":"bool","name":"useOracle","type":"bool"},{"internalType":"bool","name":"notify","type":"bool"},{"internalType":"uint256","name":"gasprice","type":"uint256"},{"internalType":"uint256","name":"gasmax","type":"uint256"},{"internalType":"bool","name":"dead","type":"bool"}],"internalType":"struct GlobalStruct","name":"global","type":"tuple"},{"components":[{"internalType":"bool","name":"active","type":"bool"},{"internalType":"uint256","name":"fee","type":"uint256"},{"internalType":"uint256","name":"density","type":"uint256"},{"internalType":"uint256","name":"offer_gasbase","type":"uint256"},{"internalType":"bool","name":"lock","type":"bool"},{"internalType":"uint256","name":"best","type":"uint256"},{"internalType":"uint256","name":"last","type":"uint256"}],"internalType":"struct LocalStruct","name":"local","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"outbound_tkn","type":"address"},{"internalType":"address","name":"inbound_tkn","type":"address"}],"name":"deactivate","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"outbound_tkn","type":"address"},{"internalType":"address","name":"inbound_tkn","type":"address"},{"internalType":"uint256","name":"offerId","type":"uint256"},{"internalType":"Offer.t","name":"offer","type":"uint256"},{"internalType":"uint256","name":"wants","type":"uint256"},{"internalType":"uint256","name":"gives","type":"uint256"},{"internalType":"OfferDetail.t","name":"offerDetail","type":"uint256"},{"internalType":"Global.t","name":"global","type":"uint256"},{"internalType":"Local.t","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"}],"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":[],"name":"governance","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"Offer.t","name":"offer","type":"uint256"}],"name":"isLive","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"kill","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"outbound_tkn","type":"address"},{"internalType":"address","name":"inbound_tkn","type":"address"}],"name":"locked","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"outbound_tkn","type":"address"},{"internalType":"address","name":"inbound_tkn","type":"address"},{"internalType":"uint256","name":"takerWants","type":"uint256"},{"internalType":"uint256","name":"takerGives","type":"uint256"},{"internalType":"bool","name":"fillWants","type":"bool"}],"name":"marketOrder","outputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"outbound_tkn","type":"address"},{"internalType":"address","name":"inbound_tkn","type":"address"},{"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":"marketOrderFor","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":[{"internalType":"address","name":"outbound_tkn","type":"address"},{"internalType":"address","name":"inbound_tkn","type":"address"},{"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":"pivotId","type":"uint256"}],"name":"newOffer","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"nonces","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"offerDetails","outputs":[{"internalType":"OfferDetail.t","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"outbound_tkn","type":"address"},{"internalType":"address","name":"inbound_tkn","type":"address"},{"internalType":"uint256","name":"offerId","type":"uint256"}],"name":"offerInfo","outputs":[{"components":[{"internalType":"uint256","name":"prev","type":"uint256"},{"internalType":"uint256","name":"next","type":"uint256"},{"internalType":"uint256","name":"wants","type":"uint256"},{"internalType":"uint256","name":"gives","type":"uint256"}],"internalType":"struct OfferStruct","name":"offer","type":"tuple"},{"components":[{"internalType":"address","name":"maker","type":"address"},{"internalType":"uint256","name":"gasreq","type":"uint256"},{"internalType":"uint256","name":"offer_gasbase","type":"uint256"},{"internalType":"uint256","name":"gasprice","type":"uint256"}],"internalType":"struct OfferDetailStruct","name":"offerDetail","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"offers","outputs":[{"internalType":"Offer.t","name":"","type":"uint256"}],"stateMutability":"view","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":[{"internalType":"address","name":"outbound_tkn","type":"address"},{"internalType":"address","name":"inbound_tkn","type":"address"},{"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":[{"internalType":"address","name":"outbound_tkn","type":"address"},{"internalType":"address","name":"inbound_tkn","type":"address"},{"internalType":"uint256","name":"density","type":"uint256"}],"name":"setDensity","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"outbound_tkn","type":"address"},{"internalType":"address","name":"inbound_tkn","type":"address"},{"internalType":"uint256","name":"fee","type":"uint256"}],"name":"setFee","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"outbound_tkn","type":"address"},{"internalType":"address","name":"inbound_tkn","type":"address"},{"internalType":"uint256","name":"offer_gasbase","type":"uint256"}],"name":"setGasbase","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"gasmax","type":"uint256"}],"name":"setGasmax","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"gasprice","type":"uint256"}],"name":"setGasprice","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"governanceAddress","type":"address"}],"name":"setGovernance","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"monitor","type":"address"}],"name":"setMonitor","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bool","name":"notify","type":"bool"}],"name":"setNotify","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bool","name":"useOracle","type":"bool"}],"name":"setUseOracle","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"vaultAddress","type":"address"}],"name":"setVault","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"outbound_tkn","type":"address"},{"internalType":"address","name":"inbound_tkn","type":"address"},{"internalType":"uint256[4][]","name":"targets","type":"uint256[4][]"},{"internalType":"bool","name":"fillWants","type":"bool"}],"name":"snipes","outputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"outbound_tkn","type":"address"},{"internalType":"address","name":"inbound_tkn","type":"address"},{"internalType":"uint256[4][]","name":"targets","type":"uint256[4][]"},{"internalType":"bool","name":"fillWants","type":"bool"},{"internalType":"address","name":"taker","type":"address"}],"name":"snipesFor","outputs":[{"internalType":"uint256","name":"successes","type":"uint256"},{"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":[{"internalType":"address","name":"outbound_tkn","type":"address"},{"internalType":"address","name":"inbound_tkn","type":"address"},{"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":"pivotId","type":"uint256"},{"internalType":"uint256","name":"offerId","type":"uint256"}],"name":"updateOffer","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"vault","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","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"}]

60a06040523480156200001157600080fd5b5060405162005f1538038062005f15833981016040819052620000349162000484565b828282604051806040016040528060088152602001674d616e67726f766560c01b815250808484847f72201d073f249840099a96530e189c4291c1852040b1e96ff1f606db656be4ff60405160405180910390a1620000938362000165565b6200009e82620001c4565b620000a98162000277565b620000b48362000327565b5050815160208084019190912060408051808201825260018152603160f81b9084015280517f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f938101939093528201527fc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc660608201524660808201523060a082015260c001905060408051601f19818403018152919052805160209091012060805250620004c99650505050505050565b6200016f620003d8565b600080546001600160a01b0319166001600160a01b0383169081179091556040519081527fd459c7242e23d490831b5676a611c4342d899d28f342d89ae80793e56a930f30906020015b60405180910390a150565b620001ce620003d8565b61ffff81168114620002275760405162461bcd60e51b815260206004820152601a60248201527f6d67762f636f6e6669672f67617370726963652f31366269747300000000000060448201526064015b60405180910390fd5b62000243816001546200044960201b620022991790919060201c565b6001556040518181527fdbebd814ae648f654dcc50c734aa76e55a32e96b7d85303a08e2ddf11874a0dd90602001620001b9565b62000281620003d8565b808162ffffff1614620002d75760405162461bcd60e51b815260206004820152601860248201527f6d67762f636f6e6669672f6761736d61782f323462697473000000000000000060448201526064016200021e565b620002f3816001546200046760201b620022d11790919060201c565b6001556040518181527f8cdff0cd5ed2543bcdf7eb93d8ec1c23626e579925f4531beeef15d84cf78d1890602001620001b9565b62000331620003d8565b6001600160a01b038116620003895760405162461bcd60e51b815260206004820152601360248201527f6d67762f636f6e6669672f676f762f6e6f74300000000000000000000000000060448201526064016200021e565b600380546001600160a01b0319166001600160a01b0383169081179091556040519081527f24a8c4807b324a269a51827c3446b8ac1cc13810d7d0c0ca1efafabddd7b621990602001620001b9565b6003546001600160a01b0316331480620003f157503330145b806200040657506003546001600160a01b0316155b620004475760405162461bcd60e51b815260206004820152601060248201526f1b59dd8bdd5b985d5d1a1bdc9a5e995960821b60448201526064016200021e565b565b60401b69ffff00000000000000001661ffff60401b19919091161790565b60281b67ffffff00000000001662ffffff60281b19919091161790565b6000806000606084860312156200049a57600080fd5b83516001600160a01b0381168114620004b257600080fd5b602085015160409095015190969495509392505050565b608051615a29620004ec600039600081816103e90152611a710152615a296000f3fe6080604052600436106102ca5760003560e01c80636d27de6711610179578063b60d4288116100d6578063dc7e98df1161008a578063fbfa77cf11610064578063fbfa77cf1461098d578063fbffe5fd146109ad578063fcaa7e2e146109cd57600080fd5b8063dc7e98df1461092d578063def5ddaa1461094d578063fbb669101461096d57600080fd5b8063cbf75c9a116100bb578063cbf75c9a146108b8578063d0ee5099146108ed578063db20266f1461090d57600080fd5b8063b60d428814610869578063c7bb25d01461087157600080fd5b8063aa4dd2821161012d578063ad97db1b11610112578063ad97db1b14610809578063b09d2a1614610829578063b2b120571461084957600080fd5b8063aa4dd282146107c9578063ab033ea9146107e957600080fd5b80637ecebe001161015e5780637ecebe00146107065780638c66715c1461073357806395999ee3146107a957600080fd5b80636d27de671461069957806370a08231146106d957600080fd5b80635722647b11610227578063644e6703116101db5780636817031b116101c05780636817031b1461062857806368c13d6b146106485780636a4f76911461068657600080fd5b8063644e6703146105515780636804f6131461060857600080fd5b80635a611f4e1161020c5780635a611f4e146104d95780635aa6e675146104f95780635cd823901461053157600080fd5b80635722647b1461047b57806359eba454146104b957600080fd5b806330adf81f1161027e57806341c0e1b51161026357806341c0e1b51461040b57806346e142fa1461042057806349f6d2dc1461046857600080fd5b806330adf81f146103955780633644e515146103d757600080fd5b806327507458116102af578063275074581461031257806327d097c6146103555780632e1a7d4d1461037557600080fd5b80630b73a18f146102df57806323024408146102ff57600080fd5b366102da576102d8336109ed565b005b600080fd5b3480156102eb57600080fd5b506102d86102fa3660046151ed565b610a14565b6102d861030d36600461523a565b6109ed565b34801561031e57600080fd5b5061034061032d366004615255565b6bffffffffffffffffffffffff16151590565b60405190151581526020015b60405180910390f35b34801561036157600080fd5b506102d861037036600461526e565b610ae6565b34801561038157600080fd5b50610340610390366004615255565b610be3565b3480156103a157600080fd5b506103c97fb7bf278e51ab1478b10530c0300f911d9ed3562fc93ab5e6593368fe23c077a281565b60405190815260200161034c565b3480156103e357600080fd5b506103c97f000000000000000000000000000000000000000000000000000000000000000081565b34801561041757600080fd5b506102d8610c3d565b34801561042c57600080fd5b5061044061043b3660046152fd565b610c9f565b604080519586526020860194909452928401919091526060830152608082015260a00161034c565b6103c9610476366004615376565b610ccb565b34801561048757600080fd5b506103c961049636600461526e565b600560209081526000938452604080852082529284528284209052825290205481565b3480156104c557600080fd5b506103406104d43660046153d4565b610ebf565b3480156104e557600080fd5b506102d86104f436600461541f565b610f4d565b34801561050557600080fd5b50600354610519906001600160a01b031681565b6040516001600160a01b03909116815260200161034c565b34801561053d57600080fd5b506102d861054c36600461523a565b610fc9565b34801561055d57600080fd5b5061057161056c36600461543c565b611046565b6040805183516001600160a01b0316815260208085015115158183015284830151151582840152606080860151818401526080808701518185015260a0968701511515878501528551151560c0808601919091529286015160e0850152938501516101008401528401516101208301529183015115156101408201529282015161016084015201516101808201526101a00161034c565b34801561061457600080fd5b506102d861062336600461541f565b611149565b34801561063457600080fd5b506102d861064336600461523a565b6111bf565b34801561065457600080fd5b506103c961066336600461526e565b600460209081526000938452604080852082529284528284209052825290205481565b6102d861069436600461546f565b61122d565b3480156106a557600080fd5b506106b96106b43660046154d6565b6113a7565b60408051948552602085019390935291830152606082015260800161034c565b3480156106e557600080fd5b506103c96106f436600461523a565b60066020526000908152604090205481565b34801561071257600080fd5b506103c961072136600461523a565b60086020526000908152604090205481565b34801561073f57600080fd5b5061075361074e36600461526e565b6113de565b60408051835181526020808501518183015284830151828401526060948501518583015283516001600160a01b0316608083015283015160a08201529082015160c082015291015160e08201526101000161034c565b3480156107b557600080fd5b506102d86107c4366004615255565b6114ba565b3480156107d557600080fd5b506103c96107e436600461553e565b61157e565b3480156107f557600080fd5b506102d861080436600461523a565b6116c4565b34801561081557600080fd5b506103c9610824366004615571565b611788565b34801561083557600080fd5b506102d86108443660046155c0565b61195f565b34801561085557600080fd5b50610440610864366004615656565b611cd9565b6102d8611d15565b34801561087d57600080fd5b506103c961088c3660046156d0565b600760209081526000948552604080862082529385528385208152918452828420909152825290205481565b3480156108c457600080fd5b506108d86108d336600461543c565b611d20565b6040805192835260208301919091520161034c565b3480156108f957600080fd5b506102d861090836600461543c565b611eac565b34801561091957600080fd5b5061034061092836600461543c565b611f35565b34801561093957600080fd5b506102d861094836600461526e565b611f70565b34801561095957600080fd5b506103c961096836600461543c565b61206d565b34801561097957600080fd5b506102d861098836600461526e565b6120a5565b34801561099957600080fd5b50600054610519906001600160a01b031681565b3480156109b957600080fd5b506106b96109c8366004615724565b6121ad565b3480156109d957600080fd5b506102d86109e8366004615255565b6121d4565b60006109fa600080611d20565b509050610a0681612307565b610a10823461235f565b5050565b610a1c6123bb565b6001600160a01b0385811660008181526002602090815260408083209489168084529482529182902080547effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f010000000000000000000000000000000000000000000000000000000000000017905581516001815291517fc4ad6e84469caa93e473acfa6c029fcf9d3757d8e268e6233c150771e9f88da69281900390910190a3610ac9858585611f70565b610ad48585846120a5565b610adf858583610ae6565b5050505050565b610aee6123bb565b808162ffffff1614610b475760405162461bcd60e51b815260206004820152601f60248201527f6d67762f636f6e6669672f6f666665725f676173626173652f3234626974730060448201526064015b60405180910390fd5b6001600160a01b0383811660008181526002602090815260408083209487168084529482529182902080547fffffffffffffffffffffffffffffffffff000000ffffffffffffffffffffffff166effffff000000000000000000000000606088901b1617905590518481527fc5859916e5f3fc1ed4d2cfb8c7a6cf709580f228fa0c9d9e0fa468744c7e4a9191015b60405180910390a3505050565b6000610bef3383612433565b60405133908390600081818185875af1925050503d8060008114610c2f576040519150601f19603f3d011682016040523d82523d6000602084013e610c34565b606091505b50909392505050565b610c456123bb565b6001547fffffffffffffffffffffffffffffffffffffffffffffffffffffff00ffffffff16640100000000176001556040517fbe26733c2bf6ff3ea5ba8cfe744422bd49052ff9ed5685c9e81e6f9321dbaddd90600090a1565b6000806000806000610cb58a8a8a8a8a336124f8565b939e929d50909b50995090975095505050505050565b6000610d3c60405180610160016040528060006001600160a01b0316815260200160006001600160a01b031681526020016000815260200160008152602001600081526020016000815260200160008152602001600081526020016000815260200160008152602001600081525090565b610d468989611d20565b6101208301819052610100830191909152610d60906126f1565b610d74816101000151826101200151612746565b3415610d8457610d84333461235f565b61012081015160181c63ffffffff166001016080820181905263ffffffff811614610df15760405162461bcd60e51b815260206004820152601360248201527f6d67762f6f6666657249644f766572666c6f77000000000000000000000000006044820152606401610b3e565b6080810151610120820151610e359160181b66ffffffff000000167fffffffffffffffffffffffffffffffffffffffffffffffffff00000000ffffff919091161790565b6101208201526001600160a01b03808a16825288166020820152604081018790526060810186905260a0810185905260c0810184905260e08101839052610e7d8160006127a7565b61012081015181516001600160a01b03908116600090815260026020908152604080832082870151909416835292905220556080015198975050505050505050565b6001600160a01b038481166000818152600760209081526040808320888616808552908352818420338086529084528285209689168086529684528285208890558251908152928301959095528101859052909291907f6e791f456e6f5034d4928b936c32bf7e650d8228543ce7985d632ab03934837d9060600160405180910390a3506001949350505050565b610f556123bb565b6001547fffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffffffff166aff00000000000000000000605083901b161760015560405181151581527f9b84fe4d9ffc14a6f7ec50c18884ee0d4bb8165aa718a28558df12ab4bd32b14906020015b60405180910390a150565b610fd16123bb565b6001546bffffffffffffffffffffffff167fffffffffffffffffffffffffffffffffffffffff000000000000000000000000606083901b16176001556040516001600160a01b03821681527ff592e42c32883b35bebfbf9d9b801570a25d5ccbba452310746ad2cfd20d548f90602001610fbe565b6040805160c081018252600080825260208201819052918101829052606081018290526080810182905260a08101919091526110bc6040518060e0016040528060001515815260200160008152602001600081526020016000815260200160001515815260200160008152602001600081525090565b6000806110c98686611d20565b91509150611133826040805160c081018252606083811c8252605884901c60ff9081161515602080850191909152605086901c82161515848601529385901c61ffff1691830191909152602884901c62ffffff1660808301529290911c909116151560a082015290565b935061113e81612ff0565b925050509250929050565b6111516123bb565b6001547fffffffffffffffffffffffffffffffffffffffff00ffffffffffffffffffffff166bff0000000000000000000000605883901b161760015560405181151581527fbf39fbfab835c119637f4dbc4fb0f428c559c210200405d1ec25ddb50a93f9d490602001610fbe565b6111c76123bb565b600080547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b0383169081179091556040519081527fd459c7242e23d490831b5676a611c4342d899d28f342d89ae80793e56a930f3090602001610fbe565b61129c60405180610160016040528060006001600160a01b0316815260200160006001600160a01b031681526020016000815260200160008152602001600081526020016000815260200160008152602001600081526020016000815260200160008152602001600081525090565b6112a68989611d20565b61012083018190526101008301919091526112c0906126f1565b6112d4816101000151826101200151612746565b34156112e4576112e4333461235f565b6001600160a01b03808a16808352908916602080840182905260408085018b9052606085018a90526080850186905260a0850189905260c0850188905260e08501879052600093845260048252808420928452918152818320858452905290205461014082015261012081015161135c8260016127a7565b610120820151811461139b5761012082015182516001600160a01b03908116600090815260026020908152604080832082880151909416835292905220555b50505050505050505050565b6000806000806113bb8a8a8a8a8a8a6130a5565b929650909450925090506113d18a8a8786613425565b9650965096509692505050565b6114096040518060800160405280600081526020016000815260200160008152602001600081525090565b61143d604051806080016040528060006001600160a01b031681526020016000815260200160008152602001600081525090565b6001600160a01b03808616600090815260046020908152604080832093881683529281528282208683529052205461147481613537565b6001600160a01b038088166000908152600560209081526040808320938a168352928152828220888352905220549093506114ae8161359e565b92505050935093915050565b6114c26123bb565b808162ffffff16146115165760405162461bcd60e51b815260206004820152601860248201527f6d67762f636f6e6669672f6761736d61782f32346269747300000000000000006044820152606401610b3e565b6001547fffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffff1667ffffff0000000000602883901b16176001556040518181527f8cdff0cd5ed2543bcdf7eb93d8ec1c23626e579925f4531beeef15d84cf78d1890602001610fbe565b60003330146115cf5760405162461bcd60e51b815260206004820152601760248201527f6d67762f666c6173686c6f616e2f70726f7465637465640000000000000000006044820152606401610b3e565b6115ee6115e2604085016020860161523a565b83308660a0013561360d565b1561167857611618611606604085016020860161523a565b60c085013560601c8560a00135613719565b1561162d576116268361381e565b90506116be565b604080516060810182527f6d67762f6d616b6572526563656976654661696c00000000000000000000000081526000602082018190529181019190915261167390606081fd5b6116be565b604080516060810182527f6d67762f74616b65725472616e736665724661696c00000000000000000000008152600060208201819052918101919091526116be90606081fd5b92915050565b6116cc6123bb565b6001600160a01b0381166117225760405162461bcd60e51b815260206004820152601360248201527f6d67762f636f6e6669672f676f762f6e6f7430000000000000000000000000006044820152606401610b3e565b600380547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b0383169081179091556040519081527f24a8c4807b324a269a51827c3446b8ac1cc13810d7d0c0ca1efafabddd7b621990602001610fbe565b6000806117958686611d20565b9150506117a1816126f1565b6001600160a01b038087166000818152600460209081526040808320948a1680845294825280832089845282528083205493835260058252808320948352938152838220888352905291909120546117f98160601c90565b6001600160a01b0316336001600160a01b0316146118595760405162461bcd60e51b815260206004820152601d60248201527f6d67762f726574726163744f666665722f756e617574686f72697a65640000006044820152606401610b3e565b6bffffffffffffffffffffffff8216156118c2578261188e898961187d8660e01c90565b60c087901c63ffffffff1688613a00565b93508084146118c0576001600160a01b03808a166000908152600260209081526040808320938c168352929052208490555b505b6118d088888885858a613b22565b841561190757603081901c62ffffff16604882901c62ffffff1601602082901c61ffff16633b9aca0002029350611907338561235f565b866001600160a01b0316886001600160a01b03167fe2ce51fba856682424155a2d89b2ad1d2adde6ea0a9e3115b4b474cd6cb1798b8860405161194c91815260200190565b60405180910390a3505050949350505050565b428410156119af5760405162461bcd60e51b815260206004820152601260248201527f6d67762f7065726d69742f6578706972656400000000000000000000000000006044820152606401610b3e565b6001600160a01b03878116600081815260086020908152604080832080546001810190915581517fb7bf278e51ab1478b10530c0300f911d9ed3562fc93ab5e6593368fe23c077a2818501528f8716818401528e871660608201526080810195909552948b1660a085015260c084018a905260e084018590526101008085018a905281518086039091018152610120850190915280519101207f19010000000000000000000000000000000000000000000000000000000000006101408401527f00000000000000000000000000000000000000000000000000000000000000006101428401526101628301529061018201604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181528282528051602091820120600080855291840180845281905260ff89169284019290925260608301879052608083018690529092509060019060a0016020604051602081039080840390855afa158015611b2a573d6000803e3d6000fd5b50506040517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe001519150506001600160a01b03811615801590611b7e5750896001600160a01b0316816001600160a01b0316145b611bca5760405162461bcd60e51b815260206004820152601b60248201527f6d67762f7065726d69742f696e76616c69645369676e617475726500000000006044820152606401610b3e565b87600760008e6001600160a01b03166001600160a01b0316815260200190815260200160002060008d6001600160a01b03166001600160a01b0316815260200190815260200160002060008c6001600160a01b03166001600160a01b0316815260200190815260200160002060008b6001600160a01b03166001600160a01b03168152602001908152602001600020819055508a6001600160a01b03168c6001600160a01b03167f6e791f456e6f5034d4928b936c32bf7e650d8228543ce7985d632ab03934837d8c8c8c604051611cc3939291906001600160a01b039384168152919092166020820152604081019190915260600190565b60405180910390a3505050505050505050505050565b6000806000806000611cef8b8b8b8b8b8b6124f8565b939850919650945092509050611d078b8b8886613425565b965096509650965096915050565b611d1e336109ed565b565b6001546001600160a01b03838116600090815260026020908152604080832093861683529290522054605882901c60ff1615611ea557600080611d638460601c90565b6040517f6968abd30000000000000000000000000000000000000000000000000000000081526001600160a01b03888116600483015287811660248301529190911690636968abd3906044016040805180830381865afa158015611dcb573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611def9190615771565b91509150611e008261ffff81161490565b15611e3d577fffffffffffffffffffffffffffffffffffffffffffff0000ffffffffffffffff841669ffff0000000000000000604084901b161793505b6dffffffffffffffffffffffffffff8116811415611ea2577fffffff0000000000000000000000000000ffffffffffffffffffffffffffffff83167cffffffffffffffffffffffffffff000000000000000000000000000000607883901b161761113e565b50505b9250929050565b611eb46123bb565b6001600160a01b03828116600081815260026020908152604080832094861680845294825280832080547effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1690558051928352517fc4ad6e84469caa93e473acfa6c029fcf9d3757d8e268e6233c150771e9f88da69281900390910190a35050565b6001600160a01b038281166000908152600260209081526040808320938516835292905290812054605881901c60ff1615155b949350505050565b611f786123bb565b6101f4811115611fca5760405162461bcd60e51b815260206004820152601460248201527f6d67762f636f6e6669672f6665652f3c3d3530300000000000000000000000006044820152606401610b3e565b6001600160a01b0383811660008181526002602090815260408083209487168084529482529182902080547fff0000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffff167effff000000000000000000000000000000000000000000000000000000000060e888901b1617905590518481527f44a6d70a601a6f8a85c075467e9d7245897140cbf6dd505c9d9d764459f5fb649101610bd6565b6001600160a01b038281166000908152600260209081526040808320938516835292905290812054603881901c63ffffffff16611f68565b6120ad6123bb565b6dffffffffffffffffffffffffffff8116811461210c5760405162461bcd60e51b815260206004820152601a60248201527f6d67762f636f6e6669672f64656e736974792f313132626974730000000000006044820152606401610b3e565b6001600160a01b0383811660008181526002602090815260408083209487168084529482529182902080547fffffff0000000000000000000000000000ffffffffffffffffffffffffffffff167cffffffffffffffffffffffffffff000000000000000000000000000000607888901b1617905590518481527f95f1a495c6d9d1a215995408f6ad898c2b33798d6eb3cf3220b4c6cc8b61b0649101610bd6565b6000806000806121c18989898989336130a5565b929c919b50995090975095505050505050565b6121dc6123bb565b61ffff8116811461222f5760405162461bcd60e51b815260206004820152601a60248201527f6d67762f636f6e6669672f67617370726963652f3136626974730000000000006044820152606401610b3e565b6001547fffffffffffffffffffffffffffffffffffffffffffff0000ffffffffffffffff1669ffff0000000000000000604083901b16176001556040518181527fdbebd814ae648f654dcc50c734aa76e55a32e96b7d85303a08e2ddf11874a0dd90602001610fbe565b60401b69ffff0000000000000000167fffffffffffffffffffffffffffffffffffffffffffff0000ffffffffffffffff919091161790565b60281b67ffffff0000000000167fffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffff919091161790565b602081901c60ff161561235c5760405162461bcd60e51b815260206004820152600860248201527f6d67762f646561640000000000000000000000000000000000000000000000006044820152606401610b3e565b50565b6001600160a01b03821660008181526006602052604090819020805484019055517f1bbf55d483639f8103dc4e035af71a4fbdb16c80be740fa3eef81198acefa094906123af9084815260200190565b60405180910390a25050565b6003546001600160a01b03163314806123d357503330145b806123e757506003546001600160a01b0316155b611d1e5760405162461bcd60e51b815260206004820152601060248201527f6d67762f756e617574686f72697a6564000000000000000000000000000000006044820152606401610b3e565b6001600160a01b0382166000908152600660205260409020548181101561249c5760405162461bcd60e51b815260206004820152601960248201527f6d67762f696e73756666696369656e7450726f766973696f6e000000000000006044820152606401610b3e565b6001600160a01b038316600081815260066020526040908190208484039055517f59c79d79be0fadf59fe689b6952b7ebe90201a3a1f00d4a31982377890bc6046906124eb9085815260200190565b60405180910390a2505050565b600080600080600061256160405180610120016040528060006001600160a01b0316815260200160006001600160a01b03168152602001600081526020016000815260200160008152602001600081526020016000815260200160008152602001600081525090565b6001600160a01b03808d1682528b16602082015261257f8c8c611d20565b8260e0018361010001828152508281525050506125e5604051806101000160405280600081526020016000815260200160008152602001600081526020016000815260200160006001600160a01b03168152602001600015158152602001600081525090565b6001600160a01b03881660a082015288151560c082015260e08201516101008301516126119190612746565b61261f8261010001516126f1565b6040517fbc01c241b6c13c2ee496b5103c73ba7c02e7d4bbc77ee637862cc2b152f068b990600090a161265481838d8d613bc8565b6080840151929950909750955061266a906140c8565b602080830151835160808085015160e0860151604080518d81529687018c905286019190915260608501526001600160a01b03808d1694938116939216917ff906db58b0529ffcfdb9bf545bfdbace7ca86906a716bfd1b65f62ff07cdec47910160405180910390a4806080015193508060e0015192505050965096509650965096915050565b605881901c60ff161561235c5760405162461bcd60e51b815260206004820152601460248201527f6d67762f7265656e7472616e63794c6f636b65640000000000000000000000006044820152606401610b3e565b61274f82612307565b61275b8160f81c151590565b610a105760405162461bcd60e51b815260206004820152600c60248201527f6d67762f696e61637469766500000000000000000000000000000000000000006044820152606401610b3e565b60c082015161ffff8116146127fe5760405162461bcd60e51b815260206004820152601e60248201527f6d67762f77726974654f666665722f67617370726963652f31366269747300006044820152606401610b3e565b61010082015160401c61ffff168260c0015110156128295761010082015160401c61ffff1660c08301525b61010082015160281c62ffffff168260a00151111561288a5760405162461bcd60e51b815260206004820152601d60248201527f6d67762f77726974654f666665722f6761737265712f746f6f486967680000006044820152606401610b3e565b60008260600151116128de5760405162461bcd60e51b815260206004820152601b60248201527f6d67762f77726974654f666665722f67697665732f746f6f4c6f7700000000006044820152606401610b3e565b61012082015160781c6dffffffffffffffffffffffffffff1661012083015160601c62ffffff168360a0015101028260600151101561295f5760405162461bcd60e51b815260206004820152601d60248201527f6d67762f77726974654f666665722f64656e736974792f746f6f4c6f770000006044820152606401610b3e565b60608201516bffffffffffffffffffffffff8116146129c05760405162461bcd60e51b815260206004820152601b60248201527f6d67762f77726974654f666665722f67697665732f39366269747300000000006044820152606401610b3e565b60408201516bffffffffffffffffffffffff811614612a215760405162461bcd60e51b815260206004820152601b60248201527f6d67762f77726974654f666665722f77616e74732f39366269747300000000006044820152606401610b3e565b600080612a2d84614166565b9150915083602001516001600160a01b031684600001516001600160a01b03167f2b101f9ef8ca2761d91bea55ab50a4d051e9248088ff7314ddb8826839544c2a33876040015188606001518960c001518a60a001518b608001518a604051612ad297969594939291906001600160a01b03979097168752602087019590955260408601939093526060850191909152608084015260a083015260c082015260e00190565b60405180910390a383516001600160a01b039081166000908152600560209081526040808320828901519094168352928152828220608088015183529052908120548415612bac57612b248160601c90565b6001600160a01b0316336001600160a01b031614612b845760405162461bcd60e51b815260206004820152601c60248201527f6d67762f7570646174654f666665722f756e617574686f72697a6564000000006044820152606401610b3e565b603081901c62ffffff16604882901c62ffffff1601602082901c61ffff16633b9aca00020291505b841580612bc6575060a0860151604882901c62ffffff1614155b80612bdd575060c0860151602082901c61ffff1614155b80612bfe575061012086015160601c62ffffff16603082901c62ffffff1614155b15612cc05761012086015160009060601c62ffffff169050612c86338860a00151838a60c0015160201b65ffff000000001660309190911b68ffffff0000000000001660489290921b6bffffff0000000000000000001660609390931b7fffffffffffffffffffffffffffffffffffffffff0000000000000000000000001692909217171790565b87516001600160a01b039081166000908152600560209081526040808320828d0151909416835292815282822060808c0151835290522055505b5060c08501516101208601516000919060601c62ffffff168760a001510102633b9aca0002905081811115612d0057612cfb33838303612433565b612d14565b81811015612d1457612d143382840361235f565b506101408501516bffffffffffffffffffffffff161580612d3d575061014085015160e01c8314155b15612f44578215612e0857608085015185516001600160a01b039081166000908152600460209081526040808320828b0151909416835292815282822087835290522054612dcf9160c01b7bffffffff000000000000000000000000000000000000000000000000167fffffffff00000000ffffffffffffffffffffffffffffffffffffffffffffffff919091161790565b85516001600160a01b039081166000908152600460209081526040808320828b0151909416835292815282822087835290522055612e57565b6080850151610120860151612e509160381b6affffffff00000000000000167fffffffffffffffffffffffffffffffffffffffffff00000000ffffffffffffff919091161790565b6101208601525b8115612ee357608085015185516001600160a01b039081166000908152600460208181526040808420828c018051871686529083528185208986528352818520548c518716865293835281852090519095168452938152838320878452905291902060e09290921b7bffffffffffffffffffffffffffffffffffffffffffffffffffffffff9091161790555b838015612f0257506101408501516bffffffffffffffffffffffff1615155b15612f4457612f3d85600001518660200151612f2388610140015160e01c90565b61014089015160c01c63ffffffff16896101200151613a00565b6101208601525b60408501516060808701516000926bffffffffffffffffffffffff909116911b77ffffffffffffffffffffffff0000000000000000000000001660c085901b7bffffffff0000000000000000000000000000000000000000000000001660e087901b17171786516001600160a01b039081166000908152600460209081526040808320828c015190941683529281528282206080909a0151825298909852909620959095555050505050565b6130346040518060e0016040528060001515815260200160008152602001600081526020016000815260200160001515815260200160008152602001600081525090565b60f882901c1515815260e882901c61ffff166020820152607882901c6dffffffffffffffffffffffffffff166040820152606082811c62ffffff1690820152605882901c60ff1615156080820152603882901c63ffffffff90811660a083015260189290921c90911660c082015290565b60008060008087886001600160a01b0316146131035760405162461bcd60e51b815260206004820152601d60248201527f6d67762f6d4f726465722f74616b657257616e74732f313630626974730000006044820152606401610b3e565b86876001600160a01b03161461315b5760405162461bcd60e51b815260206004820152601d60248201527f6d67762f6d4f726465722f74616b657247697665732f313630626974730000006044820152606401610b3e565b6131bc60405180610120016040528060006001600160a01b0316815260200160006001600160a01b03168152602001600081526020016000815260200160008152602001600081526020016000815260200160008152602001600081525090565b6001600160a01b03808c1682528a1660208201526131da8b8b611d20565b610100830181905260e083019190915260381c63ffffffff1660408281019182526001600160a01b038d81166000908152600460209081528382208f841683528152838220945182529384528281205460608087019190915260808087018f905260a08088018f90528551610100808201885296810185905292830184905290820183905260e0808301939093528e82529481018d9052918a169382019390935289151560c082015291830151908301516132959190612746565b6132a38261010001516126f1565b6101008201517fffffffffffffffffffffffffffffffffffffffff00ffffffffffffffffffffff166b0100000000000000000000001782610100018181525050816101000151600260008e6001600160a01b03166001600160a01b0316815260200190815260200160002060008d6001600160a01b03166001600160a01b03168152602001908152602001600020819055507fbc01c241b6c13c2ee496b5103c73ba7c02e7d4bbc77ee637862cc2b152f068b960405160405180910390a161336d81836001614397565b61337a81608001516140c8565b866001600160a01b03168b6001600160a01b03168d6001600160a01b03167ff906db58b0529ffcfdb9bf545bfdbace7ca86906a716bfd1b65f62ff07cdec478460400151856060015186608001518760e001516040516133f3949392919093845260208401929092526040830152606082015260800190565b60405180910390a460408101516060820151608083015160e090930151919e909d50919b509950975050505050505050565b6001600160a01b03808516600090815260076020908152604080832087851684528252808320938616835292815282822033835290522054818110156134ad5760405162461bcd60e51b815260206004820152601060248201527f6d67762f6c6f77416c6c6f77616e6365000000000000000000000000000000006044820152606401610b3e565b6001600160a01b038581166000818152600760209081526040808320898616808552908352818420958916808552958352818420338086529084529382902088880390819055825196875292860193909352840152917f6e791f456e6f5034d4928b936c32bf7e650d8228543ce7985d632ab03934837d9060600160405180910390a35050505050565b6135626040518060800160405280600081526020016000815260200160008152602001600081525090565b60e082901c815263ffffffff60c083901c1660208201526bffffffffffffffffffffffff606083811c8216604084015292169181019190915290565b6135d2604051806080016040528060006001600160a01b031681526020016000815260200160008152602001600081525090565b606082811c8252604883901c62ffffff908116602080850191909152603085901c90911660408401529290921c61ffff169181019190915290565b604080516001600160a01b0385811660248301528481166044830152606480830185905283518084039091018152608490920183526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f23b872dd000000000000000000000000000000000000000000000000000000001790529151600092839182918916906136a1908590615795565b6000604051808303816000865af19150503d80600081146136de576040519150601f19603f3d011682016040523d82523d6000602084013e6136e3565b606091505b509150915081801561370d57508051158061370d57508080602001905181019061370d91906157d0565b98975050505050505050565b604080516001600160a01b038481166024830152604480830185905283518084039091018152606490920183526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fa9059cbb000000000000000000000000000000000000000000000000000000001790529151600092839182918816906137a5908590615795565b6000604051808303816000865af19150503d80600081146137e2576040519150601f19603f3d011682016040523d82523d6000602084013e6137e7565b606091505b509150915081801561381157508051158061381157508080602001905181019061381191906157d0565b93505050505b9392505050565b600080636c49c32c60e01b8360405160240161383a91906157ed565b604080518083037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe00181529190526020810180517fffffffff00000000000000000000000000000000000000000000000000000000939093167bffffffffffffffffffffffffffffffffffffffffffffffffffffffff90931692909217909152905060c0830135604881901c62ffffff169060601c60005a905082604082048203101561392757604080516060810182527f6d67762f6e6f74456e6f756768476173466f724d616b6572547261646500000081526000602082018190529181019190915261392790606081fd5b600080613935848688614638565b915091505a83039650816139895761398960405180606001604052807f6d67762f6d616b6572526576657274000000000000000000000000000000000081526020018960001b815260200183815250606081fd5b60006139a761399b60208b018b61523a565b86308c6080013561360d565b9050806139f4576139f460405180606001604052807f6d67762f6d616b65725472616e736665724661696c000000000000000000000081526020018a60001b815260200184815250606081fd5b50505050505050919050565b60008315613a84576001600160a01b038681166000908152600460209081526040808320938916835292815282822087835290522080547fffffffff00000000ffffffffffffffffffffffffffffffffffffffffffffffff167bffffffff00000000000000000000000000000000000000000000000060c086901b16179055613abd565b7fffffffffffffffffffffffffffffffffffffffffff00000000ffffffffffffff82166affffffff00000000000000603885901b161791505b8215613b19576001600160a01b038681166000908152600460209081526040808320938916835292815282822086835290522080547bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1660e086901b1790555b50949350505050565b7fffffffffffffffffffffffffffffffffffffffff000000000000000000000000831692508015613b73577fffffffffffffffffffffffffffffffffffffffffffffffffffff0000ffffffff821691505b506001600160a01b039485166000818152600460209081526040808320979098168083529681528782208683528152878220949094559081526005835285812094815293825284842092845291905291902055565b60008080805b848110156140bd576000604089018190526060890152858582818110613bf657613bf6615898565b905060800201600060048110613c0e57613c0e615898565b60209081029190910135604089810191825289516001600160a01b039081166000908152600485528281208c86018051841683529086528382208551835286528382205460608e019081528d518416835260058752848320915190931682528552828120935181529290935290205460c0890152516bffffffffffffffffffffffff161580613cdf5750858582818110613caa57613caa615898565b905060800201600360048110613cc257613cc2615898565b6020020135613cdd8860c0015162ffffff60489190911c1690565b115b15613ce9576140b5565b858582818110613cfb57613cfb615898565b905060800201600160048110613d1357613d13615898565b6020020135868683818110613d2a57613d2a615898565b905060800201600160048110613d4257613d42615898565b60200201356bffffffffffffffffffffffff1614613da25760405162461bcd60e51b815260206004820152601c60248201527f6d67762f736e697065732f74616b657257616e74732f393662697473000000006044820152606401610b3e565b858582818110613db457613db4615898565b905060800201600260048110613dcc57613dcc615898565b6020020135868683818110613de357613de3615898565b905060800201600260048110613dfb57613dfb615898565b60200201356bffffffffffffffffffffffff1614613e5b5760405162461bcd60e51b815260206004820152601c60248201527f6d67762f736e697065732f74616b657247697665732f393662697473000000006044820152606401610b3e565b858582818110613e6d57613e6d615898565b905060800201600160048110613e8557613e85615898565b60200201356080880152858582818110613ea157613ea1615898565b905060800201600260048110613eb957613eb9615898565b602002013560a08801526101008701517fffffffffffffffffffffffffffffffffffffffff00ffffffffffffffffffffff166b01000000000000000000000017610100880181905287516001600160a01b039081166000908152600260209081526040808320828d01519094168352929052908120919091558080613f3e8b8b614666565b925092509250807f6d67762f747261646553756363657373000000000000000000000000000000001415613f73576001870196505b807f6d67762f6e6f744578656375746564000000000000000000000000000000000014613fd557613fce8a600001518b60200151613fb58d6060015160e01c90565b60608e015160c01c63ffffffff168e6101000151613a00565b6101008b01525b6101008a01517fffffffffffffffffffffffffffffffffffffffff00ffffffffffffffffffffff168a610100018181525050896101000151600260008c600001516001600160a01b03166001600160a01b0316815260200190815260200160002060008c602001516001600160a01b03166001600160a01b031681526020019081526020016000208190555061406b8b8b614d53565b807f6d67762f6e6f74457865637574656400000000000000000000000000000000001461409f5761409f8b8b858585614e67565b8a60400151860195508a60600151850194505050505b600101613bce565b509450945094915050565b801561235c57604051600090339083908381818185875af1925050503d8060008114614110576040519150601f19603f3d011682016040523d82523d6000602084013e614115565b606091505b5050905080610a105760405162461bcd60e51b815260206004820152601760248201527f6d67762f73656e6450656e616c747952657665727465640000000000000000006044820152606401610b3e565b60008060008060008560e0015190506000866080015182146141bb5786516001600160a01b039081166000908152600460209081526040808320828c01519094168352928152828220858352905220546141c2565b8661014001515b90506bffffffffffffffffffffffff81166142205761012087015160381c63ffffffff1687516001600160a01b039081166000908152600460209081526040808320828d015190941683529281528282208483529052205490925090505b61422b878284614ed7565b156142c55760005b60c082901c63ffffffff16156142ad575086516001600160a01b039081166000908152600460209081526040808320828c0151909416835292815282822063ffffffff60c086901c1680845291529190205490614291898383614ed7565b156142a1578093508192506142a7565b506142ad565b50614233565b8260c083901c63ffffffff16909550935061434e9050565b60005b6142d28260e01c90565b1561433b5760006142e38360e01c90565b89516001600160a01b039081166000908152600460209081526040808320828f015190941683529281528282208483529052205492509050614326898383614ed7565b15614331575061433b565b92509050806142c8565b6143458260e01c90565b94509192508291505b8660800151841461435f5783614369565b61014087015160e01c5b8760800151841461437a578361438a565b61014088015160c01c63ffffffff165b9550955050505050915091565b8080156143be57508260c001516143b55760008260a00151116143be565b60008260800151115b80156143ce575060008260400151115b156145b45781516001600160a01b03908116600090815260056020908152604080832082870151909416835292815282822083860151835290529081205460c0840152808061441d8686614666565b608088015160a089015160408a015160608b015160c08c015196995094975092955090939092907f6d67762f6e6f744578656375746564000000000000000000000000000000000086146145225760408b01518b511161447e576000614487565b60408b01518b51035b60808b015260608b81015160208d01510360a08c01528a015160c01c63ffffffff168a6040018181525050600460008b600001516001600160a01b03166001600160a01b0316815260200190815260200160002060008b602001516001600160a01b03166001600160a01b0316815260200190815260200160002060008b604001518152602001908152602001600020548a60600181815250505b6145508b8b887f6d67762f6e6f74457865637574656400000000000000000000000000000000001415614397565b60408a0183905260808a0185905260a08a0184905260608a0182905260c08a018190527f6d67762f6e6f744578656375746564000000000000000000000000000000000086146145a7576145a78b8b8a8a8a614e67565b5050505050505050505050565b6145d38260000151836020015160008560400151866101000151613a00565b7fffffffffffffffffffffffffffffffffffffffff00ffffffffffffffffffffff16610100830181905282516001600160a01b03908116600090815260026020908152604080832082880151909416835292905220556146338383614d53565b505050565b6000806146436151b3565b60648185516020870160008a8af192508060448415020151915050935093915050565b60008060008061468b85606001516bffffffffffffffffffffffff60609190911c1690565b905060006146a886606001516bffffffffffffffffffffffff1690565b608087015160a08801519192509080830284830211156146f65750600095508594507f6d67762f6e6f74457865637574656400000000000000000000000000000000009350614d4c92505050565b8860c00151801561470657508183105b8061471d57508860c0015115801561471d57508084105b15614735576080880183905260a088018490526147a9565b8860c00151156147805783820283818161475157614751615869565b061561475e576001614761565b60005b60ff1684828161477357614773615869565b040160a08a0152506147a9565b8361479157608088018390526147a9565b83818402816147a2576147a2615869565b0460808901525b50505050600080306001600160a01b031663aa4dd28260e01b878960a001516040516024016147d99291906158c7565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff000000000000000000000000000000000000000000000000000000009094169390931790925290516148629190615795565b6000604051808303816000865af19150503d806000811461489f576040519150601f19603f3d011682016040523d82523d6000602084013e6148a4565b606091505b50915091508115614a2457808060200190518101906148c3919061594f565b94507f6d67762f74726164655375636365737300000000000000000000000000000000925085602001516001600160a01b031686600001516001600160a01b03167f1d81940ff2c3ac549c8acefd7c1b2ffb24aacc8b3cfa309c22abc3de79727f9d88604001518a60a001518a608001518b60a0015160405161496894939291909384526001600160a01b039290921660208401526040830152606082015260800190565b60405180910390a360e086015160501c60ff16156149ff5760e086015160a08801516040517fcac1827100000000000000000000000000000000000000000000000000000000815260609290921c9163cac18271916149cc918a91906004016158c7565b600060405180830381600087803b1580156149e657600080fd5b505af11580156149fa573d6000803e3d6000fd5b505050505b6080860151604088018051909101905260a08601516060880180519091019052614d04565b602081015160408201516060830151909650945092507f6d67762f6d616b65725265766572740000000000000000000000000000000000831480614a875750827f6d67762f6d616b65725472616e736665724661696c0000000000000000000000145b80614ab15750827f6d67762f6d616b6572526563656976654661696c000000000000000000000000145b15614bdc5785602001516001600160a01b031686600001516001600160a01b03167f1d59c048edb5397821854bd12cbe02242b82101c677f949d076d14eb7bd8343688604001518a60a001518a608001518b60a0015189604051614b409594939291909485526001600160a01b0393909316602085015260408401919091526060830152608082015260a00190565b60405180910390a360e086015160501c60ff1615614bd75760e086015160a08801516040517f1159507300000000000000000000000000000000000000000000000000000000815260609290921c91631159507391614ba4918a91906004016158c7565b600060405180830381600087803b158015614bbe57600080fd5b505af1158015614bd2573d6000803e3d6000fd5b505050505b614d04565b827f6d67762f6e6f74456e6f756768476173466f724d616b657254726164650000001415614c4c5760405162461bcd60e51b815260206004820152601d60248201527f6d67762f6e6f74456e6f756768476173466f724d616b657254726164650000006044820152606401610b3e565b827f6d67762f74616b65725472616e736665724661696c00000000000000000000001415614cbc5760405162461bcd60e51b815260206004820152601560248201527f6d67762f74616b65725472616e736665724661696c00000000000000000000006044820152606401610b3e565b60405162461bcd60e51b815260206004820152600d60248201527f6d67762f737761704572726f72000000000000000000000000000000000000006044820152606401610b3e565b614d4986600001518760200151886040015189606001518a60c00151887f6d67762f747261646553756363657373000000000000000000000000000000001415613b22565b50505b9250925092565b6101008101516000906127109060e81c61ffff1684604001510281614d7a57614d7a615869565b0490508015614dfa57604083018051829003905260e083018190528151600054614dae91906001600160a01b031683613719565b614dfa5760405162461bcd60e51b815260206004820152601360248201527f6d67762f6665655472616e736665724661696c000000000000000000000000006044820152606401610b3e565b60408301511561463357614e1b82600001518460a001518560400151613719565b6146335760405162461bcd60e51b815260206004820152601560248201527f6d67762f4d67764661696c546f50617954616b657200000000000000000000006044820152606401610b3e565b60c084015160481c62ffffff1680841115614e80578093505b614e8e858583038585614f80565b84019350817f6d67762f7472616465537563636573730000000000000000000000000000000014614ecf57614ec38585615121565b60808701805190910190525b505050505050565b600081614ee657506001613817565b60408401516060858101516bffffffffffffffffffffffff9186901c821692918616919080840282840280821415614f735789516001600160a01b039081166000908152600560209081526040808320828f015190941683529281528282208b835290529081205460481c62ffffff1660a08c015194029590930294909410159550613817945050505050565b1194506138179350505050565b600080633d3d130d60e01b86604051806040016040528087815260200186815250604051602401614fb2929190615968565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff00000000000000000000000000000000000000000000000000000000939093169290921790915260c087015190915060601c60005a90508660408204820310156150955760405162461bcd60e51b815260206004820181905260248201527f6d67762f6e6f74456e6f756768476173466f724d616b6572506f7374686f6f6b6044820152606401610b3e565b6000806150a3848a87614638565b915091505a83039550816151145789602001516001600160a01b03168a600001516001600160a01b03167f51d5a3ecf332626d83c8207fe9bd22f38801f578c7eb222e2d811cd1b4c629a58c604001518460405161510b929190918252602082015260400190565b60405180910390a35b5050505050949350505050565b60c082015160009062ffffff604882901c81169161ffff602082901c1660309190911c909116820102633b9aca00028184111561515c578193505b61010085015160009060601c62ffffff1660e08701519086019060401c61ffff16633b9aca0002029050818111156151915750805b6151aa6151a28760c0015160601c90565b82840361235f565b95945050505050565b60405180608001604052806004906020820280368337509192915050565b80356001600160a01b03811681146151e857600080fd5b919050565b600080600080600060a0868803121561520557600080fd5b61520e866151d1565b945061521c602087016151d1565b94979496505050506040830135926060810135926080909101359150565b60006020828403121561524c57600080fd5b613817826151d1565b60006020828403121561526757600080fd5b5035919050565b60008060006060848603121561528357600080fd5b61528c846151d1565b925061529a602085016151d1565b9150604084013590509250925092565b60008083601f8401126152bc57600080fd5b50813567ffffffffffffffff8111156152d457600080fd5b6020830191508360208260071b8501011115611ea557600080fd5b801515811461235c57600080fd5b60008060008060006080868803121561531557600080fd5b61531e866151d1565b945061532c602087016151d1565b9350604086013567ffffffffffffffff81111561534857600080fd5b615354888289016152aa565b9094509250506060860135615368816152ef565b809150509295509295909350565b600080600080600080600060e0888a03121561539157600080fd5b61539a886151d1565b96506153a8602089016151d1565b96999698505050506040850135946060810135946080820135945060a0820135935060c0909101359150565b600080600080608085870312156153ea57600080fd5b6153f3856151d1565b9350615401602086016151d1565b925061540f604086016151d1565b9396929550929360600135925050565b60006020828403121561543157600080fd5b8135613817816152ef565b6000806040838503121561544f57600080fd5b615458836151d1565b9150615466602084016151d1565b90509250929050565b600080600080600080600080610100898b03121561548c57600080fd5b615495896151d1565b97506154a360208a016151d1565b979a9799505050506040860135956060810135956080820135955060a0820135945060c0820135935060e0909101359150565b60008060008060008060c087890312156154ef57600080fd5b6154f8876151d1565b9550615506602088016151d1565b945060408701359350606087013592506080870135615524816152ef565b915061553260a088016151d1565b90509295509295509295565b60008082840361014081121561555357600080fd5b6101208082121561556357600080fd5b84935061113e8186016151d1565b6000806000806080858703121561558757600080fd5b615590856151d1565b935061559e602086016151d1565b92506040850135915060608501356155b5816152ef565b939692955090935050565b60008060008060008060008060006101208a8c0312156155df57600080fd5b6155e88a6151d1565b98506155f660208b016151d1565b975061560460408b016151d1565b965061561260608b016151d1565b955060808a0135945060a08a0135935060c08a013560ff8116811461563657600080fd5b8093505060e08a013591506101008a013590509295985092959850929598565b60008060008060008060a0878903121561566f57600080fd5b615678876151d1565b9550615686602088016151d1565b9450604087013567ffffffffffffffff8111156156a257600080fd5b6156ae89828a016152aa565b90955093505060608701356156c2816152ef565b9150615532608088016151d1565b600080600080608085870312156156e657600080fd5b6156ef856151d1565b93506156fd602086016151d1565b925061570b604086016151d1565b9150615719606086016151d1565b905092959194509250565b600080600080600060a0868803121561573c57600080fd5b615745866151d1565b9450615753602087016151d1565b935060408601359250606086013591506080860135615368816152ef565b6000806040838503121561578457600080fd5b505080516020909101519092909150565b6000825160005b818110156157b6576020818601810151858301520161579c565b818111156157c5576000828501525b509190910192915050565b6000602082840312156157e257600080fd5b8151613817816152ef565b61012081016001600160a01b0380615804856151d1565b16835280615814602086016151d1565b1660208401525060408301356040830152606083013560608301526080830135608083015260a083013560a083015260c083013560c083015260e083013560e083015261010080840135818401525092915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b610140810161593882856001600160a01b038082511683528060208301511660208401525060408101516040830152606081015160608301526080810151608083015260a081015160a083015260c081015160c083015260e081015160e08301526101008082015181840152505050565b6001600160a01b0383166101208301529392505050565b60006020828403121561596157600080fd5b5051919050565b61016081016159d982856001600160a01b038082511683528060208301511660208401525060408101516040830152606081015160608301526080810151608083015260a081015160a083015260c081015160c083015260e081015160e08301526101008082015181840152505050565b82516101208301526020830151610140830152939250505056fea2646970667358221220456bed48860bc84bb92cd2249b83edea27d1fd756bce6bf3b05e81a7e3a50e3464736f6c634300080a003300000000000000000000000047897ee61498d02b18794601ed3a71896a1ff894000000000000000000000000000000000000000000000000000000000000002800000000000000000000000000000000000000000000000000000000000aae60

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

00000000000000000000000047897ee61498d02b18794601ed3a71896a1ff894000000000000000000000000000000000000000000000000000000000000002800000000000000000000000000000000000000000000000000000000000aae60

-----Decoded View---------------
Arg [0] : governance (address): 0x47897ee61498d02b18794601ed3a71896a1ff894
Arg [1] : gasprice (uint256): 40
Arg [2] : gasmax (uint256): 700000

-----Encoded View---------------
3 Constructor Arguments found :
Arg [0] : 00000000000000000000000047897ee61498d02b18794601ed3a71896a1ff894
Arg [1] : 0000000000000000000000000000000000000000000000000000000000000028
Arg [2] : 00000000000000000000000000000000000000000000000000000000000aae60


Block Transaction Gas Used Reward
Age Block Fee Address BC Fee Address Voting Power Jailed Incoming
Block Uncle Number Difficulty Gas Used Reward
Loading