Blockchain / Global / Address

Contract

0xdC76648d77BA56b6CBC823A56567e450F1671fFf

Balance
0.1 SOMI
Transactions sent
2
Type
Contract · 16,892 bytes of code

MangroveOrder Verified

Compiler
solc 0.8.20+commit.a1b79de6
Language
Solidity
Optimization
Yes, 200 runs
EVM version
paris
License
—
Verified
2026-10-12

src/strategies/MangroveOrder.sol

and 34 more files

Solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.18;

import {IMangrove} from "@mgv/src/IMangrove.sol";
import {
  RenegingForwarder,
  MangroveOffer,
  Tick,
  RouterProxyFactory,
  RouterProxy
} from "@mgv-strats/src/strategies/offer_forwarder/RenegingForwarder.sol";
import {TransferLib, RL} from "@mgv-strats/src/strategies/MangroveOffer.sol";
import {IOrderLogic} from "@mgv-strats/src/strategies/interfaces/IOrderLogic.sol";
import {SmartRouter, AbstractRoutingLogic} from "@mgv-strats/src/strategies/routers/SmartRouter.sol";

import {MgvLib, IERC20, OLKey} from "@mgv/src/core/MgvLib.sol";

///@title MangroveOrder. A periphery contract to Mangrove protocol that implements "Good Til Cancel" orders, "Good Til Cancel Enforced" orders, "Post Only" orders, "Immediate Or Cancel" orders, and "Fill Or Kill" orders,
///@notice A GTC order is a buy (sell) limit order complemented by a bid (ask) limit order, called a resting order, that occurs when the buy (sell) order was partially filled.
/// If the GTC is for some amount $a_goal$ at a price $p$, and the corresponding limit order was partially filled for $a_now < a_goal$,
/// the resting order should be posted for an amount $a_later = a_goal - a_now$ at price $p$.
/// If the resting order fails to be posted, the transaction wont fail.
///@notice A GTCE order mimics a GTC order, but shall the resting order fail to be posted, the transaction will revert.
///@notice A PO order is a buy (sell) limit order that is only posted and is not ran against the market.
///@notice An IOC order is a buy (sell) limit order that is ran against the market for a partial or complete fill and no resting order will be posted.
///@notice A FOK order is simply a buy or sell limit order that is either completely filled or cancelled. No resting order is posted.
contract MangroveOrder is RenegingForwarder, IOrderLogic {
  ///@notice MangroveOrder is a Forwarder logic with a smart router.
  ///@param mgv The mangrove contract on which this logic will run taker and maker orders.
  ///@param factory the router proxy factory used to deploy or retrieve user routers
  ///@param deployer The address of the admin of `this` at the end of deployment
  constructor(IMangrove mgv, RouterProxyFactory factory, address deployer)
    RenegingForwarder(mgv, factory, _deploySmartRouter())
  {
    _setAdmin(deployer);
  }

  /// @notice Deploys a new RouterProxy for a given owner.
  /// @return The deployed SmartRouter
  function _deploySmartRouter() internal virtual returns (SmartRouter) {
    return new SmartRouter(address(this));
  }

  ///@notice compares a taker order with a market order result and checks whether the order was entirely filled
  ///@param tko the taker order
  ///@param res the market order result
  ///@return true if the order was entirely filled, false otherwise.
  function checkCompleteness(TakerOrder calldata tko, TakerOrderResult memory res) internal pure returns (bool) {
    // The order can be incomplete if the price becomes too high or the end of the book is reached.
    if (tko.fillWants) {
      // when fillWants is true, the market order stops when `fillVolume` units of `outbound_tkn` have been obtained (minus potential fees);
      return res.takerGot + res.fee >= tko.fillVolume;
    } else {
      // otherwise, the market order stops when `fillVolume` units of `tko.olKey.inbound_tkn` have been sold.
      return res.takerGave >= tko.fillVolume;
    }
  }

  ///@inheritdoc IOrderLogic
  function take(TakerOrder calldata tko) external payable returns (TakerOrderResult memory res) {
    // Checking whether order is expired
    require(tko.expiryDate == 0 || block.timestamp < tko.expiryDate, "mgvOrder/expired");
    (RouterProxy proxy,) = ROUTER_FACTORY.instantiate(msg.sender, ROUTER_IMPLEMENTATION);
    SmartRouter userRouter = SmartRouter(address(proxy));

    // Notations:
    // NAT_USER: initial value of `msg.sender.balance` (native balance of user)
    // OUT/IN_USER: initial value of `userRouter.tokenBalanceOf({token: tko.[out|in]bound_tkn, fundOwner:msg.sender})
    // NAT_THIS: initial value of `address(this).balance` (native balance of `this`)
    // OUT/IN_THIS: initial value of `tko.[out|in]bound_tkn.balanceOf(address(this))` (`this` balance of tokens)

    logOrderData(tko); // for indexing
    bool isComplete; // stores whether market order is filled
    // this will check if this isn't a post only offer for the market order
    if (tko.orderType.executesMarketOrder()) {
      // State before market order:
      // * User balances: (NAT_USER -`msg.value`, OUT_USER, IN_USER)
      // * `this` balances: (NAT_THIS +`msg.value`, OUT_THIS, IN_THIS)
      (res, isComplete) = executeMarketOrder(tko, userRouter);
      // State after market order:
      // * (NAT_USER-`msg.value`, OUT_USER+`res.takerGot`, IN_USER-`res.takerGave`)
      // * (NAT_THIS+`msg.value`+`res.bounty`, OUT_THIS, IN_THIS)

      // NB: for post only orders, we will keep the same semantic for the state of balances
      // However in the case of post only, since `res.bounty` == `res.takerGot` == `res.takerGave` == 0,
      // the state of balances can be annotated the same as before the market order.
    }

    ///@dev collected bounty compensates gas consumption for the failed offer, but could be lower than the cost of an additional native token transfer
    /// instead of sending the bounty back to `msg.sender` we recycle it into the resting order's provision (so `msg.sender` can retrieve it when deprovisioning).
    /// corner case: if the bounty is large enough, this will make posting of the resting order fail because of `gasprice` overflow.
    /// The funds would then be sent back to `msg.sender` (see below).
    uint fund = msg.value + res.bounty;

    if ( // resting order is:
      tko.orderType.postRestingOrder(isComplete) // required
    ) {
      // When posting a resting order `msg.sender` (taker) becomes a maker.
      // For maker orders, outbound tokens are what makers send. Here `msg.sender` sends `tko.olKey.inbound_tkn`.
      // The offer list on which this contract must post `msg.sender`'s resting order is thus `tko.olKey.flipped()`
      // the call below will continue filling the memory data `res`.
      fund = postRestingOrder({tko: tko, olKey: tko.olKey.flipped(), res: res, userRouter: userRouter, fund: fund});
      // POST (case `postRestingOrder` succeeded):
      // * (NAT_USER-`msg.value`, OUT_USER+`res.takerGot`, IN_USER-`res.takerGave`)
      // * (NAT_THIS, OUT_THIS, IN_THIS)
      // * `fund == 0`
      // * `ownerData[tko.olKey.flipped().hash()][res.offerId].owner == msg.sender`.
      // * Mangrove emitted an `OfferWrite` log whose `maker` field is `address(this)` and `offerId` is `res.offerId`.

      // POST (case `postRestingOrder` failed):
      // * (NAT_USER-`msg.value`, OUT_USER+`res.takerGot`, IN_USER-`res.takerGave`)
      // * (NAT_THIS+`msg.value`+`res.bounty`, OUT_THIS, IN_THIS)
      // * `fund == msg.value + res.bounty`.
      // * `res.offerId == 0`
    }

    if (fund > 0) {
      // NB this calls gives reentrancy power to callee, but OK since:
      // 1. callee is `msg.sender` so no griefing risk of making this call fail for out of gas
      // 2. w.r.t reentrancy for profit:
      // * from POST above a reentrant call would entail either:
      //   - `fund == 0` (no additional funds transferred)
      //   - or `fund == msg.value + res.bounty` with `msg.value` being from reentrant call and `res.bounty` from a second resting order.
      // Thus no additional fund can be redeemed by caller using reentrancy.
      (bool noRevert,) = msg.sender.call{value: fund}("");
      require(noRevert, "mgvOrder/refundFail");
    }
    // POST (case `postRestingOrder` succeeded)
    // * (NAT_USER, OUT_USER+`res.takerGot`, IN_USER-`res.takerGave`)
    // * (NAT_THIS, OUT_THIS, IN_THIS)
    // POST (else)
    // * (NAT_USER+`res.bounty`, OUT_USER+`res.takerGot`, IN_USER-`res.takerGave`)
    // * (NAT_THIS, OUT_THIS, IN_THIS)
    emit MangroveOrderComplete();
    return res;
  }

  ///@notice logs `MangroveOrderStart`
  ///@param tko the arguments in memory of the taker order
  ///@dev this function avoids loading too many variables on the stack
  function logOrderData(TakerOrder memory tko) internal {
    emit MangroveOrderStart({
      olKeyHash: tko.olKey.hash(),
      taker: msg.sender,
      tick: tko.tick,
      orderType: tko.orderType,
      fillVolume: tko.fillVolume,
      fillWants: tko.fillWants,
      offerId: tko.offerId,
      takerGivesLogic: tko.takerGivesLogic,
      takerWantsLogic: tko.takerWantsLogic
    });
  }

  /// @notice Executes a market order on Mangrove according to the taker order.
  /// @param tko arguments in memory of the taker order
  /// @param userRouter the user router
  /// @return res the result of the market order
  /// @return isComplete true if the order was entirely filled, false otherwise.
  /// @dev tko.orderType must not be PO
  function executeMarketOrder(TakerOrder calldata tko, SmartRouter userRouter)
    internal
    returns (TakerOrderResult memory res, bool isComplete)
  {
    // `pullAmount` is derived in `execute` of core protocol to ensure same behavior.
    uint pullAmount = tko.fillWants ? tko.tick.inboundFromOutboundUp(tko.fillVolume) : tko.fillVolume;
    // Pulling funds from `msg.sender`'s routing policy
    RL.RoutingOrder memory inboundRoute = RL.createOrder({fundOwner: msg.sender, token: IERC20(tko.olKey.inbound_tkn)});
    if (address(tko.takerGivesLogic) != address(0)) {
      // telling router to use `takerGivesLogic` when `inboundRoute` is selected
      userRouter.setLogic(inboundRoute, tko.takerGivesLogic);
    }
    require(userRouter.pull(inboundRoute, pullAmount, true) == pullAmount, "mgvOrder/transferInFail");

    // Executes a market order on Mangrove
    (res.takerGot, res.takerGave, res.bounty, res.fee) =
      MGV.marketOrderByTick({olKey: tko.olKey, maxTick: tko.tick, fillVolume: tko.fillVolume, fillWants: tko.fillWants});

    // Current state:
    // * (NAT_USER-`msg.value`, OUT_USER, IN_USER-`takerGives`)
    // * (NAT_THIS+`msg.value`+`res.bounty`, OUT_THIS+`res.takerGot`, IN_THIS+`takerGives`-`res.takerGave`)

    // Checking whether order was entirely filled
    isComplete = checkCompleteness(tko, res);

    // we check whether the order was entirely filled before posting the resting order
    // if the order is a FoK and was not entirely filled, we revert.
    // if the order is GTC(E) or IOC, the market order succeeds.
    // in all cases, if the market order is filled, we do not revert (Here we assume no PO orders are passed to this function)
    require(tko.orderType.marketOrderSucceded(isComplete), "mgvOrder/partialFill");

    RL.RoutingOrder memory outboundRoute;
    if (res.takerGot > 0) {
      // routing outbound token (received by taker) according to takerWantsLogic
      // We approve the user router to pull `takerGot` outbound tokens from this contract.
      TransferLib.approveToken(IERC20(tko.olKey.outbound_tkn), address(userRouter), res.takerGot);
      // pushing tokens received during market order
      outboundRoute = RL.createOrder({token: IERC20(tko.olKey.outbound_tkn), fundOwner: msg.sender});
      if (address(tko.takerWantsLogic) != address(0)) {
        // telling router to use `takerWantsLogic` when `outboundRoute` is selected.
        userRouter.setLogic(outboundRoute, tko.takerWantsLogic);
      }
      require(userRouter.push(outboundRoute, res.takerGot) == res.takerGot, "mgvOrder/pushFailed");
      // set back router's storage to 0 for some cheap gas refund
      if (address(tko.takerWantsLogic) != address(0)) {
        userRouter.setLogic(outboundRoute, AbstractRoutingLogic(address(0)));
      }
    }
    // we now deal with inbound left in case the order was partially filled
    uint inboundLeft = pullAmount - res.takerGave;
    if (inboundLeft > 0) {
      TransferLib.approveToken(IERC20(tko.olKey.inbound_tkn), address(userRouter), inboundLeft);
      // Here we can use `inboundRoute`.
      require(userRouter.push(inboundRoute, inboundLeft) == inboundLeft, "mgvOrder/pushFailed");
    }
    // set back router's storage to 0 for some cheap gas refund
    if (address(tko.takerGivesLogic) != address(0)) {
      userRouter.setLogic(inboundRoute, AbstractRoutingLogic(address(0)));
    }
  }

  ///@notice posts a maker order on the (`olKey`) offer list.
  ///@param tko the arguments in memory of the taker order
  ///@param olKey the offer list key.
  ///@param fund amount of WEIs used to cover for the offer bounty (covered gasprice is derived from `fund`).
  ///@param userRouter the user router
  ///@param res the result of the taker order.
  ///@return refund the amount to refund to the taker of the fund.
  ///@dev if relative limit price of taker order is `ratio` in the (outbound_tkn, inbound_tkn) offer list (represented by `tick=log_{1.0001}(ratio)` )
  ///@dev then entailed relative price for resting order must be `1/ratio` (relative price on the (inbound_tkn, outbound_tkn) offer list)
  ///@dev so with ticks that is `-log(ratio)`, or -tick.
  ///@dev the price of the resting order should be the same as for the max price for the market order.
  function postRestingOrder(
    TakerOrder calldata tko,
    OLKey memory olKey,
    TakerOrderResult memory res,
    SmartRouter userRouter,
    uint fund
  ) internal returns (uint refund) {
    // computing hash in advance
    bytes32 olKeyHash = olKey.hash();

    // computing residual values
    Tick residualTick = Tick.wrap(-Tick.unwrap(tko.tick));
    uint residualGives;
    if (tko.fillWants) {
      // partialFill => tko.fillVolume > res.takerGot + res.fee
      uint residualWants = tko.fillVolume - (res.takerGot + res.fee);
      // adapting residualGives to match relative limit price chosen by the taker
      residualGives = residualTick.outboundFromInboundUp(residualWants);
    } else {
      // partialFill => tko.fillVolume > res.takerGave
      residualGives = tko.fillVolume - res.takerGave;
    }
    OfferArgs memory args = OfferArgs({
      olKey: olKey,
      tick: residualTick,
      gives: residualGives,
      gasreq: tko.restingOrderGasreq,
      gasprice: 0, // ignored
      fund: fund,
      noRevert: true // returns `(0,reason)` if MGV reverts
    });
    if (tko.offerId == 0) {
      // posting a new offer
      (res.offerId, res.offerWriteData) = _newOffer(args, msg.sender);
    } else {
      // updating an existing offer
      uint offerId = tko.offerId;
      // checking ownership of offer since we use internal version of `updateOffer` which is unguarded
      require(ownerData[olKeyHash][offerId].owner == msg.sender, "AccessControlled/Invalid");
      // msg sender might have given an offerId that is already live
      // we disallow this to avoid confusion
      require(!MGV.offers(olKey, offerId).isLive(), "mgvOrder/offerAlreadyActive");
      res.offerWriteData = _updateOffer(args, offerId);
      if (res.offerWriteData == REPOST_SUCCESS) {
        res.offerId = offerId;
      } else {
        res.offerId = 0;
      }
    }
    if (res.offerId == 0) {
      // either:
      // - residualGives is below current density
      // - `fund` is too low and would yield a gasprice that is lower than Mangrove's
      // - `fund` is too high and would yield a gasprice overflow
      // - offer list is not active (Mangrove is not dead otherwise market order would have reverted)
      // So the resting order was not posted.
      // If GTC is enforced (GTCE) or if the order is PO we revert.
      require(!tko.orderType.enforcePostRestingOrder(), "mgvOrder/RestingOrderFailed");
      // `fund` is no longer needed so sending it back to `msg.sender`
      refund = fund;
    } else {
      // offer was successfully posted
      // `fund` was used and we leave `refund` at 0.
      // update or create routes for the given offer if needed
      RL.RoutingOrder memory route = RL.RoutingOrder({
        token: IERC20(olKey.outbound_tkn),
        fundOwner: msg.sender,
        olKeyHash: olKeyHash,
        offerId: res.offerId
      });
      if (address(tko.takerGivesLogic) != address(0)) {
        // because the taker becomes the maker, the outbound token logic is the takerGivesLogic
        userRouter.setLogic(route, tko.takerGivesLogic);
      }
      // updating route for inbound token
      route.token = IERC20(olKey.inbound_tkn);
      if (address(tko.takerWantsLogic) != address(0)) {
        // because the taker becomes the maker, the inbound token logic is the takerWantsLogic
        userRouter.setLogic(route, tko.takerWantsLogic);
      }

      // setting expiry date for the resting order
      if (tko.expiryDate > 0) {
        _setReneging(olKeyHash, res.offerId, tko.expiryDate, 0);
      }
    }
  }
}

Other files

  • node_modules/@mangrovedao/mangrove-core/src/preprocessed/Structs.post.sol
    // SPDX-License-Identifier: MIT
    
    pragma solidity ^0.8.13;
    
    /* ************************************************** *
                GENERATED FILE. DO NOT EDIT.
     * ************************************************** */
    
    
    import {Offer, OfferUnpacked, OfferLib}  from "./Offer.post.sol";
    
    import {OfferDetail, OfferDetailUnpacked, OfferDetailLib}  from "./OfferDetail.post.sol";
    
    import {Global, GlobalUnpacked, GlobalLib}  from "./Global.post.sol";
    
    import {Local, LocalUnpacked, LocalLib}  from "./Local.post.sol";
  • src/strategies/routers/abstract/AbstractRouter.sol
    // SPDX-License-Identifier: MIT
    pragma solidity ^0.8.10;
    
    import {AccessControlled} from "@mgv-strats/src/strategies/utils/AccessControlled.sol";
    import {RoutingOrderLib as RL} from "./RoutingOrderLib.sol";
    
    /// @title AbstractRouter
    /// @notice Partial implementation and requirements for liquidity routers.
    
    abstract contract AbstractRouter is AccessControlled(msg.sender) {
      ///@notice This modifier verifies that `msg.sender` an allowed caller of this router.
      modifier onlyBound() {
        require(isBound(msg.sender), "AccessControlled/Invalid");
        _;
      }
    
      ///@notice This modifier verifies that `msg.sender` is the admin or an allowed caller of this router.
      modifier boundOrAdmin() {
        require(msg.sender == admin() || isBound(msg.sender), "AccessControlled/Invalid");
        _;
      }
    
      ///@notice logging bound maker contract
      ///@param maker the maker address. This is indexed, so that RPC calls can filter on it.
      ///@notice by emitting this data, an indexer will be able to keep track of what maker contracts are allowed to call this router.
      event MakerBind(address indexed maker);
    
      ///@notice logging unbound maker contract
      ///@param maker the maker address. This is indexed, so that RPC calls can filter on it.
      ///@notice by emitting this data, an indexer will be able to keep track of what maker contracts are allowed to call this router.
      event MakerUnbind(address indexed maker);
    
      ///@notice getter for the `makers: addr => bool` mapping
      ///@param mkr the address of a maker contract
      ///@return true if `mkr` is authorized to call this router.
      function isBound(address mkr) public view virtual returns (bool) {
        return RL.boundMakerContracts()[mkr];
      }
    
      ///@notice pulls liquidity from the reserve and sends it to the calling maker contract.
      ///@param routingOrder the arguments of the pull order
      ///@param amount of token that needs to be routed
      ///@param strict if false the router may pull at more than `amount` to msg.sender. Otherwise it pulls at least `amount`.
      ///@return pulled the amount of `routingOrder.token` that has been sent to `msg.sender`
      function pull(RL.RoutingOrder calldata routingOrder, uint amount, bool strict)
        external
        onlyBound
        returns (uint pulled)
      {
        if (strict && amount == 0) {
          return 0;
        }
        pulled = __pull__(routingOrder, amount, strict);
      }
    
      ///@notice router dependent hook to customize pull orders.
      ///@param routingOrder the arguments of the pull order
      ///@param amount of token that needs to be routed
      ///@param strict if false the router may pull at more than `amount` to msg.sender. Otherwise it pulls at least `amount`.
      ///@return pulled the amount of `routingOrder.token` that has been sent to `msg.sender`
      function __pull__(RL.RoutingOrder memory routingOrder, uint amount, bool strict) internal virtual returns (uint);
    
      ///@notice pushes liquidity from msg.sender to the reserve
      ///@param routingOrder the arguments of the push order
      ///@param amount of token that needs to be routed
      ///@return pushed the amount of `routingOrder.token` that has been taken from `msg.sender`
      function push(RL.RoutingOrder calldata routingOrder, uint amount) external onlyBound returns (uint pushed) {
        if (amount == 0) {
          return 0;
        }
        pushed = __push__(routingOrder, amount);
      }
    
      ///@notice router dependent hook to customize pull orders.
      ///@param routingOrder the arguments of the pull order
      ///@param amount of token that needs to be routed
      ///@return pushed the amount of `routingOrder.token` that has been sent to `msg.sender`
      function __push__(RL.RoutingOrder memory routingOrder, uint amount) internal virtual returns (uint pushed);
    
      ///@notice iterative `push` of the whole maker contract's balance
      ///@dev minimizes external calls in case several assets needs to be pushed via the router.
      ///@param routingOrders to be executed
      function flush(RL.RoutingOrder[] memory routingOrders) external onlyBound {
        for (uint i = 0; i < routingOrders.length; ++i) {
          uint amount = routingOrders[i].token.balanceOf(msg.sender);
          if (amount > 0) {
            require(__push__(routingOrders[i], amount) == amount, "router/flushFailed");
          }
        }
      }
    
      ///@notice adds a maker contract address to the allowed makers of this router
      ///@dev this function is callable by router's admin to bootstrap, but later on an allowed maker contract can add another address
      ///@param makerContract the maker contract address
      function bind(address makerContract) public onlyAdmin {
        RL.boundMakerContracts()[makerContract] = true;
        emit MakerBind(makerContract);
      }
    
      ///@notice removes a maker contract address from the allowed makers of this router
      ///@param makerContract the maker contract address
      function _unbind(address makerContract) internal {
        RL.boundMakerContracts()[makerContract] = false;
        emit MakerUnbind(makerContract);
      }
    
      ///@notice removes `msg.sender` from the allowed makers of this router
      function unbind() external onlyBound {
        _unbind(msg.sender);
      }
    
      ///@notice removes a makerContract from the allowed makers of this router
      ///@param makerContract the maker contract address
      function unbind(address makerContract) external onlyAdmin {
        _unbind(makerContract);
      }
    
      /**
       * @notice Returns the token balance available for a specific routing order
       * @param routingOrder The routing order to check the balance for
       * @return balance The balance of the token in the routing order
       */
      function tokenBalanceOf(RL.RoutingOrder calldata routingOrder) public view virtual returns (uint balance);
    }
    
  • node_modules/@mangrovedao/mangrove-core/lib/forge-std/src/console2.sol
    // SPDX-License-Identifier: MIT
    pragma solidity >=0.4.22 <0.9.0;
    
    /// @dev The original console.sol uses `int` and `uint` for computing function selectors, but it should
    /// use `int256` and `uint256`. This modified version fixes that. This version is recommended
    /// over `console.sol` if you don't need compatibility with Hardhat as the logs will show up in
    /// forge stack traces. If you do need compatibility with Hardhat, you must use `console.sol`.
    /// Reference: https://github.com/NomicFoundation/hardhat/issues/2178
    library console2 {
        address constant CONSOLE_ADDRESS = address(0x000000000000000000636F6e736F6c652e6c6f67);
    
        function _castLogPayloadViewToPure(
            function(bytes memory) internal view fnIn
        ) internal pure returns (function(bytes memory) internal pure fnOut) {
            assembly {
                fnOut := fnIn
            }
        }
    
        function _sendLogPayload(bytes memory payload) internal pure {
            _castLogPayloadViewToPure(_sendLogPayloadView)(payload);
        }
    
        function _sendLogPayloadView(bytes memory payload) private view {
            uint256 payloadLength = payload.length;
            address consoleAddress = CONSOLE_ADDRESS;
            /// @solidity memory-safe-assembly
            assembly {
                let payloadStart := add(payload, 32)
                let r := staticcall(gas(), consoleAddress, payloadStart, payloadLength, 0, 0)
            }
        }
    
        function log() internal pure {
            _sendLogPayload(abi.encodeWithSignature("log()"));
        }
    
        function logInt(int256 p0) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(int256)", p0));
        }
    
        function logUint(uint256 p0) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(uint256)", p0));
        }
    
        function logString(string memory p0) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(string)", p0));
        }
    
        function logBool(bool p0) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(bool)", p0));
        }
    
        function logAddress(address p0) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(address)", p0));
        }
    
        function logBytes(bytes memory p0) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(bytes)", p0));
        }
    
        function logBytes1(bytes1 p0) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(bytes1)", p0));
        }
    
        function logBytes2(bytes2 p0) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(bytes2)", p0));
        }
    
        function logBytes3(bytes3 p0) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(bytes3)", p0));
        }
    
        function logBytes4(bytes4 p0) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(bytes4)", p0));
        }
    
        function logBytes5(bytes5 p0) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(bytes5)", p0));
        }
    
        function logBytes6(bytes6 p0) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(bytes6)", p0));
        }
    
        function logBytes7(bytes7 p0) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(bytes7)", p0));
        }
    
        function logBytes8(bytes8 p0) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(bytes8)", p0));
        }
    
        function logBytes9(bytes9 p0) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(bytes9)", p0));
        }
    
        function logBytes10(bytes10 p0) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(bytes10)", p0));
        }
    
        function logBytes11(bytes11 p0) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(bytes11)", p0));
        }
    
        function logBytes12(bytes12 p0) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(bytes12)", p0));
        }
    
        function logBytes13(bytes13 p0) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(bytes13)", p0));
        }
    
        function logBytes14(bytes14 p0) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(bytes14)", p0));
        }
    
        function logBytes15(bytes15 p0) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(bytes15)", p0));
        }
    
        function logBytes16(bytes16 p0) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(bytes16)", p0));
        }
    
        function logBytes17(bytes17 p0) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(bytes17)", p0));
        }
    
        function logBytes18(bytes18 p0) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(bytes18)", p0));
        }
    
        function logBytes19(bytes19 p0) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(bytes19)", p0));
        }
    
        function logBytes20(bytes20 p0) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(bytes20)", p0));
        }
    
        function logBytes21(bytes21 p0) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(bytes21)", p0));
        }
    
        function logBytes22(bytes22 p0) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(bytes22)", p0));
        }
    
        function logBytes23(bytes23 p0) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(bytes23)", p0));
        }
    
        function logBytes24(bytes24 p0) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(bytes24)", p0));
        }
    
        function logBytes25(bytes25 p0) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(bytes25)", p0));
        }
    
        function logBytes26(bytes26 p0) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(bytes26)", p0));
        }
    
        function logBytes27(bytes27 p0) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(bytes27)", p0));
        }
    
        function logBytes28(bytes28 p0) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(bytes28)", p0));
        }
    
        function logBytes29(bytes29 p0) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(bytes29)", p0));
        }
    
        function logBytes30(bytes30 p0) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(bytes30)", p0));
        }
    
        function logBytes31(bytes31 p0) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(bytes31)", p0));
        }
    
        function logBytes32(bytes32 p0) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(bytes32)", p0));
        }
    
        function log(uint256 p0) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(uint256)", p0));
        }
    
        function log(int256 p0) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(int256)", p0));
        }
    
        function log(string memory p0) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(string)", p0));
        }
    
        function log(bool p0) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(bool)", p0));
        }
    
        function log(address p0) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(address)", p0));
        }
    
        function log(uint256 p0, uint256 p1) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(uint256,uint256)", p0, p1));
        }
    
        function log(uint256 p0, string memory p1) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(uint256,string)", p0, p1));
        }
    
        function log(uint256 p0, bool p1) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(uint256,bool)", p0, p1));
        }
    
        function log(uint256 p0, address p1) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(uint256,address)", p0, p1));
        }
    
        function log(string memory p0, uint256 p1) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(string,uint256)", p0, p1));
        }
    
        function log(string memory p0, int256 p1) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(string,int256)", p0, p1));
        }
    
        function log(string memory p0, string memory p1) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(string,string)", p0, p1));
        }
    
        function log(string memory p0, bool p1) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(string,bool)", p0, p1));
        }
    
        function log(string memory p0, address p1) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(string,address)", p0, p1));
        }
    
        function log(bool p0, uint256 p1) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(bool,uint256)", p0, p1));
        }
    
        function log(bool p0, string memory p1) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(bool,string)", p0, p1));
        }
    
        function log(bool p0, bool p1) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(bool,bool)", p0, p1));
        }
    
        function log(bool p0, address p1) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(bool,address)", p0, p1));
        }
    
        function log(address p0, uint256 p1) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(address,uint256)", p0, p1));
        }
    
        function log(address p0, string memory p1) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(address,string)", p0, p1));
        }
    
        function log(address p0, bool p1) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(address,bool)", p0, p1));
        }
    
        function log(address p0, address p1) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(address,address)", p0, p1));
        }
    
        function log(uint256 p0, uint256 p1, uint256 p2) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(uint256,uint256,uint256)", p0, p1, p2));
        }
    
        function log(uint256 p0, uint256 p1, string memory p2) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(uint256,uint256,string)", p0, p1, p2));
        }
    
        function log(uint256 p0, uint256 p1, bool p2) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(uint256,uint256,bool)", p0, p1, p2));
        }
    
        function log(uint256 p0, uint256 p1, address p2) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(uint256,uint256,address)", p0, p1, p2));
        }
    
        function log(uint256 p0, string memory p1, uint256 p2) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(uint256,string,uint256)", p0, p1, p2));
        }
    
        function log(uint256 p0, string memory p1, string memory p2) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(uint256,string,string)", p0, p1, p2));
        }
    
        function log(uint256 p0, string memory p1, bool p2) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(uint256,string,bool)", p0, p1, p2));
        }
    
        function log(uint256 p0, string memory p1, address p2) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(uint256,string,address)", p0, p1, p2));
        }
    
        function log(uint256 p0, bool p1, uint256 p2) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(uint256,bool,uint256)", p0, p1, p2));
        }
    
        function log(uint256 p0, bool p1, string memory p2) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(uint256,bool,string)", p0, p1, p2));
        }
    
        function log(uint256 p0, bool p1, bool p2) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(uint256,bool,bool)", p0, p1, p2));
        }
    
        function log(uint256 p0, bool p1, address p2) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(uint256,bool,address)", p0, p1, p2));
        }
    
        function log(uint256 p0, address p1, uint256 p2) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(uint256,address,uint256)", p0, p1, p2));
        }
    
        function log(uint256 p0, address p1, string memory p2) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(uint256,address,string)", p0, p1, p2));
        }
    
        function log(uint256 p0, address p1, bool p2) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(uint256,address,bool)", p0, p1, p2));
        }
    
        function log(uint256 p0, address p1, address p2) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(uint256,address,address)", p0, p1, p2));
        }
    
        function log(string memory p0, uint256 p1, uint256 p2) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(string,uint256,uint256)", p0, p1, p2));
        }
    
        function log(string memory p0, uint256 p1, string memory p2) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(string,uint256,string)", p0, p1, p2));
        }
    
        function log(string memory p0, uint256 p1, bool p2) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(string,uint256,bool)", p0, p1, p2));
        }
    
        function log(string memory p0, uint256 p1, address p2) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(string,uint256,address)", p0, p1, p2));
        }
    
        function log(string memory p0, string memory p1, uint256 p2) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(string,string,uint256)", p0, p1, p2));
        }
    
        function log(string memory p0, string memory p1, string memory p2) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(string,string,string)", p0, p1, p2));
        }
    
        function log(string memory p0, string memory p1, bool p2) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(string,string,bool)", p0, p1, p2));
        }
    
        function log(string memory p0, string memory p1, address p2) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(string,string,address)", p0, p1, p2));
        }
    
        function log(string memory p0, bool p1, uint256 p2) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(string,bool,uint256)", p0, p1, p2));
        }
    
        function log(string memory p0, bool p1, string memory p2) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(string,bool,string)", p0, p1, p2));
        }
    
        function log(string memory p0, bool p1, bool p2) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(string,bool,bool)", p0, p1, p2));
        }
    
        function log(string memory p0, bool p1, address p2) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(string,bool,address)", p0, p1, p2));
        }
    
        function log(string memory p0, address p1, uint256 p2) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(string,address,uint256)", p0, p1, p2));
        }
    
        function log(string memory p0, address p1, string memory p2) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(string,address,string)", p0, p1, p2));
        }
    
        function log(string memory p0, address p1, bool p2) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(string,address,bool)", p0, p1, p2));
        }
    
        function log(string memory p0, address p1, address p2) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(string,address,address)", p0, p1, p2));
        }
    
        function log(bool p0, uint256 p1, uint256 p2) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(bool,uint256,uint256)", p0, p1, p2));
        }
    
        function log(bool p0, uint256 p1, string memory p2) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(bool,uint256,string)", p0, p1, p2));
        }
    
        function log(bool p0, uint256 p1, bool p2) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(bool,uint256,bool)", p0, p1, p2));
        }
    
        function log(bool p0, uint256 p1, address p2) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(bool,uint256,address)", p0, p1, p2));
        }
    
        function log(bool p0, string memory p1, uint256 p2) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(bool,string,uint256)", p0, p1, p2));
        }
    
        function log(bool p0, string memory p1, string memory p2) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(bool,string,string)", p0, p1, p2));
        }
    
        function log(bool p0, string memory p1, bool p2) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(bool,string,bool)", p0, p1, p2));
        }
    
        function log(bool p0, string memory p1, address p2) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(bool,string,address)", p0, p1, p2));
        }
    
        function log(bool p0, bool p1, uint256 p2) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(bool,bool,uint256)", p0, p1, p2));
        }
    
        function log(bool p0, bool p1, string memory p2) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(bool,bool,string)", p0, p1, p2));
        }
    
        function log(bool p0, bool p1, bool p2) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(bool,bool,bool)", p0, p1, p2));
        }
    
        function log(bool p0, bool p1, address p2) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(bool,bool,address)", p0, p1, p2));
        }
    
        function log(bool p0, address p1, uint256 p2) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(bool,address,uint256)", p0, p1, p2));
        }
    
        function log(bool p0, address p1, string memory p2) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(bool,address,string)", p0, p1, p2));
        }
    
        function log(bool p0, address p1, bool p2) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(bool,address,bool)", p0, p1, p2));
        }
    
        function log(bool p0, address p1, address p2) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(bool,address,address)", p0, p1, p2));
        }
    
        function log(address p0, uint256 p1, uint256 p2) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(address,uint256,uint256)", p0, p1, p2));
        }
    
        function log(address p0, uint256 p1, string memory p2) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(address,uint256,string)", p0, p1, p2));
        }
    
        function log(address p0, uint256 p1, bool p2) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(address,uint256,bool)", p0, p1, p2));
        }
    
        function log(address p0, uint256 p1, address p2) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(address,uint256,address)", p0, p1, p2));
        }
    
        function log(address p0, string memory p1, uint256 p2) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(address,string,uint256)", p0, p1, p2));
        }
    
        function log(address p0, string memory p1, string memory p2) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(address,string,string)", p0, p1, p2));
        }
    
        function log(address p0, string memory p1, bool p2) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(address,string,bool)", p0, p1, p2));
        }
    
        function log(address p0, string memory p1, address p2) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(address,string,address)", p0, p1, p2));
        }
    
        function log(address p0, bool p1, uint256 p2) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(address,bool,uint256)", p0, p1, p2));
        }
    
        function log(address p0, bool p1, string memory p2) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(address,bool,string)", p0, p1, p2));
        }
    
        function log(address p0, bool p1, bool p2) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(address,bool,bool)", p0, p1, p2));
        }
    
        function log(address p0, bool p1, address p2) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(address,bool,address)", p0, p1, p2));
        }
    
        function log(address p0, address p1, uint256 p2) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(address,address,uint256)", p0, p1, p2));
        }
    
        function log(address p0, address p1, string memory p2) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(address,address,string)", p0, p1, p2));
        }
    
        function log(address p0, address p1, bool p2) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(address,address,bool)", p0, p1, p2));
        }
    
        function log(address p0, address p1, address p2) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(address,address,address)", p0, p1, p2));
        }
    
        function log(uint256 p0, uint256 p1, uint256 p2, uint256 p3) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(uint256,uint256,uint256,uint256)", p0, p1, p2, p3));
        }
    
        function log(uint256 p0, uint256 p1, uint256 p2, string memory p3) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(uint256,uint256,uint256,string)", p0, p1, p2, p3));
        }
    
        function log(uint256 p0, uint256 p1, uint256 p2, bool p3) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(uint256,uint256,uint256,bool)", p0, p1, p2, p3));
        }
    
        function log(uint256 p0, uint256 p1, uint256 p2, address p3) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(uint256,uint256,uint256,address)", p0, p1, p2, p3));
        }
    
        function log(uint256 p0, uint256 p1, string memory p2, uint256 p3) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(uint256,uint256,string,uint256)", p0, p1, p2, p3));
        }
    
        function log(uint256 p0, uint256 p1, string memory p2, string memory p3) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(uint256,uint256,string,string)", p0, p1, p2, p3));
        }
    
        function log(uint256 p0, uint256 p1, string memory p2, bool p3) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(uint256,uint256,string,bool)", p0, p1, p2, p3));
        }
    
        function log(uint256 p0, uint256 p1, string memory p2, address p3) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(uint256,uint256,string,address)", p0, p1, p2, p3));
        }
    
        function log(uint256 p0, uint256 p1, bool p2, uint256 p3) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(uint256,uint256,bool,uint256)", p0, p1, p2, p3));
        }
    
        function log(uint256 p0, uint256 p1, bool p2, string memory p3) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(uint256,uint256,bool,string)", p0, p1, p2, p3));
        }
    
        function log(uint256 p0, uint256 p1, bool p2, bool p3) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(uint256,uint256,bool,bool)", p0, p1, p2, p3));
        }
    
        function log(uint256 p0, uint256 p1, bool p2, address p3) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(uint256,uint256,bool,address)", p0, p1, p2, p3));
        }
    
        function log(uint256 p0, uint256 p1, address p2, uint256 p3) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(uint256,uint256,address,uint256)", p0, p1, p2, p3));
        }
    
        function log(uint256 p0, uint256 p1, address p2, string memory p3) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(uint256,uint256,address,string)", p0, p1, p2, p3));
        }
    
        function log(uint256 p0, uint256 p1, address p2, bool p3) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(uint256,uint256,address,bool)", p0, p1, p2, p3));
        }
    
        function log(uint256 p0, uint256 p1, address p2, address p3) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(uint256,uint256,address,address)", p0, p1, p2, p3));
        }
    
        function log(uint256 p0, string memory p1, uint256 p2, uint256 p3) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(uint256,string,uint256,uint256)", p0, p1, p2, p3));
        }
    
        function log(uint256 p0, string memory p1, uint256 p2, string memory p3) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(uint256,string,uint256,string)", p0, p1, p2, p3));
        }
    
        function log(uint256 p0, string memory p1, uint256 p2, bool p3) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(uint256,string,uint256,bool)", p0, p1, p2, p3));
        }
    
        function log(uint256 p0, string memory p1, uint256 p2, address p3) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(uint256,string,uint256,address)", p0, p1, p2, p3));
        }
    
        function log(uint256 p0, string memory p1, string memory p2, uint256 p3) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(uint256,string,string,uint256)", p0, p1, p2, p3));
        }
    
        function log(uint256 p0, string memory p1, string memory p2, string memory p3) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(uint256,string,string,string)", p0, p1, p2, p3));
        }
    
        function log(uint256 p0, string memory p1, string memory p2, bool p3) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(uint256,string,string,bool)", p0, p1, p2, p3));
        }
    
        function log(uint256 p0, string memory p1, string memory p2, address p3) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(uint256,string,string,address)", p0, p1, p2, p3));
        }
    
        function log(uint256 p0, string memory p1, bool p2, uint256 p3) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(uint256,string,bool,uint256)", p0, p1, p2, p3));
        }
    
        function log(uint256 p0, string memory p1, bool p2, string memory p3) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(uint256,string,bool,string)", p0, p1, p2, p3));
        }
    
        function log(uint256 p0, string memory p1, bool p2, bool p3) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(uint256,string,bool,bool)", p0, p1, p2, p3));
        }
    
        function log(uint256 p0, string memory p1, bool p2, address p3) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(uint256,string,bool,address)", p0, p1, p2, p3));
        }
    
        function log(uint256 p0, string memory p1, address p2, uint256 p3) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(uint256,string,address,uint256)", p0, p1, p2, p3));
        }
    
        function log(uint256 p0, string memory p1, address p2, string memory p3) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(uint256,string,address,string)", p0, p1, p2, p3));
        }
    
        function log(uint256 p0, string memory p1, address p2, bool p3) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(uint256,string,address,bool)", p0, p1, p2, p3));
        }
    
        function log(uint256 p0, string memory p1, address p2, address p3) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(uint256,string,address,address)", p0, p1, p2, p3));
        }
    
        function log(uint256 p0, bool p1, uint256 p2, uint256 p3) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(uint256,bool,uint256,uint256)", p0, p1, p2, p3));
        }
    
        function log(uint256 p0, bool p1, uint256 p2, string memory p3) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(uint256,bool,uint256,string)", p0, p1, p2, p3));
        }
    
        function log(uint256 p0, bool p1, uint256 p2, bool p3) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(uint256,bool,uint256,bool)", p0, p1, p2, p3));
        }
    
        function log(uint256 p0, bool p1, uint256 p2, address p3) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(uint256,bool,uint256,address)", p0, p1, p2, p3));
        }
    
        function log(uint256 p0, bool p1, string memory p2, uint256 p3) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(uint256,bool,string,uint256)", p0, p1, p2, p3));
        }
    
        function log(uint256 p0, bool p1, string memory p2, string memory p3) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(uint256,bool,string,string)", p0, p1, p2, p3));
        }
    
        function log(uint256 p0, bool p1, string memory p2, bool p3) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(uint256,bool,string,bool)", p0, p1, p2, p3));
        }
    
        function log(uint256 p0, bool p1, string memory p2, address p3) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(uint256,bool,string,address)", p0, p1, p2, p3));
        }
    
        function log(uint256 p0, bool p1, bool p2, uint256 p3) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(uint256,bool,bool,uint256)", p0, p1, p2, p3));
        }
    
        function log(uint256 p0, bool p1, bool p2, string memory p3) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(uint256,bool,bool,string)", p0, p1, p2, p3));
        }
    
        function log(uint256 p0, bool p1, bool p2, bool p3) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(uint256,bool,bool,bool)", p0, p1, p2, p3));
        }
    
        function log(uint256 p0, bool p1, bool p2, address p3) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(uint256,bool,bool,address)", p0, p1, p2, p3));
        }
    
        function log(uint256 p0, bool p1, address p2, uint256 p3) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(uint256,bool,address,uint256)", p0, p1, p2, p3));
        }
    
        function log(uint256 p0, bool p1, address p2, string memory p3) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(uint256,bool,address,string)", p0, p1, p2, p3));
        }
    
        function log(uint256 p0, bool p1, address p2, bool p3) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(uint256,bool,address,bool)", p0, p1, p2, p3));
        }
    
        function log(uint256 p0, bool p1, address p2, address p3) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(uint256,bool,address,address)", p0, p1, p2, p3));
        }
    
        function log(uint256 p0, address p1, uint256 p2, uint256 p3) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(uint256,address,uint256,uint256)", p0, p1, p2, p3));
        }
    
        function log(uint256 p0, address p1, uint256 p2, string memory p3) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(uint256,address,uint256,string)", p0, p1, p2, p3));
        }
    
        function log(uint256 p0, address p1, uint256 p2, bool p3) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(uint256,address,uint256,bool)", p0, p1, p2, p3));
        }
    
        function log(uint256 p0, address p1, uint256 p2, address p3) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(uint256,address,uint256,address)", p0, p1, p2, p3));
        }
    
        function log(uint256 p0, address p1, string memory p2, uint256 p3) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(uint256,address,string,uint256)", p0, p1, p2, p3));
        }
    
        function log(uint256 p0, address p1, string memory p2, string memory p3) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(uint256,address,string,string)", p0, p1, p2, p3));
        }
    
        function log(uint256 p0, address p1, string memory p2, bool p3) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(uint256,address,string,bool)", p0, p1, p2, p3));
        }
    
        function log(uint256 p0, address p1, string memory p2, address p3) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(uint256,address,string,address)", p0, p1, p2, p3));
        }
    
        function log(uint256 p0, address p1, bool p2, uint256 p3) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(uint256,address,bool,uint256)", p0, p1, p2, p3));
        }
    
        function log(uint256 p0, address p1, bool p2, string memory p3) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(uint256,address,bool,string)", p0, p1, p2, p3));
        }
    
        function log(uint256 p0, address p1, bool p2, bool p3) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(uint256,address,bool,bool)", p0, p1, p2, p3));
        }
    
        function log(uint256 p0, address p1, bool p2, address p3) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(uint256,address,bool,address)", p0, p1, p2, p3));
        }
    
        function log(uint256 p0, address p1, address p2, uint256 p3) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(uint256,address,address,uint256)", p0, p1, p2, p3));
        }
    
        function log(uint256 p0, address p1, address p2, string memory p3) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(uint256,address,address,string)", p0, p1, p2, p3));
        }
    
        function log(uint256 p0, address p1, address p2, bool p3) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(uint256,address,address,bool)", p0, p1, p2, p3));
        }
    
        function log(uint256 p0, address p1, address p2, address p3) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(uint256,address,address,address)", p0, p1, p2, p3));
        }
    
        function log(string memory p0, uint256 p1, uint256 p2, uint256 p3) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(string,uint256,uint256,uint256)", p0, p1, p2, p3));
        }
    
        function log(string memory p0, uint256 p1, uint256 p2, string memory p3) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(string,uint256,uint256,string)", p0, p1, p2, p3));
        }
    
        function log(string memory p0, uint256 p1, uint256 p2, bool p3) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(string,uint256,uint256,bool)", p0, p1, p2, p3));
        }
    
        function log(string memory p0, uint256 p1, uint256 p2, address p3) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(string,uint256,uint256,address)", p0, p1, p2, p3));
        }
    
        function log(string memory p0, uint256 p1, string memory p2, uint256 p3) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(string,uint256,string,uint256)", p0, p1, p2, p3));
        }
    
        function log(string memory p0, uint256 p1, string memory p2, string memory p3) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(string,uint256,string,string)", p0, p1, p2, p3));
        }
    
        function log(string memory p0, uint256 p1, string memory p2, bool p3) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(string,uint256,string,bool)", p0, p1, p2, p3));
        }
    
        function log(string memory p0, uint256 p1, string memory p2, address p3) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(string,uint256,string,address)", p0, p1, p2, p3));
        }
    
        function log(string memory p0, uint256 p1, bool p2, uint256 p3) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(string,uint256,bool,uint256)", p0, p1, p2, p3));
        }
    
        function log(string memory p0, uint256 p1, bool p2, string memory p3) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(string,uint256,bool,string)", p0, p1, p2, p3));
        }
    
        function log(string memory p0, uint256 p1, bool p2, bool p3) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(string,uint256,bool,bool)", p0, p1, p2, p3));
        }
    
        function log(string memory p0, uint256 p1, bool p2, address p3) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(string,uint256,bool,address)", p0, p1, p2, p3));
        }
    
        function log(string memory p0, uint256 p1, address p2, uint256 p3) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(string,uint256,address,uint256)", p0, p1, p2, p3));
        }
    
        function log(string memory p0, uint256 p1, address p2, string memory p3) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(string,uint256,address,string)", p0, p1, p2, p3));
        }
    
        function log(string memory p0, uint256 p1, address p2, bool p3) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(string,uint256,address,bool)", p0, p1, p2, p3));
        }
    
        function log(string memory p0, uint256 p1, address p2, address p3) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(string,uint256,address,address)", p0, p1, p2, p3));
        }
    
        function log(string memory p0, string memory p1, uint256 p2, uint256 p3) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(string,string,uint256,uint256)", p0, p1, p2, p3));
        }
    
        function log(string memory p0, string memory p1, uint256 p2, string memory p3) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(string,string,uint256,string)", p0, p1, p2, p3));
        }
    
        function log(string memory p0, string memory p1, uint256 p2, bool p3) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(string,string,uint256,bool)", p0, p1, p2, p3));
        }
    
        function log(string memory p0, string memory p1, uint256 p2, address p3) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(string,string,uint256,address)", p0, p1, p2, p3));
        }
    
        function log(string memory p0, string memory p1, string memory p2, uint256 p3) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(string,string,string,uint256)", p0, p1, p2, p3));
        }
    
        function log(string memory p0, string memory p1, string memory p2, string memory p3) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(string,string,string,string)", p0, p1, p2, p3));
        }
    
        function log(string memory p0, string memory p1, string memory p2, bool p3) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(string,string,string,bool)", p0, p1, p2, p3));
        }
    
        function log(string memory p0, string memory p1, string memory p2, address p3) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(string,string,string,address)", p0, p1, p2, p3));
        }
    
        function log(string memory p0, string memory p1, bool p2, uint256 p3) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(string,string,bool,uint256)", p0, p1, p2, p3));
        }
    
        function log(string memory p0, string memory p1, bool p2, string memory p3) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(string,string,bool,string)", p0, p1, p2, p3));
        }
    
        function log(string memory p0, string memory p1, bool p2, bool p3) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(string,string,bool,bool)", p0, p1, p2, p3));
        }
    
        function log(string memory p0, string memory p1, bool p2, address p3) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(string,string,bool,address)", p0, p1, p2, p3));
        }
    
        function log(string memory p0, string memory p1, address p2, uint256 p3) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(string,string,address,uint256)", p0, p1, p2, p3));
        }
    
        function log(string memory p0, string memory p1, address p2, string memory p3) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(string,string,address,string)", p0, p1, p2, p3));
        }
    
        function log(string memory p0, string memory p1, address p2, bool p3) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(string,string,address,bool)", p0, p1, p2, p3));
        }
    
        function log(string memory p0, string memory p1, address p2, address p3) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(string,string,address,address)", p0, p1, p2, p3));
        }
    
        function log(string memory p0, bool p1, uint256 p2, uint256 p3) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(string,bool,uint256,uint256)", p0, p1, p2, p3));
        }
    
        function log(string memory p0, bool p1, uint256 p2, string memory p3) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(string,bool,uint256,string)", p0, p1, p2, p3));
        }
    
        function log(string memory p0, bool p1, uint256 p2, bool p3) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(string,bool,uint256,bool)", p0, p1, p2, p3));
        }
    
        function log(string memory p0, bool p1, uint256 p2, address p3) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(string,bool,uint256,address)", p0, p1, p2, p3));
        }
    
        function log(string memory p0, bool p1, string memory p2, uint256 p3) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(string,bool,string,uint256)", p0, p1, p2, p3));
        }
    
        function log(string memory p0, bool p1, string memory p2, string memory p3) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(string,bool,string,string)", p0, p1, p2, p3));
        }
    
        function log(string memory p0, bool p1, string memory p2, bool p3) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(string,bool,string,bool)", p0, p1, p2, p3));
        }
    
        function log(string memory p0, bool p1, string memory p2, address p3) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(string,bool,string,address)", p0, p1, p2, p3));
        }
    
        function log(string memory p0, bool p1, bool p2, uint256 p3) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(string,bool,bool,uint256)", p0, p1, p2, p3));
        }
    
        function log(string memory p0, bool p1, bool p2, string memory p3) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(string,bool,bool,string)", p0, p1, p2, p3));
        }
    
        function log(string memory p0, bool p1, bool p2, bool p3) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(string,bool,bool,bool)", p0, p1, p2, p3));
        }
    
        function log(string memory p0, bool p1, bool p2, address p3) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(string,bool,bool,address)", p0, p1, p2, p3));
        }
    
        function log(string memory p0, bool p1, address p2, uint256 p3) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(string,bool,address,uint256)", p0, p1, p2, p3));
        }
    
        function log(string memory p0, bool p1, address p2, string memory p3) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(string,bool,address,string)", p0, p1, p2, p3));
        }
    
        function log(string memory p0, bool p1, address p2, bool p3) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(string,bool,address,bool)", p0, p1, p2, p3));
        }
    
        function log(string memory p0, bool p1, address p2, address p3) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(string,bool,address,address)", p0, p1, p2, p3));
        }
    
        function log(string memory p0, address p1, uint256 p2, uint256 p3) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(string,address,uint256,uint256)", p0, p1, p2, p3));
        }
    
        function log(string memory p0, address p1, uint256 p2, string memory p3) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(string,address,uint256,string)", p0, p1, p2, p3));
        }
    
        function log(string memory p0, address p1, uint256 p2, bool p3) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(string,address,uint256,bool)", p0, p1, p2, p3));
        }
    
        function log(string memory p0, address p1, uint256 p2, address p3) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(string,address,uint256,address)", p0, p1, p2, p3));
        }
    
        function log(string memory p0, address p1, string memory p2, uint256 p3) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(string,address,string,uint256)", p0, p1, p2, p3));
        }
    
        function log(string memory p0, address p1, string memory p2, string memory p3) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(string,address,string,string)", p0, p1, p2, p3));
        }
    
        function log(string memory p0, address p1, string memory p2, bool p3) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(string,address,string,bool)", p0, p1, p2, p3));
        }
    
        function log(string memory p0, address p1, string memory p2, address p3) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(string,address,string,address)", p0, p1, p2, p3));
        }
    
        function log(string memory p0, address p1, bool p2, uint256 p3) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(string,address,bool,uint256)", p0, p1, p2, p3));
        }
    
        function log(string memory p0, address p1, bool p2, string memory p3) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(string,address,bool,string)", p0, p1, p2, p3));
        }
    
        function log(string memory p0, address p1, bool p2, bool p3) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(string,address,bool,bool)", p0, p1, p2, p3));
        }
    
        function log(string memory p0, address p1, bool p2, address p3) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(string,address,bool,address)", p0, p1, p2, p3));
        }
    
        function log(string memory p0, address p1, address p2, uint256 p3) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(string,address,address,uint256)", p0, p1, p2, p3));
        }
    
        function log(string memory p0, address p1, address p2, string memory p3) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(string,address,address,string)", p0, p1, p2, p3));
        }
    
        function log(string memory p0, address p1, address p2, bool p3) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(string,address,address,bool)", p0, p1, p2, p3));
        }
    
        function log(string memory p0, address p1, address p2, address p3) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(string,address,address,address)", p0, p1, p2, p3));
        }
    
        function log(bool p0, uint256 p1, uint256 p2, uint256 p3) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(bool,uint256,uint256,uint256)", p0, p1, p2, p3));
        }
    
        function log(bool p0, uint256 p1, uint256 p2, string memory p3) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(bool,uint256,uint256,string)", p0, p1, p2, p3));
        }
    
        function log(bool p0, uint256 p1, uint256 p2, bool p3) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(bool,uint256,uint256,bool)", p0, p1, p2, p3));
        }
    
        function log(bool p0, uint256 p1, uint256 p2, address p3) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(bool,uint256,uint256,address)", p0, p1, p2, p3));
        }
    
        function log(bool p0, uint256 p1, string memory p2, uint256 p3) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(bool,uint256,string,uint256)", p0, p1, p2, p3));
        }
    
        function log(bool p0, uint256 p1, string memory p2, string memory p3) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(bool,uint256,string,string)", p0, p1, p2, p3));
        }
    
        function log(bool p0, uint256 p1, string memory p2, bool p3) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(bool,uint256,string,bool)", p0, p1, p2, p3));
        }
    
        function log(bool p0, uint256 p1, string memory p2, address p3) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(bool,uint256,string,address)", p0, p1, p2, p3));
        }
    
        function log(bool p0, uint256 p1, bool p2, uint256 p3) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(bool,uint256,bool,uint256)", p0, p1, p2, p3));
        }
    
        function log(bool p0, uint256 p1, bool p2, string memory p3) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(bool,uint256,bool,string)", p0, p1, p2, p3));
        }
    
        function log(bool p0, uint256 p1, bool p2, bool p3) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(bool,uint256,bool,bool)", p0, p1, p2, p3));
        }
    
        function log(bool p0, uint256 p1, bool p2, address p3) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(bool,uint256,bool,address)", p0, p1, p2, p3));
        }
    
        function log(bool p0, uint256 p1, address p2, uint256 p3) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(bool,uint256,address,uint256)", p0, p1, p2, p3));
        }
    
        function log(bool p0, uint256 p1, address p2, string memory p3) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(bool,uint256,address,string)", p0, p1, p2, p3));
        }
    
        function log(bool p0, uint256 p1, address p2, bool p3) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(bool,uint256,address,bool)", p0, p1, p2, p3));
        }
    
        function log(bool p0, uint256 p1, address p2, address p3) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(bool,uint256,address,address)", p0, p1, p2, p3));
        }
    
        function log(bool p0, string memory p1, uint256 p2, uint256 p3) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(bool,string,uint256,uint256)", p0, p1, p2, p3));
        }
    
        function log(bool p0, string memory p1, uint256 p2, string memory p3) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(bool,string,uint256,string)", p0, p1, p2, p3));
        }
    
        function log(bool p0, string memory p1, uint256 p2, bool p3) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(bool,string,uint256,bool)", p0, p1, p2, p3));
        }
    
        function log(bool p0, string memory p1, uint256 p2, address p3) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(bool,string,uint256,address)", p0, p1, p2, p3));
        }
    
        function log(bool p0, string memory p1, string memory p2, uint256 p3) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(bool,string,string,uint256)", p0, p1, p2, p3));
        }
    
        function log(bool p0, string memory p1, string memory p2, string memory p3) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(bool,string,string,string)", p0, p1, p2, p3));
        }
    
        function log(bool p0, string memory p1, string memory p2, bool p3) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(bool,string,string,bool)", p0, p1, p2, p3));
        }
    
        function log(bool p0, string memory p1, string memory p2, address p3) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(bool,string,string,address)", p0, p1, p2, p3));
        }
    
        function log(bool p0, string memory p1, bool p2, uint256 p3) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(bool,string,bool,uint256)", p0, p1, p2, p3));
        }
    
        function log(bool p0, string memory p1, bool p2, string memory p3) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(bool,string,bool,string)", p0, p1, p2, p3));
        }
    
        function log(bool p0, string memory p1, bool p2, bool p3) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(bool,string,bool,bool)", p0, p1, p2, p3));
        }
    
        function log(bool p0, string memory p1, bool p2, address p3) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(bool,string,bool,address)", p0, p1, p2, p3));
        }
    
        function log(bool p0, string memory p1, address p2, uint256 p3) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(bool,string,address,uint256)", p0, p1, p2, p3));
        }
    
        function log(bool p0, string memory p1, address p2, string memory p3) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(bool,string,address,string)", p0, p1, p2, p3));
        }
    
        function log(bool p0, string memory p1, address p2, bool p3) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(bool,string,address,bool)", p0, p1, p2, p3));
        }
    
        function log(bool p0, string memory p1, address p2, address p3) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(bool,string,address,address)", p0, p1, p2, p3));
        }
    
        function log(bool p0, bool p1, uint256 p2, uint256 p3) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(bool,bool,uint256,uint256)", p0, p1, p2, p3));
        }
    
        function log(bool p0, bool p1, uint256 p2, string memory p3) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(bool,bool,uint256,string)", p0, p1, p2, p3));
        }
    
        function log(bool p0, bool p1, uint256 p2, bool p3) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(bool,bool,uint256,bool)", p0, p1, p2, p3));
        }
    
        function log(bool p0, bool p1, uint256 p2, address p3) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(bool,bool,uint256,address)", p0, p1, p2, p3));
        }
    
        function log(bool p0, bool p1, string memory p2, uint256 p3) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(bool,bool,string,uint256)", p0, p1, p2, p3));
        }
    
        function log(bool p0, bool p1, string memory p2, string memory p3) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(bool,bool,string,string)", p0, p1, p2, p3));
        }
    
        function log(bool p0, bool p1, string memory p2, bool p3) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(bool,bool,string,bool)", p0, p1, p2, p3));
        }
    
        function log(bool p0, bool p1, string memory p2, address p3) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(bool,bool,string,address)", p0, p1, p2, p3));
        }
    
        function log(bool p0, bool p1, bool p2, uint256 p3) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(bool,bool,bool,uint256)", p0, p1, p2, p3));
        }
    
        function log(bool p0, bool p1, bool p2, string memory p3) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(bool,bool,bool,string)", p0, p1, p2, p3));
        }
    
        function log(bool p0, bool p1, bool p2, bool p3) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(bool,bool,bool,bool)", p0, p1, p2, p3));
        }
    
        function log(bool p0, bool p1, bool p2, address p3) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(bool,bool,bool,address)", p0, p1, p2, p3));
        }
    
        function log(bool p0, bool p1, address p2, uint256 p3) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(bool,bool,address,uint256)", p0, p1, p2, p3));
        }
    
        function log(bool p0, bool p1, address p2, string memory p3) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(bool,bool,address,string)", p0, p1, p2, p3));
        }
    
        function log(bool p0, bool p1, address p2, bool p3) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(bool,bool,address,bool)", p0, p1, p2, p3));
        }
    
        function log(bool p0, bool p1, address p2, address p3) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(bool,bool,address,address)", p0, p1, p2, p3));
        }
    
        function log(bool p0, address p1, uint256 p2, uint256 p3) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(bool,address,uint256,uint256)", p0, p1, p2, p3));
        }
    
        function log(bool p0, address p1, uint256 p2, string memory p3) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(bool,address,uint256,string)", p0, p1, p2, p3));
        }
    
        function log(bool p0, address p1, uint256 p2, bool p3) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(bool,address,uint256,bool)", p0, p1, p2, p3));
        }
    
        function log(bool p0, address p1, uint256 p2, address p3) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(bool,address,uint256,address)", p0, p1, p2, p3));
        }
    
        function log(bool p0, address p1, string memory p2, uint256 p3) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(bool,address,string,uint256)", p0, p1, p2, p3));
        }
    
        function log(bool p0, address p1, string memory p2, string memory p3) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(bool,address,string,string)", p0, p1, p2, p3));
        }
    
        function log(bool p0, address p1, string memory p2, bool p3) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(bool,address,string,bool)", p0, p1, p2, p3));
        }
    
        function log(bool p0, address p1, string memory p2, address p3) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(bool,address,string,address)", p0, p1, p2, p3));
        }
    
        function log(bool p0, address p1, bool p2, uint256 p3) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(bool,address,bool,uint256)", p0, p1, p2, p3));
        }
    
        function log(bool p0, address p1, bool p2, string memory p3) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(bool,address,bool,string)", p0, p1, p2, p3));
        }
    
        function log(bool p0, address p1, bool p2, bool p3) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(bool,address,bool,bool)", p0, p1, p2, p3));
        }
    
        function log(bool p0, address p1, bool p2, address p3) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(bool,address,bool,address)", p0, p1, p2, p3));
        }
    
        function log(bool p0, address p1, address p2, uint256 p3) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(bool,address,address,uint256)", p0, p1, p2, p3));
        }
    
        function log(bool p0, address p1, address p2, string memory p3) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(bool,address,address,string)", p0, p1, p2, p3));
        }
    
        function log(bool p0, address p1, address p2, bool p3) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(bool,address,address,bool)", p0, p1, p2, p3));
        }
    
        function log(bool p0, address p1, address p2, address p3) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(bool,address,address,address)", p0, p1, p2, p3));
        }
    
        function log(address p0, uint256 p1, uint256 p2, uint256 p3) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(address,uint256,uint256,uint256)", p0, p1, p2, p3));
        }
    
        function log(address p0, uint256 p1, uint256 p2, string memory p3) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(address,uint256,uint256,string)", p0, p1, p2, p3));
        }
    
        function log(address p0, uint256 p1, uint256 p2, bool p3) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(address,uint256,uint256,bool)", p0, p1, p2, p3));
        }
    
        function log(address p0, uint256 p1, uint256 p2, address p3) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(address,uint256,uint256,address)", p0, p1, p2, p3));
        }
    
        function log(address p0, uint256 p1, string memory p2, uint256 p3) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(address,uint256,string,uint256)", p0, p1, p2, p3));
        }
    
        function log(address p0, uint256 p1, string memory p2, string memory p3) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(address,uint256,string,string)", p0, p1, p2, p3));
        }
    
        function log(address p0, uint256 p1, string memory p2, bool p3) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(address,uint256,string,bool)", p0, p1, p2, p3));
        }
    
        function log(address p0, uint256 p1, string memory p2, address p3) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(address,uint256,string,address)", p0, p1, p2, p3));
        }
    
        function log(address p0, uint256 p1, bool p2, uint256 p3) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(address,uint256,bool,uint256)", p0, p1, p2, p3));
        }
    
        function log(address p0, uint256 p1, bool p2, string memory p3) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(address,uint256,bool,string)", p0, p1, p2, p3));
        }
    
        function log(address p0, uint256 p1, bool p2, bool p3) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(address,uint256,bool,bool)", p0, p1, p2, p3));
        }
    
        function log(address p0, uint256 p1, bool p2, address p3) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(address,uint256,bool,address)", p0, p1, p2, p3));
        }
    
        function log(address p0, uint256 p1, address p2, uint256 p3) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(address,uint256,address,uint256)", p0, p1, p2, p3));
        }
    
        function log(address p0, uint256 p1, address p2, string memory p3) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(address,uint256,address,string)", p0, p1, p2, p3));
        }
    
        function log(address p0, uint256 p1, address p2, bool p3) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(address,uint256,address,bool)", p0, p1, p2, p3));
        }
    
        function log(address p0, uint256 p1, address p2, address p3) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(address,uint256,address,address)", p0, p1, p2, p3));
        }
    
        function log(address p0, string memory p1, uint256 p2, uint256 p3) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(address,string,uint256,uint256)", p0, p1, p2, p3));
        }
    
        function log(address p0, string memory p1, uint256 p2, string memory p3) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(address,string,uint256,string)", p0, p1, p2, p3));
        }
    
        function log(address p0, string memory p1, uint256 p2, bool p3) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(address,string,uint256,bool)", p0, p1, p2, p3));
        }
    
        function log(address p0, string memory p1, uint256 p2, address p3) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(address,string,uint256,address)", p0, p1, p2, p3));
        }
    
        function log(address p0, string memory p1, string memory p2, uint256 p3) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(address,string,string,uint256)", p0, p1, p2, p3));
        }
    
        function log(address p0, string memory p1, string memory p2, string memory p3) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(address,string,string,string)", p0, p1, p2, p3));
        }
    
        function log(address p0, string memory p1, string memory p2, bool p3) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(address,string,string,bool)", p0, p1, p2, p3));
        }
    
        function log(address p0, string memory p1, string memory p2, address p3) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(address,string,string,address)", p0, p1, p2, p3));
        }
    
        function log(address p0, string memory p1, bool p2, uint256 p3) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(address,string,bool,uint256)", p0, p1, p2, p3));
        }
    
        function log(address p0, string memory p1, bool p2, string memory p3) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(address,string,bool,string)", p0, p1, p2, p3));
        }
    
        function log(address p0, string memory p1, bool p2, bool p3) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(address,string,bool,bool)", p0, p1, p2, p3));
        }
    
        function log(address p0, string memory p1, bool p2, address p3) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(address,string,bool,address)", p0, p1, p2, p3));
        }
    
        function log(address p0, string memory p1, address p2, uint256 p3) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(address,string,address,uint256)", p0, p1, p2, p3));
        }
    
        function log(address p0, string memory p1, address p2, string memory p3) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(address,string,address,string)", p0, p1, p2, p3));
        }
    
        function log(address p0, string memory p1, address p2, bool p3) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(address,string,address,bool)", p0, p1, p2, p3));
        }
    
        function log(address p0, string memory p1, address p2, address p3) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(address,string,address,address)", p0, p1, p2, p3));
        }
    
        function log(address p0, bool p1, uint256 p2, uint256 p3) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(address,bool,uint256,uint256)", p0, p1, p2, p3));
        }
    
        function log(address p0, bool p1, uint256 p2, string memory p3) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(address,bool,uint256,string)", p0, p1, p2, p3));
        }
    
        function log(address p0, bool p1, uint256 p2, bool p3) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(address,bool,uint256,bool)", p0, p1, p2, p3));
        }
    
        function log(address p0, bool p1, uint256 p2, address p3) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(address,bool,uint256,address)", p0, p1, p2, p3));
        }
    
        function log(address p0, bool p1, string memory p2, uint256 p3) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(address,bool,string,uint256)", p0, p1, p2, p3));
        }
    
        function log(address p0, bool p1, string memory p2, string memory p3) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(address,bool,string,string)", p0, p1, p2, p3));
        }
    
        function log(address p0, bool p1, string memory p2, bool p3) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(address,bool,string,bool)", p0, p1, p2, p3));
        }
    
        function log(address p0, bool p1, string memory p2, address p3) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(address,bool,string,address)", p0, p1, p2, p3));
        }
    
        function log(address p0, bool p1, bool p2, uint256 p3) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(address,bool,bool,uint256)", p0, p1, p2, p3));
        }
    
        function log(address p0, bool p1, bool p2, string memory p3) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(address,bool,bool,string)", p0, p1, p2, p3));
        }
    
        function log(address p0, bool p1, bool p2, bool p3) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(address,bool,bool,bool)", p0, p1, p2, p3));
        }
    
        function log(address p0, bool p1, bool p2, address p3) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(address,bool,bool,address)", p0, p1, p2, p3));
        }
    
        function log(address p0, bool p1, address p2, uint256 p3) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(address,bool,address,uint256)", p0, p1, p2, p3));
        }
    
        function log(address p0, bool p1, address p2, string memory p3) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(address,bool,address,string)", p0, p1, p2, p3));
        }
    
        function log(address p0, bool p1, address p2, bool p3) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(address,bool,address,bool)", p0, p1, p2, p3));
        }
    
        function log(address p0, bool p1, address p2, address p3) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(address,bool,address,address)", p0, p1, p2, p3));
        }
    
        function log(address p0, address p1, uint256 p2, uint256 p3) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(address,address,uint256,uint256)", p0, p1, p2, p3));
        }
    
        function log(address p0, address p1, uint256 p2, string memory p3) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(address,address,uint256,string)", p0, p1, p2, p3));
        }
    
        function log(address p0, address p1, uint256 p2, bool p3) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(address,address,uint256,bool)", p0, p1, p2, p3));
        }
    
        function log(address p0, address p1, uint256 p2, address p3) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(address,address,uint256,address)", p0, p1, p2, p3));
        }
    
        function log(address p0, address p1, string memory p2, uint256 p3) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(address,address,string,uint256)", p0, p1, p2, p3));
        }
    
        function log(address p0, address p1, string memory p2, string memory p3) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(address,address,string,string)", p0, p1, p2, p3));
        }
    
        function log(address p0, address p1, string memory p2, bool p3) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(address,address,string,bool)", p0, p1, p2, p3));
        }
    
        function log(address p0, address p1, string memory p2, address p3) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(address,address,string,address)", p0, p1, p2, p3));
        }
    
        function log(address p0, address p1, bool p2, uint256 p3) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(address,address,bool,uint256)", p0, p1, p2, p3));
        }
    
        function log(address p0, address p1, bool p2, string memory p3) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(address,address,bool,string)", p0, p1, p2, p3));
        }
    
        function log(address p0, address p1, bool p2, bool p3) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(address,address,bool,bool)", p0, p1, p2, p3));
        }
    
        function log(address p0, address p1, bool p2, address p3) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(address,address,bool,address)", p0, p1, p2, p3));
        }
    
        function log(address p0, address p1, address p2, uint256 p3) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(address,address,address,uint256)", p0, p1, p2, p3));
        }
    
        function log(address p0, address p1, address p2, string memory p3) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(address,address,address,string)", p0, p1, p2, p3));
        }
    
        function log(address p0, address p1, address p2, bool p3) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(address,address,address,bool)", p0, p1, p2, p3));
        }
    
        function log(address p0, address p1, address p2, address p3) internal pure {
            _sendLogPayload(abi.encodeWithSignature("log(address,address,address,address)", p0, p1, p2, p3));
        }
    
    }
  • src/strategies/offer_forwarder/abstract/Forwarder.sol
    // SPDX-License-Identifier: MIT
    pragma solidity ^0.8.10;
    
    import {MangroveOffer, TransferLib} from "@mgv-strats/src/strategies/MangroveOffer.sol";
    import {IForwarder} from "@mgv-strats/src/strategies/interfaces/IForwarder.sol";
    import {RL, AbstractRouter} from "@mgv-strats/src/strategies/routers/abstract/AbstractRouter.sol";
    import {RouterProxyFactory, RouterProxy} from "@mgv-strats/src/strategies/routers/RouterProxyFactory.sol";
    import {IOfferLogic} from "@mgv-strats/src/strategies/interfaces/IOfferLogic.sol";
    import {MgvLib, IERC20, OLKey, OfferDetail, Global, Local} from "@mgv/src/core/MgvLib.sol";
    import {IMangrove} from "@mgv/src/IMangrove.sol";
    
    ///@title Class for maker contracts that forward offer makers instructions to Mangrove in a permissionless fashion.
    ///@notice Each offer posted via this contract are managed by their offer maker, not by this contract's admin.
    ///@notice This class implements IForwarder, which contains specific Forwarder logic functions in additions to IOfferLogic interface.
    
    abstract contract Forwarder is IForwarder, MangroveOffer {
      ///@notice approx of amount of gas units required to complete `__handleResidualProvision__` when evaluating penalty.
      uint constant GAS_APPROX = 2000;
    
      ///@notice the router factory contract that is used to deploy offer owner routers
      RouterProxyFactory public immutable ROUTER_FACTORY;
    
      ///@notice data associated to each offer published on Mangrove by this contract.
      ///@param owner address of the account that can manage (update or retract) the offer
      ///@param weiBalance fraction of `this` balance on Mangrove that is assigned to `owner`.
      ///@dev `OwnerData` packs into one word.
      struct OwnerData {
        address owner;
        uint96 weiBalance;
      }
    
      ///@notice Owner data mapping.
      ///@dev mapping is olKeyHash -> offerId -> OwnerData
      ///@dev 'ownerData[olKeyHash][offerId].owner == maker` if `maker` is offer owner of `offerId` in the `(out, in)` offer list.
      mapping(bytes32 olKeyHash => mapping(uint offerId => OwnerData)) internal ownerData;
    
      ///@notice modifier to enforce function caller to be offer owner
      modifier onlyOwner(bytes32 olKeyHash, uint offerId) {
        require(ownerData[olKeyHash][offerId].owner == msg.sender, "AccessControlled/Invalid");
        _;
      }
    
      ///@notice modifier to enforce function caller to be offer owner or MGV (for use in the offer logic)
      modifier mgvOrOwner(bytes32 olKeyHash, uint offerId) {
        if (msg.sender != address(MGV)) {
          require(ownerData[olKeyHash][offerId].owner == msg.sender, "AccessControlled/Invalid");
        }
        _;
      }
    
      ///@notice Forwarder constructor
      ///@param mgv the deployed Mangrove contract on which this contract will post offers.
      ///@param factory the router proxy factory contract -- cannot be 0x
      ///@param routerImplementation the deployed SmartRouter contract used to generate proxys for offer owners -- cannot be 0x
      constructor(IMangrove mgv, RouterProxyFactory factory, AbstractRouter routerImplementation)
        MangroveOffer(mgv, routerImplementation)
      {
        ROUTER_FACTORY = factory;
        require(address(factory) != address(0), "Forwarder/0xFactory");
        require(address(routerImplementation) != address(0), "Forwarder/0xRouter");
      }
    
      /// @notice grants managing (update/retract) rights on a particular offer.
      /// @param olKeyHash the hash of the offer list key.
      /// @param offerId the offer identifier in the offer list.
      /// @param owner the address of the offer maker.
      /// @param leftover the fraction of `msg.value` that is not locked in the offer provision due to rounding error (see `_newOffer`).
      function addOwner(bytes32 olKeyHash, uint offerId, address owner, uint leftover) internal {
        ownerData[olKeyHash][offerId] = OwnerData({owner: owner, weiBalance: uint96(leftover)});
        emit NewOwnedOffer(olKeyHash, offerId, owner);
      }
    
      /// @notice computes the maximum `gasprice` that can be covered by the amount of provision given in argument.
      /// @param gasreq the gas required by the offer
      /// @param provision the amount of native token one wishes to use, to provision the offer on Mangrove.
      /// @param offerGasbase Mangrove's offer_gasbase.
      /// @return gasprice the gas price that is covered by `provision` - `leftover`.
      /// @return leftover the sub amount of `provision` that is not used to provision the offer.
      /// @dev the returned gasprice is slightly lower than the real gasprice that the provision can cover because of the rounding error due to division
      function deriveGasprice(uint gasreq, uint provision, uint offerGasbase)
        internal
        pure
        returns (uint gasprice, uint leftover)
      {
        unchecked {
          uint num = (offerGasbase + gasreq) * 1e6;
          // pre-check to avoid underflow since 0 is interpreted as "use Mangrove's gasprice"
          require(provision >= num, "mgv/insufficientProvision");
          // Gasprice is eventually a uint26, so too much provision would yield a gasprice overflow
          // Reverting here with a clearer reason
          require(provision < ((1 << 26) - 1) * num, "Forwarder/provisionTooHigh");
          gasprice = provision / num;
    
          // computing amount of native tokens that are not going to be locked on Mangrove
          // this amount should still be recoverable by offer maker when retracting the offer
          leftover = provision - (gasprice * num);
        }
      }
    
      /// @inheritdoc IForwarder
      function ownerOf(bytes32 olKeyHash, uint offerId) public view override returns (address owner) {
        owner = ownerData[olKeyHash][offerId].owner;
        require(owner != address(0), "Forwarder/unknownOffer");
      }
    
      /// @inheritdoc IOfferLogic
      function router(address fundOwner) public view override returns (AbstractRouter) {
        return AbstractRouter(address(ROUTER_FACTORY.computeProxyAddress(fundOwner, ROUTER_IMPLEMENTATION)));
      }
    
      ///@notice approves a router proxy for transfering funds from this contract
      ///@param token the IERC20 whose approval is required
      ///@param proxy the router proxy contract
      ///@param amount the approval quantity.
      function _approveProxy(IERC20 token, RouterProxy proxy, uint amount) internal {
        require(TransferLib.approveToken(token, address(proxy), amount), "MangroveOffer/ProxyApprovaFailed");
      }
    
      /// @notice Derives the gas price for the new offer and verifies it against the global configuration.
      /// @param args function's arguments in memory
      /// @return gasprice the gas price that is covered by `provision` - `leftover`.
      /// @return leftover the sub amount of `provision` that is not used to provision the offer.
      /// @dev the returned gasprice is slightly lower than the real gasprice that the provision can cover because of the rounding error due to division
      function deriveAndCheckGasprice(OfferArgs memory args) internal view returns (uint gasprice, uint leftover) {
        (Global global, Local local) = MGV.config(args.olKey);
        // computing max `gasprice` such that `offData.fund` covers `offData.gasreq` at `gasprice`
        (gasprice, leftover) = deriveGasprice(args.gasreq, args.fund, local.offer_gasbase());
        // mangrove will take max(`mko.gasprice`, `global.gasprice`)
        // if `mko.gasprice < global.gasprice` Mangrove will use available provision of this contract to provision the offer
        // this would potentially take native tokens that have been released after some offer managed by this contract have failed
        // so one needs to make sure here that only provision of this call will be used to provision the offer on mangrove
        require(gasprice >= global.gasprice(), "mgv/insufficientProvision");
      }
    
      /// @notice Inserts a new offer on a Mangrove Offer List
      /// @dev If inside a hook, one should call `_newOffer` to create a new offer and not directly `MGV.newOffer` to make sure one is correctly dealing with:
      /// * offer ownership
      /// * offer provisions and gasprice
      /// * offer owner has a router proxy for when her offer is taken
      /// @param args function's arguments in memory
      /// @param owner the address of the offer owner
      /// @return offerId the identifier of the new offer on the offer list. Can be 0 if posting was rejected by Mangrove and `args.noRevert` is `true`.
      /// @return status the status of the new offer on Mangrove if the call has not reverted. It may be NEW_OFFER_SUCCESS or Mangrove's revert reason if `args.noRevert` was set to true.
      /// Forwarder logic does not manage user funds on Mangrove, as a consequence:
      /// An offer maker's redeemable provisions on Mangrove is just the sum $S_locked(maker)$ of locked provision in all live offers it owns
      /// plus the sum $S_free(maker)$ of `weiBalance`'s in all dead offers it owns (see `OwnerData.weiBalance`).
      /// Notice $\sum_i S_free(maker_i)$ <= MGV.balanceOf(address(this))`.
      /// Any fund of an offer maker on Mangrove that is either not locked on Mangrove or stored in the `OwnerData` free wei's is thus not recoverable by the offer maker (although it is admin recoverable).
      /// Therefore we need to make sure that all `msg.value` is either used to provision the offer at `gasprice` or stored in the offer data under `weiBalance`.
      /// To do so, we do not let offer maker fix a gasprice. Rather we derive the gasprice based on `msg.value`.
      /// Because of rounding errors in `deriveGasprice` a small amount of WEIs will accumulate in mangrove's balance of `this` contract
      /// We assign this leftover to the corresponding `weiBalance` of `OwnerData`.
      function _newOffer(OfferArgs memory args, address owner) internal virtual returns (uint offerId, bytes32 status) {
        // convention for default gasreq value
        (uint gasprice, uint leftover) = deriveAndCheckGasprice(args);
    
        // the call below cannot revert for lack of provision (by design)
        // it may still revert if `args.fund` yields a gasprice that is too high (Mangrove's gasprice must hold on 26 bits)
        // or if `args.gives` is below density (dust)
        try MGV.newOfferByTick{value: args.fund}(args.olKey, args.tick, args.gives, args.gasreq, gasprice) returns (
          uint offerId_
        ) {
          // assign `offerId_` to caller
          addOwner(args.olKey.hash(), offerId_, owner, leftover);
          offerId = offerId_;
          status = NEW_OFFER_SUCCESS;
        } catch Error(string memory reason) {
          /// letting revert bubble up unless `noRevert` is positioned.
          require(args.noRevert, reason);
          status = bytes32(bytes(reason));
        }
      }
    
      struct UpdateOfferVars {
        uint leftover;
        Global global;
        Local local;
        OfferDetail offerDetail;
      }
    
      ///@inheritdoc MangroveOffer
      ///@notice Internal `updateOffer`, using arguments and variables on memory to avoid stack too deep.
      ///@return reason Either REPOST_SUCCESS or Mangrove's revert reason if update was rejected by Mangrove and `args.noRevert` is `true`.
      function _updateOffer(OfferArgs memory args, uint offerId) internal virtual override returns (bytes32 reason) {
        unchecked {
          UpdateOfferVars memory vars;
          (vars.global, vars.local) = MGV.config(args.olKey);
          vars.offerDetail = MGV.offerDetails(args.olKey, offerId);
    
          uint old_gasreq = vars.offerDetail.gasreq();
          // re-deriving gasprice only if necessary
          if (
            args.fund > 0 // user adds more provision
              || args.gasreq != old_gasreq // offer's `gasreq` is modified
              || vars.offerDetail.offer_gasbase() != vars.local.offer_gasbase() // governance has updated `offer_gasbase`
          ) {
            // adding current locked provision to funds (0 if offer is deprovisioned)
            uint locked_funds = vars.offerDetail.gasprice() * 1e6 * (old_gasreq + vars.offerDetail.offer_gasbase());
            // note that if `args.gasreq < old_gasreq` then offer gasprice will increase (even if `args.fund == 0`) to match the incurred excess of locked provision
            (args.gasprice, vars.leftover) =
              deriveGasprice(args.gasreq, args.fund + locked_funds, vars.local.offer_gasbase());
    
            // leftover can be safely cast to uint96 since it's a rounding error
            // adding `leftover` to potential previous value since it was not included in args.fund
            ownerData[args.olKey.hash()][offerId].weiBalance += uint96(vars.leftover);
          } else {
            // no funds are added so we keep old gasprice
            args.gasprice = vars.offerDetail.gasprice();
          }
          // if `args.fund` is too low, offer gasprice might be below mangrove's gasprice
          // Mangrove will then take its own gasprice for the offer and would possibly tap into `this` contract's balance to cover for the missing provision
          if (args.gasprice < vars.global.gasprice()) {
            if (args.noRevert) {
              return "mgv/insufficientProvision";
            } else {
              revert("mgv/insufficientProvision");
            }
          }
    
          try MGV.updateOfferByTick{value: args.fund}(
            args.olKey, args.tick, args.gives, args.gasreq, args.gasprice, offerId
          ) {
            return REPOST_SUCCESS;
          } catch Error(string memory _reason) {
            require(args.noRevert, _reason);
            return bytes32(bytes(_reason));
          }
        }
      }
    
      ///@inheritdoc IOfferLogic
      function provisionOf(OLKey memory olKey, uint offerId) external view override returns (uint provision) {
        provision = _provisionOf(olKey, offerId);
        unchecked {
          provision += ownerData[olKey.hash()][offerId].weiBalance;
        }
      }
    
      ///@notice Retracts an offer from an Offer List of Mangrove.
      ///@param olKey the offer list key.
      ///@param offerId the identifier of the offer in the offer list
      ///@param deprovision if set to `true` if offer owner wishes to redeem the offer's provision.
      ///@param noRevert whether mangrove revert should bubble up or be caught
      ///@return freeWei the amount of native tokens (in WEI) that have been retrieved by retracting the offer.
      ///@return status a bytes32 containing the revert reason if mangrove reverted and `noRevert` is true.
      ///@dev An offer that is retracted without `deprovision` is retracted from the offer list, but still has its provisions locked by Mangrove.
      ///@dev Calling this function, with the `deprovision` flag, should be accompanied by a native token transfer to offer owner in order not to leave funds on this balance.
      function _retractOffer(OLKey memory olKey, uint offerId, bool noRevert, bool deprovision)
        internal
        returns (uint freeWei, bytes32 status)
      {
        OwnerData storage od = ownerData[olKey.hash()][offerId];
        freeWei = deprovision ? od.weiBalance : 0;
    
        try MGV.retractOffer(olKey, offerId, deprovision) returns (uint weis) {
          // We add `weis` credited to the contract to `freeWei` amount
          // They were the locked native token provision of the offer that has been retracted
          freeWei += weis;
        } catch Error(string memory reason) {
          require(noRevert, reason);
          status = bytes32(bytes(reason));
        }
        // if `deprovision` is set to false, `freeWei` is 0 because `retractOffer` will return 0
        if (freeWei > 0) {
          // pulling free wei from Mangrove to `this`
          require(MGV.withdraw(freeWei), "Forwarder/withdrawFail");
          // resetting pending returned provision
          // calling code must now assign freeWei to owner
          od.weiBalance = 0;
        }
      }
    
      ///@dev put received inbound tokens on offer maker's reserve during `makerExecute`
      /// if nothing is done at that stage then it could still be done during `makerPosthook`.
      /// However one would then need to pay attention to the following fact:
      /// if `order.olKey.inbound_tkn` is not pushed to reserve during `makerExecute`, in the posthook of this offer execution, the `order.olKey.inbound_tkn` balance of this contract would then contain
      /// the sum of all payments of offers managed by `this` that are in a better position in the offer list (because posthook is called in the call stack order).
      ///@inheritdoc MangroveOffer
      function __put__(uint amount, MgvLib.SingleOrder calldata order) internal virtual override returns (uint) {
        bytes32 olKeyHash = order.olKey.hash();
        address owner = ownerOf(olKeyHash, order.offerId);
        AbstractRouter ownerRouter = router(owner);
        // exact transfer approval in order to be able to push funds to the router
        _approveProxy(IERC20(order.olKey.inbound_tkn), RouterProxy(payable(address(ownerRouter))), amount);
    
        RL.RoutingOrder memory pushOrder = RL.RoutingOrder({
          olKeyHash: olKeyHash,
          offerId: order.offerId,
          token: IERC20(order.olKey.inbound_tkn),
          fundOwner: owner
        });
        return amount - ownerRouter.push(pushOrder, amount);
      }
    
      ///@dev get outbound tokens from offer owner reserve
      ///@inheritdoc MangroveOffer
      function __get__(uint amount, MgvLib.SingleOrder calldata order) internal virtual override returns (uint) {
        bytes32 olKeyHash = order.olKey.hash();
        address owner = ownerOf(olKeyHash, order.offerId);
        AbstractRouter ownerRouter = router(owner);
        // telling proxy one is requiring `amount` of `outTkn` for `owner`.
        // because `pull` is strict, `pulled <= amount` (cannot be greater)
        // we do not check local balance here because multi user contracts do not keep more balance than what has been pulled
        // note we assume here that:
        // 1. proxy has been previously deployed
        // 2. offer owner has approved proxy for outbound token transfer (this may be done prior to proxy deployment thanks to deterministic address of the proxy)
        return amount
          - ownerRouter.pull(
            RL.RoutingOrder({
              olKeyHash: olKeyHash,
              token: IERC20(order.olKey.outbound_tkn),
              offerId: order.offerId,
              fundOwner: owner
            }),
            amount,
            true
          ); // this will make trade fail if `missing > pulled` and this `get` is not nested in another `get` in descendant of this class.
      }
    
      ///@dev if offer failed to execute, Mangrove retracts and deprovisions it after the posthook call.
      /// As a consequence if this hook is reached, `this` balance on Mangrove *will* increase, after the posthook,
      /// of some amount $n$ of native tokens.
      /// Note we cannot assume the whole balance of `this` on Mangrove belongs to the offer owner since other offers may have failed during the market order.
      /// So we evaluate here an underapproximation $~n$ in order to credit the offer maker in a pull based manner:
      /// failed offer owner can retrieve $~n$ by calling `retractOffer` on the failed offer.
      /// because $~n<n$ a small amount of WEIs will accumulate on the balance of `this` on Mangrove over time.
      /// Note that these WEIs are not burnt since they can be admin retrieved using `withdrawFromMangrove`.
      /// @inheritdoc MangroveOffer
      function __handleResidualProvision__(MgvLib.SingleOrder calldata order) internal virtual override {
        mapping(uint => OwnerData) storage semiBookOwnerData = ownerData[order.olKey.hash()];
        // NB if several offers of `this` contract have failed during the market order, the balance of this contract on Mangrove will contain cumulated free provision
    
        // computing an under approximation of returned provision because of this offer's failure
        uint gasreq = order.offerDetail.gasreq();
        uint provision = 1e6 * order.offerDetail.gasprice() * (gasreq + order.offerDetail.offer_gasbase());
    
        // gasUsed estimate to complete posthook and penalize this offer is ~1750 (empirical estimate)
        uint gasprice = order.global.gasprice() * 1e6;
        uint approxGasConsumption = gasreq + GAS_APPROX + order.local.offer_gasbase();
        uint approxBounty = (approxGasConsumption - gasleft()) * gasprice;
        uint approxReturnedProvision = approxBounty >= provision ? 0 : provision - approxBounty;
    
        // storing the portion of this contract's balance on Mangrove that should be attributed back to the failing offer's owner
        // those free WEIs can be retrieved by offer owner, by calling `retractOffer` with the `deprovision` flag.
        semiBookOwnerData[order.offerId].weiBalance += uint96(approxReturnedProvision);
      }
    }
    
  • node_modules/@mangrovedao/mangrove-core/lib/core/LocalExtra.sol
    // SPDX-License-Identifier: MIT
    pragma solidity ^0.8.17;
    
    import {Bin,TickTreeLib,Field} from "@mgv/lib/core/TickTreeLib.sol";
    import {Density, DensityLib} from "@mgv/lib/core/DensityLib.sol";
    import {Local,LocalUnpacked,LocalLib} from "@mgv/src/preprocessed/Local.post.sol";
    
    
    
    // cleanup-mask: 0s at location of fields to hide from maker, 1s elsewhere
    uint constant HIDE_FIELDS_FROM_MAKER_MASK = ~(LocalLib.binPosInLeaf_mask_inv | LocalLib.level3_mask_inv | LocalLib.level2_mask_inv | LocalLib.level1_mask_inv | LocalLib.root_mask_inv | LocalLib.last_mask_inv);
    
    /* Extra functions for local */
    library LocalExtra {
    
      /* Sets the density in fixed-point 96X32 format (it is stored in floating-point, see `DensityLib` for more information). */
      function densityFrom96X32(Local local, uint density96X32) internal pure returns (Local) { unchecked {
        return local.density(DensityLib.from96X32(density96X32));
      }}
    
      /* Returns the gasbase in gas (it is stored in kilogas) */
      function offer_gasbase(Local local) internal pure returns (uint) { unchecked {
        return local.kilo_offer_gasbase() * 1e3;
      }}
    
      /* Sets the gasbase in gas (it is stored in kilogas) */
      function offer_gasbase(Local local,uint val) internal pure returns (Local) { unchecked {
        return local.kilo_offer_gasbase(val/1e3);
      }}
    
      /* Returns the bin that contains the best offer in \`local\`'s offer list */
      function bestBin(Local local) internal pure returns (Bin) { unchecked {
        return TickTreeLib.bestBinFromLocal(local);
      }}
    
      /* Erases field that give information about the current structure of the offer list. */
      function clearFieldsForMaker(Local local) internal pure returns (Local) { unchecked {
        return Local.wrap(
          Local.unwrap(local)
          & HIDE_FIELDS_FROM_MAKER_MASK);
      }}
    }
    
    /* Extra functions for the struct version of local */
    library LocalUnpackedExtra {
      /* Sets the density in fixed-point 96X32 format (it is stored in floating-point, see `DensityLib` for more information). */
      function densityFrom96X32(LocalUnpacked memory local, uint density96X32) internal pure { unchecked {
        local.density = DensityLib.from96X32(density96X32);
      }}
    
      /* Returns the gasbase in gas (it is stored in kilogas) */
      function offer_gasbase(LocalUnpacked memory local) internal pure returns (uint) { unchecked {
        return local.kilo_offer_gasbase * 1e3;
      }}
    
      /* Sets the gasbase in gas (it is stored in kilogas) */
      function offer_gasbase(LocalUnpacked memory local,uint val) internal pure { unchecked {
        local.kilo_offer_gasbase = val/1e3;
      }}
    
      /* Returns the bin that contains the best offer in \`local\`'s offer list */
      function bestBin(LocalUnpacked memory local) internal pure returns (Bin) { unchecked {
        return TickTreeLib.bestBinFromBranch(local.binPosInLeaf,local.level3,local.level2,local.level1,local.root);
      }}
    }
  • node_modules/@mangrovedao/mangrove-core/lib/core/OfferExtra.sol
    // SPDX-License-Identifier: MIT
    pragma solidity ^0.8.17;
    
    import "@mgv/lib/core/TickTreeLib.sol";
    import "@mgv/lib/core/TickLib.sol";
    import {Offer,OfferUnpacked,OfferLib} from "@mgv/src/preprocessed/Offer.post.sol";
    
    
    /* cleanup-mask: 0s at location of fields to hide from maker, 1s elsewhere */
    uint constant HIDE_FIELDS_FROM_MAKER_MASK = ~(OfferLib.prev_mask_inv | OfferLib.next_mask_inv);
    
    /* Extra functions for the offer */
    library OfferExtra {
      /* Compute wants from tick and gives */
      function wants(Offer offer) internal pure returns (uint) {
        return offer.tick().inboundFromOutboundUp(offer.gives());
      }
    
      /* Sugar to test offer liveness */
      function isLive(Offer offer) internal pure returns (bool resp) {
        uint gives = offer.gives();
        assembly ("memory-safe") {
          resp := iszero(iszero(gives))
        }
      }
    
      /* Get the bin where the offer is stored given an offer list that has `tickSpacing` */
      function bin(Offer offer, uint tickSpacing) internal pure returns (Bin) {
        return offer.tick().nearestBin(tickSpacing);
      }
    
      /* Removes prev/next pointers from an offer before sending it to the maker.  Ensures that the maker has no information about the state of the book when it gets called. */
      function clearFieldsForMaker(Offer offer) internal pure returns (Offer) {
        unchecked {
          return Offer.wrap(
            Offer.unwrap(offer)
            & HIDE_FIELDS_FROM_MAKER_MASK);
        }
      }
    }
    
    /* Extra functions for the struct version of the offer */
    library OfferUnpackedExtra {
      /* Compute wants from tick and gives */
      function wants(OfferUnpacked memory offer) internal pure returns (uint) {
        return offer.tick.inboundFromOutboundUp(offer.gives);
      }
    
      /* Sugar to test offer liveness */
      function isLive(OfferUnpacked memory offer) internal pure returns (bool resp) {
        uint gives = offer.gives;
        assembly ("memory-safe") {
          resp := iszero(iszero(gives))
        }
      }
    
      /* Removes prev/next pointers from an offer before sending it to the maker; Ensures that the maker has no information about the state of the book when it gets called. */
      function bin(OfferUnpacked memory offer, uint tickSpacing) internal pure returns (Bin) {
        return offer.tick.nearestBin(tickSpacing);
      }
    
    }
  • src/strategies/interfaces/IForwarder.sol
    // SPDX-License-Identifier: MIT
    pragma solidity >=0.8.10;
    
    import {RL, AbstractRouter} from "@mgv-strats/src/strategies/routers/abstract/AbstractRouter.sol";
    
    ///@title IForwarder
    ///@notice Interface for contracts that manage liquidity on Mangrove on behalf of multiple offer makers
    interface IForwarder {
      ///@notice Logging new offer owner
      ///@param olKeyHash the hash of the offer list key. This is indexed to allow RPC calls to filter on it.
      ///@param offerId the Mangrove offer id. This is indexed to allow RPC calls to filter on it.
      ///@param owner the offer maker that can manage the offer. It is indexed to allow RPC calls to filter on it.
      ///@notice By emitting this data, an indexer will be able to keep track of the real owner of an offer
      event NewOwnedOffer(bytes32 indexed olKeyHash, uint indexed offerId, address indexed owner);
    
      /// @notice view on an offer owner.
      /// @param olKeyHash the hash of the offer list key.
      /// @param offerId the offer identifier on the offer list.
      /// @return owner the offer maker that can manage the offer.
      /// @dev `ownerOf(in,out,id)` is equivalent to `offerOwners(in, out, [id])` but more gas efficient.
      function ownerOf(bytes32 olKeyHash, uint offerId) external view returns (address owner);
    }
    
  • node_modules/@mangrovedao/mangrove-core/lib/core/DensityLib.sol
    // SPDX-License-Identifier: MIT
    pragma solidity ^0.8.17;
    
    import {Field} from "@mgv/lib/core/TickTreeLib.sol";
    import {ONES} from "@mgv/lib/core/Constants.sol";
    import {BitLib} from "@mgv/lib/core/BitLib.sol";
    
    /*
    
    The density of a semibook is the number of outbound tokens per gas required. An offer must a respect a semibook's density.
    
    Density can be < 1.
    
    The density of a semibook is stored as a 9 bits float. For convenience, governance functions read density as a 96.32 fixed point number. The functions below give conversion utilities between the two formats
    
    As a guideline, fixed-point densities should be uints and should use hungarian notation (for instance `uint density96X32`). Floating-point densities should use the Density user-defined type.
    
    The float <-> fixed conversion is format agnostic but the expectation is that fixed points are 96x32, and floats are 2-bit mantissa, 7bit exponent with bias 32. 
    
    The encoding is nonstandard so the code can be simpler.
    
    There are no subnormal floats in this encoding, `[exp][mantissa]` means:
    
    ```
    if exp is 0 or 1:   0bmantissa   * 2^-32
    otherwise:          0b1.mantissa * 2^(exp-32)
    ```
    
    so the small values have some holes:
    
    ```
      coeff   exponent  available    |   coeff   exponent  available
      --------------------------------------------------------------
      0b0.00                         |  0b1.10     -31
      0b1.00     -32                 |  0b1.11     -31        no
      0b1.01     -32        no       |  0b1.00     -30
      0b1.10     -32        no       |  0b1.01     -30
      0b1.11     -32        no       |  0b1.10     -30
      0b1.00     -31                 |  0b1.11     -30
      0b1.01     -31        no       |  0b1.00     -29
    ```
    */
    
    type Density is uint;
    using DensityLib for Density global;
    
    library DensityLib {
      /* Numbers in this file assume that density is 9 bits in structs.ts */
      uint constant BITS = 9; // must match structs.ts
      uint constant MANTISSA_BITS = 2;
      uint constant SUBNORMAL_LIMIT = ~(ONES << (MANTISSA_BITS+1));
      uint constant MANTISSA_MASK = ~(ONES << MANTISSA_BITS);
      uint constant MASK = ~(ONES << BITS);
      uint constant MANTISSA_INTEGER = 1 << MANTISSA_BITS;
      uint constant EXPONENT_BITS = BITS - MANTISSA_BITS;
    
      function eq(Density a, Density b) internal pure returns (bool) { unchecked {
        return Density.unwrap(a) == Density.unwrap(b);
      }}
    
      /* Check the size of a fixed-point formatted density */
      function checkDensity96X32(uint density96X32) internal pure returns (bool) { unchecked {
        return density96X32 < (1<<(96+32));
      }}
    
      /* fixed-point -> float conversion */
      /* Warning: no bit cleaning (expected to be done by Local's code), no checking that the input is on 128 bits. */
      /* floats with `[exp=1]` are not in the image of fromFixed. They are considered noncanonical. */
      function from96X32(uint density96X32) internal pure returns (Density) { unchecked {
        if (density96X32 <= MANTISSA_MASK) {
          return Density.wrap(density96X32);
        }
        // invariant: `exp >= 2` (so not 0)
        uint exp = BitLib.fls(density96X32);
        return make(density96X32 >> (exp-MANTISSA_BITS),exp);
      }}
    
      /* float -> fixed-point conversion */
      function to96X32(Density density) internal pure returns (uint) { unchecked {
        /* also accepts floats not generated by fixedToFloat, i.e. with exp=1 */
        if (Density.unwrap(density) <= SUBNORMAL_LIMIT) {
          return Density.unwrap(density) & MANTISSA_MASK;
        }
        /* assumes exp is on the right number of bits */
        // invariant: `exp >= 2`
        uint shift = (Density.unwrap(density) >> MANTISSA_BITS) - MANTISSA_BITS;
        return ((Density.unwrap(density) & MANTISSA_MASK) | MANTISSA_INTEGER) << shift;
      }}
    
      function mantissa(Density density) internal pure returns (uint) { unchecked {
        return Density.unwrap(density) & MANTISSA_MASK;
      }}
    
      function exponent(Density density) internal pure returns (uint) { unchecked {
        return Density.unwrap(density) >> MANTISSA_BITS;
      }}
    
      /* Make a float from a mantissa and an exponent. May make a noncanonical float. */
      /* Warning: no checks */
      function make(uint _mantissa, uint _exponent) internal pure returns (Density) { unchecked {
        return Density.wrap((_exponent << MANTISSA_BITS) | (_mantissa & MANTISSA_MASK));
      }}
    
      /* None of the functions below will overflow if m is 96bit wide.
         Density being a 96.32 number is useful because:
         - Most of its range is representable with the 9-bits float format chosen
         - It does not overflow when multiplied with a 96bit number, which is the size chosen to represent token amounts in Mangrove.
         - Densities below `2^-32` need `> 4e9` gasreq to force gives > 0, which is not realistic
      */
      /* Multiply the density with m, rounded towards zero. */
      /* May overflow if `|m|>9` */
      function multiply(Density density, uint m) internal pure returns (uint) { unchecked {
        return (m * density.to96X32())>>32;
      }}
      /* Multiply the density with m, rounded towards +infinity. */
      /* May overflow if `|m|>96` */
      function multiplyUp(Density density, uint m) internal pure returns (uint) { unchecked {
        uint part = m * density.to96X32();
        return (part >> 32) + (part%(2<<32) == 0 ? 0 : 1);
      }}
    
      /* Convenience function: get a fixed-point density from the given parameters. Computes the price of gas in outbound tokens (base units), then multiplies by cover_factor. */
      /* Warning: you must multiply input usd prices by 100 */
      /* not supposed to be gas optimized */
      function paramsTo96X32(
        uint outbound_decimals, 
        uint gasprice_in_Mwei, 
        uint eth_in_centiusd, 
        uint outbound_display_in_centiusd, 
        uint cover_factor
      ) internal pure returns (uint) {
        // Do not use unchecked here
        require(uint8(outbound_decimals) == outbound_decimals,"DensityLib/fixedFromParams1/decimals/wrong");
        uint num = cover_factor * gasprice_in_Mwei * (10**outbound_decimals) * eth_in_centiusd;
        // use * instead of << to trigger overflow check
        return (num * (1 << 32)) / (outbound_display_in_centiusd * 1e12);
      }
    
      /* Version with token in Mwei instead of usd */
      function paramsTo96X32(
        uint outbound_decimals, 
        uint gasprice_in_Mwei, 
        uint outbound_display_in_Mwei, 
        uint cover_factor
      ) internal pure returns (uint) {
        /* **Do not** use unchecked here. */
        require(uint8(outbound_decimals) == outbound_decimals,"DensityLib/fixedFromParams2/decimals/wrong");
        uint num = cover_factor * gasprice_in_Mwei * (10**outbound_decimals);
        /* use `*` instead of `<<` to trigger overflow check */
        return (num * (1 << 32)) / outbound_display_in_Mwei;
      }
    }
  • node_modules/@mangrovedao/mangrove-core/lib/core/TickLib.sol
    // SPDX-License-Identifier: MIT
    pragma solidity ^0.8.17;
    
    import {Bin} from "@mgv/lib/core/TickTreeLib.sol";
    import "@mgv/lib/core/BitLib.sol";
    import "@mgv/lib/core/Constants.sol";
    
    /* This file is inspired by Uniswap's approach to ticks, with the following notable changes:
    - directly compute ticks base 1.0001 (not base `sqrt(1.0001)`)
    - directly compute ratios (not `sqrt(ratio)`) (simpler code elsewhere when dealing with actual ratios and logs of ratios)
    - ratios are floating-point numbers, not fixed-point numbers (increases precision when computing amounts)
    */
    
    
    /* # TickLib
    
    The `TickLib` file contains tick math-related code and utilities for manipulating ticks. It also holds functions related to ratios, which are represented as (mantissa,exponent) pairs. */
    
    /* Globally enable `tick.method(...)` */
    type Tick is int;
    using TickLib for Tick global;
    
    library TickLib {
    
      function inRange(Tick tick) internal pure returns (bool) {
        return Tick.unwrap(tick) >= MIN_TICK && Tick.unwrap(tick) <= MAX_TICK;
      }
    
      function eq(Tick tick1, Tick tick2) internal pure returns (bool) {
        unchecked {
          return Tick.unwrap(tick1) == Tick.unwrap(tick2);
        }
      }
    
      /* Returns the nearest, higher bin to the given `tick` at the given `tickSpacing`
        
        We do not force ticks to fit the tickSpacing (aka `tick%tickSpacing==0`). Ratios are rounded up that the maker is always paid at least what they asked for
      */
      function nearestBin(Tick tick, uint tickSpacing) internal pure returns (Bin bin) {
        unchecked {
          // By default division rounds towards 0. Since `smod` is signed we get the sign of `tick` and `tick%tickSpacing` in a single instruction.
          assembly("memory-safe") {
            bin := sdiv(tick,tickSpacing)
            bin := add(bin,sgt(smod(tick,tickSpacing),0))
          }
        }
      }
    
      /* ## Conversion functions */
    
      /* ### (inbound,tick) → outbound 
      `inboundFromOutbound[Up]` converts an outbound amount (i.e. an `offer.gives` or a `takerWants`), to an inbound amount, following the price induced by `tick`. There's a rounding-up and a rounding-down variant.
    
      `outboundAmt` should not exceed 127 bits.
      */
      function inboundFromOutbound(Tick tick, uint outboundAmt) internal pure returns (uint) {
        (uint sig, uint exp) = ratioFromTick(tick);
        return (sig * outboundAmt) >> exp;
      }
    
      function inboundFromOutboundUp(Tick tick, uint outboundAmt) internal pure returns (uint) {
        unchecked {
          (uint sig, uint exp) = ratioFromTick(tick);
          return divExpUp(sig*outboundAmt,exp);
        }
      }
    
      /* ### (outbound,tick) → inbound */
      /* `outboundFromInbound[Up]` converts an inbound amount (i.e. an `offer.wants` or a `takerGives`), to an outbound amount, following the price induced by `tick`. There's a rounding-up and a rounding-down variant.
    
      `inboundAmt` should not exceed 127 bits.
      */
      function outboundFromInbound(Tick tick, uint inboundAmt) internal pure returns (uint) {
        (uint sig, uint exp) = ratioFromTick(Tick.wrap(-Tick.unwrap(tick)));
        return (sig * inboundAmt) >> exp;
      }
    
      function outboundFromInboundUp(Tick tick, uint inboundAmt) internal pure returns (uint) {
        unchecked {
          (uint sig, uint exp) = ratioFromTick(Tick.wrap(-Tick.unwrap(tick)));
          return divExpUp(sig*inboundAmt,exp);
        }
      }
    
      /* ## Ratio representation
    
      Ratios are represented as a (mantissa,exponent) pair which represents the number `mantissa * 2**-exponent`.
    
      The exponent is negated so that, for ratios in the accepted range, the exponent is `>= 0`. This simplifies the code.
    
      Floats are normalized so that the mantissa uses exactly 128 bits. It enables easy comparison between floats and ensures they can be multiplied by amounts without overflow.
    
      The accepted ratio range is between `ratioFromTick(MIN_TICK)` and `ratioFromTick(MAX_TICK)` (inclusive).
      */
      
    
      /* ### (inbound,outbound) → ratio */
    
      /* `ratioFromVolumes` converts a pair of (inbound,outbound) volumes to a floating-point, normalized ratio. It rounds down.
      * `outboundAmt = 0` has a special meaning and the highest possible price will be returned.
      * `inboundAmt = 0` has a special meaning if `outboundAmt != 0` and the lowest possible price will be returned.
      */
      function ratioFromVolumes(uint inboundAmt, uint outboundAmt) internal pure returns (uint mantissa, uint exp) {
        unchecked {
          require(inboundAmt <= MAX_SAFE_VOLUME, "mgv/ratioFromVol/inbound/tooBig");
          require(outboundAmt <= MAX_SAFE_VOLUME, "mgv/ratioFromVol/outbound/tooBig");
          if (outboundAmt == 0) {
            return (MAX_RATIO_MANTISSA,uint(MAX_RATIO_EXP));
          } else if (inboundAmt == 0) {
            return (MIN_RATIO_MANTISSA,uint(MIN_RATIO_EXP));
          }
          uint ratio = (inboundAmt << MANTISSA_BITS) / outboundAmt; 
          uint log2 = BitLib.fls(ratio);
          require(ratio != 0,"mgv/ratioFromVolumes/zeroRatio");
          if (log2 > MANTISSA_BITS_MINUS_ONE) {
            uint diff = log2 - MANTISSA_BITS_MINUS_ONE;
            return (ratio >> diff, MANTISSA_BITS - diff);
          } else {
            uint diff = MANTISSA_BITS_MINUS_ONE - log2;
            return (ratio << diff, MANTISSA_BITS + diff);
          }
        }
      }
    
      /* ### (inbound,outbound) → tick */
      function tickFromVolumes(uint inboundAmt, uint outboundAmt) internal pure returns (Tick tick) {
        (uint man, uint exp) = ratioFromVolumes(inboundAmt, outboundAmt);
        return tickFromNormalizedRatio(man,exp);
      }
    
      /* ### ratio → tick */
      /* Does not require a normalized ratio. */
      function tickFromRatio(uint mantissa, int exp) internal pure returns (Tick) {
        uint normalized_exp;
        (mantissa, normalized_exp) = normalizeRatio(mantissa, exp);
        return tickFromNormalizedRatio(mantissa,normalized_exp);
      }
    
      /* ### low-level ratio → tick */
      /* Given `ratio`, return greatest tick `t` such that `ratioFromTick(t) <= ratio`. 
      * Input ratio must be within the maximum and minimum ratios returned by the available ticks. 
      * Does _not_ expected a normalized float.
      
      The function works as follows:
      * Approximate log2(ratio) to the 13th fractional digit.
      * Following <a href="https://hackmd.io/@mangrovedao/HJvl21zla">https://hackmd.io/@mangrovedao/HJvl21zla</a>, obtain `tickLow` and `tickHigh` such that $\log_{1.0001}(ratio)$ is between them
      * Return the highest one that yields a ratio below the input ratio.
      */
      function tickFromNormalizedRatio(uint mantissa, uint exp) internal pure returns (Tick tick) {
        if (floatLt(mantissa, exp, MIN_RATIO_MANTISSA, uint(MIN_RATIO_EXP))) {
          revert("mgv/tickFromRatio/tooLow");
        }
        if (floatLt(MAX_RATIO_MANTISSA, uint(MAX_RATIO_EXP), mantissa, exp)) {
          revert("mgv/tickFromRatio/tooHigh");
        }
        int log2ratio = int(MANTISSA_BITS_MINUS_ONE) - int(exp) << 64;
        uint mpow = mantissa >> MANTISSA_BITS_MINUS_ONE - 127; // give 129 bits of room left
    
        /* How the fractional digits of the log are computed: 
        * for a given `n` compute $n^2$. 
        * If $\lfloor\log_2(n^2)\rfloor = 2\lfloor\log_2(n)\rfloor$ then the fractional part of $\log_2(n^2)$ was $< 0.5$ (first digit is 0). 
        * If $\lfloor\log_2(n^2)\rfloor = 1 + 2\lfloor\log_2(n)\rfloor$ then the fractional part of $\log_2(n^2)$ was $\geq 0.5$ (first digit is 1).
        * Apply starting with `n=mpow` repeatedly by keeping `n` on 127 bits through right-shifts (has no impact on high fractional bits).
        */
    
        assembly ("memory-safe") {
          // 13 bits of precision
          mpow := shr(127, mul(mpow, mpow))
          let highbit := shr(128, mpow)
          log2ratio := or(log2ratio, shl(63, highbit))
          mpow := shr(highbit, mpow)
    
          mpow := shr(127, mul(mpow, mpow))
          highbit := shr(128, mpow)
          log2ratio := or(log2ratio, shl(62, highbit))
          mpow := shr(highbit, mpow)
    
          mpow := shr(127, mul(mpow, mpow))
          highbit := shr(128, mpow)
          log2ratio := or(log2ratio, shl(61, highbit))
          mpow := shr(highbit, mpow)
    
          mpow := shr(127, mul(mpow, mpow))
          highbit := shr(128, mpow)
          log2ratio := or(log2ratio, shl(60, highbit))
          mpow := shr(highbit, mpow)
    
          mpow := shr(127, mul(mpow, mpow))
          highbit := shr(128, mpow)
          log2ratio := or(log2ratio, shl(59, highbit))
          mpow := shr(highbit, mpow)
    
          mpow := shr(127, mul(mpow, mpow))
          highbit := shr(128, mpow)
          log2ratio := or(log2ratio, shl(58, highbit))
          mpow := shr(highbit, mpow)
    
          mpow := shr(127, mul(mpow, mpow))
          highbit := shr(128, mpow)
          log2ratio := or(log2ratio, shl(57, highbit))
          mpow := shr(highbit, mpow)
    
          mpow := shr(127, mul(mpow, mpow))
          highbit := shr(128, mpow)
          log2ratio := or(log2ratio, shl(56, highbit))
          mpow := shr(highbit, mpow)
    
          mpow := shr(127, mul(mpow, mpow))
          highbit := shr(128, mpow)
          log2ratio := or(log2ratio, shl(55, highbit))
          mpow := shr(highbit, mpow)
    
          mpow := shr(127, mul(mpow, mpow))
          highbit := shr(128, mpow)
          log2ratio := or(log2ratio, shl(54, highbit))
          mpow := shr(highbit, mpow)
    
          mpow := shr(127, mul(mpow, mpow))
          highbit := shr(128, mpow)
          log2ratio := or(log2ratio, shl(53, highbit))
          mpow := shr(highbit, mpow)
    
          mpow := shr(127, mul(mpow, mpow))
          highbit := shr(128, mpow)
          log2ratio := or(log2ratio, shl(52, highbit))
          mpow := shr(highbit, mpow)
    
          mpow := shr(127, mul(mpow, mpow))
          highbit := shr(128, mpow)
          log2ratio := or(log2ratio, shl(51, highbit))
        }
    
        // Convert log base 2 to log base 1.0001 (multiply by `log2(1.0001)^-1 << 64`), since log2ratio is x64 this yields a x128 number.
        int log_bp_ratio = log2ratio * 127869479499801913173571;
    
        // tickLow is approx - maximum error
        int tickLow = int((log_bp_ratio - 1701496478404567508395759362389778998) >> 128);
        // tickHigh is approx + minimum error
        int tickHigh = int((log_bp_ratio + 289637967442836606107396900709005211253) >> 128);
    
        (uint mantissaHigh, uint expHigh) = ratioFromTick(Tick.wrap(tickHigh));
    
        bool ratioHighGt = floatLt(mantissa, exp, mantissaHigh, expHigh);
        if (tickLow == tickHigh || ratioHighGt) {
          tick = Tick.wrap(tickLow);
        } else { 
          tick = Tick.wrap(tickHigh);
        }
      }
    
      /* ### tick → ratio conversion function */
      /* Returns a normalized (man,exp) ratio floating-point number. The mantissa is on 128 bits to avoid overflow when mulitplying with token amounts. The exponent has no bias. for easy comparison. */
      function ratioFromTick(Tick tick) internal pure returns (uint man, uint exp) {
        unchecked {
          (man, exp) = nonNormalizedRatioFromTick(tick);
          int shiftedTick = Tick.unwrap(tick) << LOG_BP_SHIFT;
          int log2ratio;
          // floor log2 of ratio towards negative infinity
          assembly ("memory-safe") {
            log2ratio := sdiv(shiftedTick,LOG_BP_2X235)
            log2ratio := sub(log2ratio,slt(smod(shiftedTick,LOG_BP_2X235),0))
          }
          int diff = log2ratio+int(exp)-int(MANTISSA_BITS_MINUS_ONE);
          if (diff > 0) {
            // For |tick| <= 887272, this drops at most 5 bits of precision
            man = man >> uint(diff);
          } else {
            man = man << uint(-diff);
          }
          // For |tick| << 887272, log2ratio <= 127
          exp = uint(int(MANTISSA_BITS_MINUS_ONE)-log2ratio);
        }
      }
    
      /* ### low-level tick → ratio conversion */
      /* Compute 1.0001^tick and returns it as a (mantissa,exponent) pair. Works by checking each set bit of `|tick|` multiplying by `1.0001^(-2**i)<<128` if the ith bit of tick is set. Since we inspect the absolute value of `tick`, `-1048576` is not a valid tick. If the tick is positive this computes `1.0001^-tick`, and we take the inverse at the end. For maximum precision some powers of 1.0001 are shifted until they occupy 128 bits. The `extra_shift` is recorded and added to the exponent.
    
      Since the resulting mantissa is left-shifted by 128 bits, if tick was positive, we divide `2**256` by the mantissa to get the 128-bit left-shifted inverse of the mantissa.
    
      The error (relative to 1.0001^tick) may be negative or positive.
      */
      function nonNormalizedRatioFromTick(Tick tick) internal pure returns (uint man, uint exp) {
        uint absTick = Tick.unwrap(tick) < 0 ? uint(-Tick.unwrap(tick)) : uint(Tick.unwrap(tick));
        require(absTick <= uint(MAX_TICK), "mgv/absTick/outOfBounds");
    
        int extra_shift;
        if (absTick & 0x1 != 0) {
          man = 0xfff97272373d413259a46990580e2139;
        } else {
          man = 0x100000000000000000000000000000000;
        }
        if (absTick & 0x2 != 0) {
          man = (man * 0xfff2e50f5f656932ef12357cf3c7fdcb) >> 128;
        }
        if (absTick & 0x4 != 0) {
          man = (man * 0xffe5caca7e10e4e61c3624eaa0941ccf) >> 128;
        }
        if (absTick & 0x8 != 0) {
          man = (man * 0xffcb9843d60f6159c9db58835c926643) >> 128;
        }
        if (absTick & 0x10 != 0) {
          man = (man * 0xff973b41fa98c081472e6896dfb254bf) >> 128;
        }
        if (absTick & 0x20 != 0) {
          man = (man * 0xff2ea16466c96a3843ec78b326b52860) >> 128;
        }
        if (absTick & 0x40 != 0) {
          man = (man * 0xfe5dee046a99a2a811c461f1969c3052) >> 128;
        }
        if (absTick & 0x80 != 0) {
          man = (man * 0xfcbe86c7900a88aedcffc83b479aa3a3) >> 128;
        }
        if (absTick & 0x100 != 0) {
          man = (man * 0xf987a7253ac413176f2b074cf7815e53) >> 128;
        }
        if (absTick & 0x200 != 0) {
          man = (man * 0xf3392b0822b70005940c7a398e4b70f2) >> 128;
        }
        if (absTick & 0x400 != 0) {
          man = (man * 0xe7159475a2c29b7443b29c7fa6e889d8) >> 128;
        }
        if (absTick & 0x800 != 0) {
          man = (man * 0xd097f3bdfd2022b8845ad8f792aa5825) >> 128;
        }
        if (absTick & 0x1000 != 0) {
          man = (man * 0xa9f746462d870fdf8a65dc1f90e061e4) >> 128;
        }
        if (absTick & 0x2000 != 0) {
          man = (man * 0xe1b0d342ada5437121767bec575e65ed) >> 128;
          extra_shift += 1;
        }
        if (absTick & 0x4000 != 0) {
          man = (man * 0xc6f84d7e5f423f66048c541550bf3e96) >> 128;
          extra_shift += 2;
        }
        if (absTick & 0x8000 != 0) {
          man = (man * 0x9aa508b5b7a84e1c677de54f3e99bc8f) >> 128;
          extra_shift += 4;
        }
        if (absTick & 0x10000 != 0) {
          man = (man * 0xbad5f1bdb70232cd33865244bdcc089c) >> 128;
          extra_shift += 9;
        }
        if (absTick & 0x20000 != 0) {
          man = (man * 0x885b9613d7e87aa498106fb7fa5edd37) >> 128;
          extra_shift += 18;
        }
        if (absTick & 0x40000 != 0) {
          man = (man * 0x9142e0723efb884889d1f447715afacd) >> 128;
          extra_shift += 37;
        }
        if (absTick & 0x80000 != 0) {
          man = (man * 0xa4d9a773d61316918f140bd96e8e6814) >> 128;
          extra_shift += 75;
        }
        if (Tick.unwrap(tick) > 0) {
          /* We use [Remco Bloemen's trick](https://xn--2-umb.com/17/512-bit-division/#divide-2-256-by-a-given-number) to divide `2**256` by `man`: */
          assembly("memory-safe") {
            man := add(div(sub(0, man), man), 1)
          }
          extra_shift = -extra_shift;
        }
        exp = uint(128 + extra_shift);
    
      }
    
      /* Shift mantissa so it occupies exactly `MANTISSA_BITS` and adjust `exp` in consequence.
      
      A float is normalized when its mantissa occupies exactly 128 bits. All in-range normalized floats have `exp >= 0`, so we can use a `uint` for exponents everywhere we expect a normalized float.
    
      When a non-normalized float is expected/used, `exp` can be negative since there is no constraint on the size of the mantissa.
      
       */
      function normalizeRatio(uint mantissa, int exp) internal pure returns (uint, uint) {
        require(mantissa != 0,"mgv/normalizeRatio/mantissaIs0");
        uint log2ratio = BitLib.fls(mantissa);
        int shift = int(MANTISSA_BITS_MINUS_ONE) - int(log2ratio);
        if (shift < 0) {
          mantissa = mantissa >> uint(-shift);
        } else {
          mantissa = mantissa << uint(shift);
        }
        exp = exp + shift;
        if (exp < 0) {
          revert("mgv/normalizeRatio/lowExp");
        }
        return (mantissa,uint(exp));
      }
    
      /* Return `a/(2**e)` rounded up */
      function divExpUp(uint a, uint e) internal pure returns (uint) {
        unchecked {
          uint rem;
          /* 
          Let mask be `(1<<e)-1`, `rem` is 1 if `a & mask > 0`, and 0 otherwise.
          Explanation:
          * if a is 0 then `rem` must be 0. `0 & mask` is 0.
          * else if `e > 255` then `0 < a < 2^e`, so `rem` must be 1. `(1<<e)-1` is `type(uint).max`, so `a & mask is a > 0`.
          * else `a & mask` is `a % 2**e`
          */
          assembly("memory-safe") {
            rem := gt(and(a,sub(shl(e,1),1)),0)
          }
          return (a>>e) + rem;
        }
      }
    
      /* Floats are normalized to 128 bits to ensure no overflow when multiplying with amounts, and for easier comparisons. Normalized in-range floats have `exp>=0`. */
      function floatLt(uint mantissa_a, uint exp_a, uint mantissa_b, uint exp_b) internal pure returns (bool) {
        /* Exponents are negated (so that exponents of ratios within the accepted range as >= 0, which simplifies the code), which explains the direction of the `exp_a > exp_b` comparison. */ 
        return (exp_a > exp_b || (exp_a == exp_b && mantissa_a < mantissa_b));
      }
    
    }
  • src/strategies/interfaces/IOrderLogic.sol
    // SPDX-License-Identifier: MIT
    pragma solidity >=0.8.10;
    
    import {OLKey} from "@mgv/src/core/MgvLib.sol";
    import {Tick} from "@mgv/lib/core/TickLib.sol";
    import {AbstractRoutingLogic} from "@mgv-strats/src/strategies/routing_logic/abstract/AbstractRoutingLogic.sol";
    import {TakerOrderType} from "@mgv-strats/src/strategies/TakerOrderLib.sol";
    
    ///@title Interface for resting orders functionality.
    interface IOrderLogic {
      ///@notice Information for creating a market order with a GTC or FOK semantics.
      ///@param olKey the offer list key.
      ///@param tick the tick
      ///@param orderType the order type
      ///@param fillVolume the volume to fill
      ///@param fillWants if true (usually when `TakerOrder` implements a "buy" on a market), the market order stops when `fillVolume` units of `olKey.outbound_tkn` have been obtained (fee included); otherwise (selling), the market order stops when `fillVolume` units of `olKey.inbound_tkn` have been sold.
      ///@param restingOrderGasreq the gas requirement for executing a resting order
      ///@param expiryDate timestamp (expressed in seconds since unix epoch) beyond which the order is no longer valid, 0 means forever
      ///@param offerId the id of an existing, dead offer owned by the taker to re-use for the resting order, 0 means no re-use.
      ///@param takerGivesLogic custom contract implementing routing logic for the tokens that are given by the taker order.
      ///@param takerWantsLogic custom contract implementing routing logic for the tokens that are wanted by the taker order.
      struct TakerOrder {
        OLKey olKey;
        Tick tick;
        TakerOrderType orderType;
        uint fillVolume;
        bool fillWants;
        uint expiryDate;
        uint offerId;
        uint restingOrderGasreq;
        AbstractRoutingLogic takerGivesLogic;
        AbstractRoutingLogic takerWantsLogic;
      }
    
      ///@notice Result of an order from the takers side.
      ///@param takerGot How much the taker got
      ///@param takerGave How much the taker gave
      ///@param bounty How much bounty was givin to the taker
      ///@param fee The fee paid by the taker
      ///@param offerId The id of the offer that was taken
      struct TakerOrderResult {
        uint takerGot;
        uint takerGave;
        uint bounty;
        uint fee;
        uint offerId;
        bytes32 offerWriteData;
      }
    
      ///@notice Information about the order.
      ///@param olKeyHash the hash of the offer list key. This could be found by the OrderStart event, but is needed for RPC call. This is indexed so that RPC calls can filter on it.
      ///@param taker The address of the taker. This could be found by the OrderStart event, but is needed for RPC call. This is indexed so that RPC calls can filter on it.
      ///@param tick The tick of the order. This is not needed for an indexer, as it can get it from the OrderStart event. It is only emitted for RPC calls.
      ///@param orderType type of the order.
      ///@param fillVolume the volume to fill. This is not needed for an indexer, as it can get it from the OrderStart event. It is only emitted for RPC calls.
      ///@param fillWants if true (buying), the market order stops when `fillVolume` units of `olKey.outbound_tkn` have been obtained (fee included); otherwise (selling), the market order stops when `fillVolume` units of `olKey.inbound_tkn` have been sold.
      ///@param offerId The optional offerId take was called with, 0 if not passed. This is not needed for an indexer. It is only emitted for RPC calls.
      ///@param takerGivesLogic custom contract implementing routing logic for the tokens that are given by the taker order.
      ///@param takerWantsLogic custom contract implementing routing logic for the tokens that are wanted by the taker order.
      ///@notice By emitting this data, an indexer will be able to tell that we are in the context of an mangroveOrder and keep track of what parameters were used to start the order.
      event MangroveOrderStart(
        bytes32 indexed olKeyHash,
        address indexed taker,
        Tick tick,
        TakerOrderType orderType,
        uint fillVolume,
        bool fillWants,
        uint offerId,
        AbstractRoutingLogic takerGivesLogic,
        AbstractRoutingLogic takerWantsLogic
      );
    
      ///@notice Indicates that the MangroveOrder has been completed.
      ///@notice We only emit this, so that an indexer can know that the order is completed and can thereby keep a correct context
      event MangroveOrderComplete();
    
      ///@notice Implements "Fill or kill" or "Good till cancelled" orders on a given offer list.
      ///@param tko the arguments in memory of the taker order
      ///@return res the result of the taker order. If `offerId==0`, no resting order was posted on `msg.sender`'s behalf.
      function take(TakerOrder memory tko) external payable returns (TakerOrderResult memory res);
    }
    
  • src/strategies/routers/SimpleRouter.sol
    // SPDX-License-Identifier: MIT
    pragma solidity ^0.8.10;
    
    import {TransferLib} from "@mgv/lib/TransferLib.sol";
    import {AbstractRouter, RL} from "./abstract/AbstractRouter.sol";
    
    ///@title `SimpleRouter` instances have a unique sourcing strategy: pull (push) liquidity directly from (to) the an offer owner's account
    ///@dev Maker contracts using this router must make sure that the `routingOrder.fundOwner` approves the router for all asset that will be pulled (outbound tokens)
    /// Thus a maker contract using a vault that is not an EOA must make sure this vault has approval capacities.
    contract SimpleRouter is AbstractRouter {
      /// @inheritdoc AbstractRouter
      /// @notice SimpleRouter disregards `routingOrder.olKeyHash` and `routingOrder.offerId` as all offers are routed directly to the fund owner.
      function __pull__(RL.RoutingOrder memory routingOrder, uint amount, bool strict)
        internal
        virtual
        override
        returns (uint pulled)
      {
        // if not strict, pulling all available tokens from reserve
        uint amount_ = strict ? amount : routingOrder.token.balanceOf(routingOrder.fundOwner);
        if (TransferLib.transferTokenFrom(routingOrder.token, routingOrder.fundOwner, msg.sender, amount_)) {
          return amount_;
        } else {
          return 0;
        }
      }
    
      /// @inheritdoc AbstractRouter
      function __push__(RL.RoutingOrder memory routingOrder, uint amount) internal virtual override returns (uint) {
        bool success = TransferLib.transferTokenFrom(routingOrder.token, msg.sender, routingOrder.fundOwner, amount);
        return success ? amount : 0;
      }
    
      ///@inheritdoc AbstractRouter
      function tokenBalanceOf(RL.RoutingOrder calldata routingOrder) public view virtual override returns (uint balance) {
        balance = routingOrder.token.balanceOf(routingOrder.fundOwner);
      }
    }
    
  • node_modules/@mangrovedao/mangrove-core/lib/IERC20.sol
    // SPDX-License-Identifier: MIT
    pragma solidity >=0.6.2;
    
    interface IERC20 {
      function totalSupply() external view returns (uint);
    
      function balanceOf(address account) external view returns (uint);
    
      function transfer(address recipient, uint amount) external returns (bool);
    
      function allowance(address owner, address spender) external view returns (uint);
    
      function approve(address spender, uint amount) external returns (bool);
    
      function transferFrom(address sender, address recipient, uint amount) external returns (bool);
    
      function symbol() external view returns (string memory);
    
      event Transfer(address indexed from, address indexed to, uint value);
      event Approval(address indexed owner, address indexed spender, uint value);
    
      function decimals() external view returns (uint8);
    
      function name() external view returns (string memory);
    }
    
  • node_modules/@mangrovedao/mangrove-core/lib/core/OfferDetailExtra.sol
    // SPDX-License-Identifier: MIT
    pragma solidity ^0.8.17;
    
    import {OfferDetail,OfferDetailUnpacked} from "@mgv/src/preprocessed/OfferDetail.post.sol";
    
    
    /* Extra functions for the offer details */
    library OfferDetailExtra {
      function offer_gasbase(OfferDetail offerDetail) internal pure returns (uint) { unchecked {
        return offerDetail.kilo_offer_gasbase() * 1e3;
      }}
      function offer_gasbase(OfferDetail offerDetail,uint val) internal pure returns (OfferDetail) { unchecked {
        return offerDetail.kilo_offer_gasbase(val/1e3);
      }}
    }
    
    /* Extra functions for the struct version of the offer details */
    library OfferDetailUnpackedExtra {
      function offer_gasbase(OfferDetailUnpacked memory offerDetail) internal pure returns (uint) { unchecked {
        return offerDetail.kilo_offer_gasbase * 1e3;
      }}
      function offer_gasbase(OfferDetailUnpacked memory offerDetail,uint val) internal pure { unchecked {
        offerDetail.kilo_offer_gasbase = val/1e3;
      }}
    }
  • src/strategies/MangroveOffer.sol
    // SPDX-License-Identifier: MIT
    pragma solidity ^0.8.10;
    
    import {AccessControlled} from "@mgv-strats/src/strategies/utils/AccessControlled.sol";
    import {IOfferLogic} from "@mgv-strats/src/strategies/interfaces/IOfferLogic.sol";
    import {MgvLib, IERC20, OLKey, OfferDetail} from "@mgv/src/core/MgvLib.sol";
    import {IMangrove} from "@mgv/src/IMangrove.sol";
    import {AbstractRouter, RL} from "@mgv-strats/src/strategies/routers/abstract/AbstractRouter.sol";
    import {TransferLib} from "@mgv/lib/TransferLib.sol";
    import {Tick} from "@mgv/lib/core/TickLib.sol";
    
    /// @title This contract is the basic building block for Mangrove strats.
    /// @notice It contains the mandatory interface expected by Mangrove (`IOfferLogic` is `IMaker`) and enforces additional functions implementations (via `IOfferLogic`).
    /// @dev Naming scheme:
    /// `f() public`: can be used, as is, in all descendants of `this` contract
    /// `_f() internal`: descendant of this contract should provide a public wrapper for this function, with necessary guards.
    /// `__f__() virtual internal`: descendant of this contract should override this function to specialize it to the needs of the strat.
    
    abstract contract MangroveOffer is AccessControlled, IOfferLogic {
      ///@notice The Mangrove deployment that is allowed to call `this` for trade execution and posthook.
      IMangrove public immutable MGV;
    
      /// @notice router contract which implements routing functions -- this can be 0x in Direct strats
      AbstractRouter public immutable ROUTER_IMPLEMENTATION;
    
      ///@notice The offer was successfully reposted.
      bytes32 internal constant REPOST_SUCCESS = "offer/updated";
      ///@notice New offer successfully created.
      bytes32 internal constant NEW_OFFER_SUCCESS = "offer/created";
      ///@notice The offer was completely filled.
      bytes32 internal constant COMPLETE_FILL = "offer/filled";
    
      ///@notice Mandatory function to allow `this` to receive native tokens
      ///@dev this happens when `this` calls `withdrawFromMangrove` with `deprovision=true` or if `this` becomes the recipient of a `makerExecute` bounty.
      receive() external payable virtual {}
    
      /**
       * @notice `MangroveOffer`'s constructor
       * @param mgv The Mangrove deployment that is allowed to call `this` for trade execution and posthook.
       * @param routerImplementation sets the type of router that the router factory can spawn
       */
      constructor(IMangrove mgv, AbstractRouter routerImplementation) AccessControlled(msg.sender) {
        require(address(mgv) != address(0), "MgvOffer/0xMangrove");
        MGV = mgv;
        ROUTER_IMPLEMENTATION = routerImplementation; // this may be 0x
      }
    
      ///*****************************
      /// Mandatory callback functions
      ///*****************************
    
      ///@notice `makerExecute` is the callback function to execute all offers that were posted on Mangrove by `this` contract.
      ///@param order a data structure that recapitulates the taker order and the offer as it was posted on mangrove
      ///@return ret a bytes32 word to pass information (if needed) to the posthook (under `result.makerData`)
      ///@dev it may not be overriden although it can be customized using `__lastLook__`, `__put__` and `__get__` hooks.
      /// NB #1: if `makerExecute` reverts, the offer will be considered to be refusing the trade.
      /// NB #2: `makerExecute` may return a `bytes32` word to pass information to posthook w/o using storage reads/writes.
      /// NB #3: Reneging on trade will have the following effects:
      /// * Offer is removed from the Offer List
      /// * Offer bounty will be withdrawn from offer provision and sent to the offer taker. The remaining provision will be credited to `this` contract's account on Mangrove
      function makerExecute(MgvLib.SingleOrder calldata order)
        external
        override
        onlyCaller(address(MGV))
        returns (bytes32 ret)
      {
        // Invoke hook that implements a last look check during execution - it may renege on trade by reverting.
        ret = __lastLook__(order);
        // Invoke hook to put the inbound_tkn, which are brought by the taker, into a specific reserve.
        require(__put__(order.takerGives, order) == 0, "mgvOffer/abort/putFailed");
        // Invoke hook to fetch the outbound_tkn, which are promised to the taker, from a specific reserve.
        require(__get__(order.takerWants, order) == 0, "mgvOffer/abort/getFailed");
      }
    
      /// @notice `makerPosthook` is the callback function that is called by Mangrove *after* the offer execution.
      /// @notice reverting during its execution will not renege on trade. Revert reason (casted to 32 bytes) is then logged by Mangrove in event `PosthookFail`.
      /// @param order a data structure that recapitulates the taker order and the offer as it was posted on mangrove
      /// @param result a data structure that gathers information about trade execution
      /// @dev It cannot be overridden but can be customized via the hooks `__posthookSuccess__`, `__posthookFallback__` and `__handleResidualProvision__` (see below).
      function makerPosthook(MgvLib.SingleOrder calldata order, MgvLib.OrderResult calldata result)
        external
        override
        onlyCaller(address(MGV))
      {
        if (result.mgvData == "mgv/tradeSuccess") {
          __posthookSuccess__(order, result.makerData);
        } else {
          // logging what went wrong during `makerExecute`
          emit LogIncident(order.olKey.hash(), order.offerId, result.makerData, result.mgvData);
          // calling strat specific handlers in case of failure
          __posthookFallback__(order, result);
          __handleResidualProvision__(order);
        }
      }
    
      ///@notice whether this contract has enabled liquidity routing
      ///@return true if this contracts routes liquidity using an external router
      function _isRouting() internal view returns (bool) {
        return address(ROUTER_IMPLEMENTATION) != address(0);
      }
    
      ///@notice takes care of status for reposting residual offer in case of a partial fill and logging of potential issues.
      ///@param order a recap of the taker order
      ///@param makerData generated during `makerExecute` so as to log it if necessary
      ///@param repostStatus from the posthook that handles residual reposting
      function logRepostStatus(MgvLib.SingleOrder calldata order, bytes32 makerData, bytes32 repostStatus) internal {
        // reposting below density is not considered an incident in order to allow the contract not to check density before posting residual
        if (
          repostStatus == COMPLETE_FILL || repostStatus == REPOST_SUCCESS || repostStatus == "mgv/writeOffer/density/tooLow"
        ) {
          return;
        } else {
          // Offer failed to repost for bad reason, logging the incident
          emit LogIncident(order.olKey.hash(), order.offerId, makerData, repostStatus);
        }
      }
    
      /// @inheritdoc IOfferLogic
      function approve(IERC20 token, address spender, uint amount) public override onlyAdmin returns (bool) {
        require(TransferLib.approveToken(token, spender, amount), "mgvOffer/approve/failed");
        return true;
      }
    
      /// @inheritdoc IOfferLogic
      function withdrawFromMangrove(uint amount, address payable receiver) public onlyAdmin {
        if (amount == type(uint).max) {
          amount = MGV.balanceOf(address(this));
        }
        // the require below is necessary if the `receive()` function is overriden
        require(MGV.withdraw(amount), "mgvOffer/withdrawFail");
        (bool noRevert,) = receiver.call{value: amount}("");
        // if `receiver` is actually not payable
        require(noRevert, "mgvOffer/weiTransferFail");
      }
    
      ///@notice Hook that implements where the inbound token, which are brought by the Offer Taker, should go during Taker Order's execution.
      ///@param amount of `inbound` tokens that are on `this` contract's balance and still need to be deposited somewhere
      ///@param order is a recall of the taker order that is at the origin of the current trade.
      ///@return missingPut (<=`amount`) is the amount of `inbound` tokens whose deposit location has not been decided (possibly because of a failure) during this function execution
      ///@dev if the last nested call to `__put__` returns a non zero value, trade execution will revert
      function __put__(uint amount, MgvLib.SingleOrder calldata order) internal virtual returns (uint missingPut);
    
      ///@notice Hook that implements where the outbound token, which are promised to the taker, should be fetched from, during Taker Order's execution.
      ///@param amount of `outbound` tokens that still needs to be brought to the balance of `this` contract when entering this function
      ///@param order is a recall of the taker order that is at the origin of the current trade.
      ///@return missingGet (<=`amount`), which is the amount of `outbound` tokens still need to be fetched at the end of this function
      ///@dev if the last nested call to `__get__` returns a non zero value, trade execution will revert
      function __get__(uint amount, MgvLib.SingleOrder calldata order) internal virtual returns (uint missingGet);
    
      /// @notice Hook that implements a last look check during Taker Order's execution.
      /// @param order is a recall of the taker order that is at the origin of the current trade.
      /// @return data is a message that will be passed to posthook provided `makerExecute` does not revert.
      /// @dev __lastLook__ should revert if trade is to be reneged on. If not, returned `bytes32` are passed to `makerPosthook` in the `makerData` field.
      /// @custom:hook overrides of this hook should be conservative and call `super.__lastLook__(order)`.
      function __lastLook__(MgvLib.SingleOrder calldata order) internal virtual returns (bytes32 data) {
        order; //shh
        data = bytes32(0);
      }
    
      /// @inheritdoc IOfferLogic
      function activate(IERC20 token) public virtual override {
        require(TransferLib.approveToken(token, address(MGV), type(uint).max), "MgvOffer/ActivationFailed");
      }
    
      ///@notice Post-hook that implements fallback behavior when Taker Order's execution failed unexpectedly.
      ///@param order is a recall of the taker order that is at the origin of the current trade.
      ///@param result contains information about trade.
      ///@return data contains verdict and reason about the executed trade.
      ///@dev `result.mgvData` is Mangrove's verdict about trade success
      ///@dev `result.makerData` either contains the first 32 bytes of revert reason if `makerExecute` reverted or the returned `bytes32`.
      /// @custom:hook overrides of this hook should be conservative and call `super.__posthookFallback__(order, result)`
      function __posthookFallback__(MgvLib.SingleOrder calldata order, MgvLib.OrderResult calldata result)
        internal
        virtual
        returns (bytes32 data)
      {
        order;
        result;
        data = bytes32(0);
      }
    
      ///@notice Given the current taker order that (partially) consumes an offer, this hook is used to declare how much `order.olKey.outbound_tkn` the offer gives after it is reposted, while also allowing adjustment to the tick.
      ///@param order is a recall of the taker order that is being treated.
      ///@return newGives the new volume of `outbound_tkn` the offer will give if fully taken.
      ///@return newTick the new tick of the reposted offer.
      ///@dev default is to require the original amount of tokens minus those that have been sent to the taker during trade execution and keep the tick.
      function __residualValues__(MgvLib.SingleOrder calldata order) internal virtual returns (uint newGives, Tick newTick) {
        newGives = order.offer.gives() - order.takerWants;
        newTick = order.offer.tick();
      }
    
      ///@notice Hook that defines what needs to be done to the part of an offer provision that was added to the balance of `this` on Mangrove after an offer has failed.
      ///@param order is a recall of the taker order that failed
      function __handleResidualProvision__(MgvLib.SingleOrder calldata order) internal virtual {
        order; // we leave the provision on Mangrove
      }
    
      ///@notice Post-hook that implements default behavior when Taker Order's execution succeeded.
      ///@param order is a recall of the taker order that is at the origin of the current trade.
      ///@param makerData is the returned value of the `__lastLook__` hook, triggered during trade execution. The special value `"lastLook/retract"` should be treated as an instruction not to repost the offer on the list.
      ///@return data can be:
      /// * `COMPLETE_FILL` when offer was completely filled
      /// * returned data of `_updateOffer` signalling the status of the reposting attempt.
      /// @custom:hook overrides of this hook should be conservative and call `super.__posthookSuccess__(order, maker_data)`
      function __posthookSuccess__(MgvLib.SingleOrder calldata order, bytes32 makerData)
        internal
        virtual
        returns (bytes32 data)
      {
        // now trying to repost residual
        (uint newGives, Tick newTick) = __residualValues__(order);
        // Density check at each repost would be too gas costly.
        // We only treat the special case of `gives==0` or `wants==0` (total fill).
        // Note: wants (given by `inboundFromOutbound`) can be 0 due to rounding given the price. We could repost to get rid of the last gives at 0 wants,
        // but the maker does not need to give away these tokens for free, so we skip it.
        // Offer below the density will cause Mangrove to throw so we encapsulate the call to `updateOffer` in order not to revert posthook for posting at dust level.
        if (newGives == 0 || newTick.inboundFromOutbound(newGives) == 0) {
          return COMPLETE_FILL;
        }
        data = _updateOffer(
          OfferArgs({
            olKey: order.olKey,
            tick: newTick,
            gives: newGives,
            gasreq: order.offerDetail.gasreq(),
            gasprice: order.offerDetail.gasprice(),
            noRevert: true,
            fund: 0
          }),
          order.offerId
        );
        // logs if anything went wrong
        logRepostStatus(order, makerData, data);
      }
    
      ///@notice Updates the offer specified by `offerId` on Mangrove with the parameters in `args`.
      ///@param args A memory struct containing the offer parameters to update.
      ///@param offerId An unsigned integer representing the identifier of the offer to be updated.
      ///@return status a `bytes32` value representing either `REPOST_SUCCESS` if the update is successful, or an error message if an error occurs and `OfferArgs.noRevert` is `true`. If `OfferArgs.noRevert` is `false`, the function reverts with the error message as the reason.
      function _updateOffer(OfferArgs memory args, uint offerId) internal virtual returns (bytes32);
    
      ///@notice computes the provision that can be redeemed if deprovisioning a certain offer
      ///@param olKey the offer list key.
      ///@param offerId the id of the offer
      ///@return provision the provision that can be redeemed
      function _provisionOf(OLKey memory olKey, uint offerId) internal view returns (uint provision) {
        OfferDetail offerDetail = MGV.offerDetails(olKey, offerId);
        unchecked {
          provision = offerDetail.gasprice() * 1e6 * (offerDetail.offer_gasbase() + offerDetail.gasreq());
        }
      }
    }
    
  • src/strategies/routers/abstract/RoutingOrderLib.sol
    // SPDX-License-Identifier: MIT
    pragma solidity ^0.8.10;
    
    import {IERC20} from "@mgv/lib/IERC20.sol";
    
    /// @title Library to obtain Routing orders of various kind
    
    library RoutingOrderLib {
      ///@notice pointer to first storage slot used for randomized access
      bytes32 private constant OFFSET = keccak256("MangroveStrats.RoutingOrderLib.Layout");
    
      ///@notice Argument to pull/push
      ///@param token the asset to be routed
      ///@param olKeyHash the id of the market that triggered the calling offer logic. Is bytes32(0) when routing is done outside offer logic.
      ///@param offerId the id of the offer that triggered the calling offer logic. Is uint(0) when routing is done outsider offer logic.
      ///@param fundOwner the owner of the routed funds. If calling router is a proxy, it address is determined by `fundOwner`.
      struct RoutingOrder {
        IERC20 token;
        bytes32 olKeyHash;
        uint offerId;
        address fundOwner;
      }
    
      ///@notice helper to create a RoutingOrder struct without zero'ed fields for market coordinates.
      ///@param token the asset to be routed
      ///@param fundOwner the owner of the routed funds
      ///@return ro the routing order struct
      function createOrder(IERC20 token, address fundOwner) internal pure returns (RoutingOrder memory ro) {
        ro.token = token;
        ro.fundOwner = fundOwner;
      }
    
      ///@notice the bound maker contracts which are allowed to call this router.
      struct Layout {
        mapping(address => bool) boundMakerContracts;
      }
    
      ///@notice allowed pullers/pushers for this router
      ///@return bound contracts to this router, in the form of a storage mapping
      function boundMakerContracts() internal view returns (mapping(address => bool) storage) {
        bytes32 offset = OFFSET;
        Layout storage st;
        assembly ("memory-safe") {
          st.slot := offset
        }
        return st.boundMakerContracts;
      }
    }
    
  • node_modules/@mangrovedao/mangrove-core/src/preprocessed/Local.post.sol
    // SPDX-License-Identifier: MIT
    
    pragma solidity ^0.8.13;
    
    /* ************************************************** *
                GENERATED FILE. DO NOT EDIT.
     * ************************************************** */
    
    /* since you can't convert bool to uint in an expression without conditionals,
     * we add a file-level function and rely on compiler optimization
     */
    function uint_of_bool(bool b) pure returns (uint u) {
      assembly ("memory-safe") { u := b }
    }
    import "@mgv/lib/core/Constants.sol";
    
    struct LocalUnpacked {
      bool active;
      uint fee;
      Density density;
      uint binPosInLeaf;
      Field level3;
      Field level2;
      Field level1;
      Field root;
      uint kilo_offer_gasbase;
      bool lock;
      uint last;
    }
    
    //some type safety for each struct
    type Local is uint;
    using LocalLib for Local global;
    
    ////////////// ADDITIONAL DEFINITIONS, IF ANY ////////////////
    import {Density, DensityLib} from "@mgv/lib/core/DensityLib.sol";
    import {Bin,TickTreeLib,Field} from "@mgv/lib/core/TickTreeLib.sol";
    /* Globally enable global.method(...) */
    import {LocalExtra,LocalUnpackedExtra} from "@mgv/lib/core/LocalExtra.sol";
    using LocalExtra for Local global;
    using LocalUnpackedExtra for LocalUnpacked global;
    
    ////////////// END OF ADDITIONAL DEFINITIONS /////////////////
    
    library LocalLib {
    
      // number of bits in each field
      uint constant active_bits             = 1;
      uint constant fee_bits                = 8;
      uint constant density_bits            = 9;
      uint constant binPosInLeaf_bits       = 2;
      uint constant level3_bits             = 64;
      uint constant level2_bits             = 64;
      uint constant level1_bits             = 64;
      uint constant root_bits               = 2;
      uint constant kilo_offer_gasbase_bits = 9;
      uint constant lock_bits               = 1;
      uint constant last_bits               = 32;
    
      // number of bits before each field
      uint constant active_before             = 0                         + 0;
      uint constant fee_before                = active_before             + active_bits;
      uint constant density_before            = fee_before                + fee_bits;
      uint constant binPosInLeaf_before       = density_before            + density_bits;
      uint constant level3_before             = binPosInLeaf_before       + binPosInLeaf_bits;
      uint constant level2_before             = level3_before             + level3_bits;
      uint constant level1_before             = level2_before             + level2_bits;
      uint constant root_before               = level1_before             + level1_bits;
      uint constant kilo_offer_gasbase_before = root_before               + root_bits;
      uint constant lock_before               = kilo_offer_gasbase_before + kilo_offer_gasbase_bits;
      uint constant last_before               = lock_before               + lock_bits;
    
      // focus-mask: 1s at field location, 0s elsewhere
      uint constant active_mask_inv             = (ONES << 256 - active_bits) >> active_before;
      uint constant fee_mask_inv                = (ONES << 256 - fee_bits) >> fee_before;
      uint constant density_mask_inv            = (ONES << 256 - density_bits) >> density_before;
      uint constant binPosInLeaf_mask_inv       = (ONES << 256 - binPosInLeaf_bits) >> binPosInLeaf_before;
      uint constant level3_mask_inv             = (ONES << 256 - level3_bits) >> level3_before;
      uint constant level2_mask_inv             = (ONES << 256 - level2_bits) >> level2_before;
      uint constant level1_mask_inv             = (ONES << 256 - level1_bits) >> level1_before;
      uint constant root_mask_inv               = (ONES << 256 - root_bits) >> root_before;
      uint constant kilo_offer_gasbase_mask_inv = (ONES << 256 - kilo_offer_gasbase_bits) >> kilo_offer_gasbase_before;
      uint constant lock_mask_inv               = (ONES << 256 - lock_bits) >> lock_before;
      uint constant last_mask_inv               = (ONES << 256 - last_bits) >> last_before;
    
      // cleanup-mask: 0s at field location, 1s elsewhere
      uint constant active_mask             = ~active_mask_inv;
      uint constant fee_mask                = ~fee_mask_inv;
      uint constant density_mask            = ~density_mask_inv;
      uint constant binPosInLeaf_mask       = ~binPosInLeaf_mask_inv;
      uint constant level3_mask             = ~level3_mask_inv;
      uint constant level2_mask             = ~level2_mask_inv;
      uint constant level1_mask             = ~level1_mask_inv;
      uint constant root_mask               = ~root_mask_inv;
      uint constant kilo_offer_gasbase_mask = ~kilo_offer_gasbase_mask_inv;
      uint constant lock_mask               = ~lock_mask_inv;
      uint constant last_mask               = ~last_mask_inv;
    
      // cast-mask: 0s followed by |field| trailing 1s
      uint constant active_cast_mask             = ~(ONES << active_bits);
      uint constant fee_cast_mask                = ~(ONES << fee_bits);
      uint constant density_cast_mask            = ~(ONES << density_bits);
      uint constant binPosInLeaf_cast_mask       = ~(ONES << binPosInLeaf_bits);
      uint constant level3_cast_mask             = ~(ONES << level3_bits);
      uint constant level2_cast_mask             = ~(ONES << level2_bits);
      uint constant level1_cast_mask             = ~(ONES << level1_bits);
      uint constant root_cast_mask               = ~(ONES << root_bits);
      uint constant kilo_offer_gasbase_cast_mask = ~(ONES << kilo_offer_gasbase_bits);
      uint constant lock_cast_mask               = ~(ONES << lock_bits);
      uint constant last_cast_mask               = ~(ONES << last_bits);
    
      // size-related error message
      string constant active_size_error             = "mgv/config/active/1bits";
      string constant fee_size_error                = "mgv/config/fee/8bits";
      string constant density_size_error            = "mgv/config/density/9bits";
      string constant binPosInLeaf_size_error       = "mgv/config/binPosInLeaf/2bits";
      string constant level3_size_error             = "mgv/config/level3/64bits";
      string constant level2_size_error             = "mgv/config/level2/64bits";
      string constant level1_size_error             = "mgv/config/level1/64bits";
      string constant root_size_error               = "mgv/config/root/2bits";
      string constant kilo_offer_gasbase_size_error = "mgv/config/kilo_offer_gasbase/9bits";
      string constant lock_size_error               = "mgv/config/lock/1bits";
      string constant last_size_error               = "mgv/config/last/32bits";
    
      // from packed to in-memory struct
      function to_struct(Local __packed) internal pure returns (LocalUnpacked memory __s) { unchecked {
        __s.active             = ((Local.unwrap(__packed) & active_mask_inv) > 0);
        __s.fee                = uint(Local.unwrap(__packed) << fee_before) >> (256 - fee_bits);
        __s.density            = Density.wrap(uint(Local.unwrap(__packed) << density_before) >> (256 - density_bits));
        __s.binPosInLeaf       = uint(Local.unwrap(__packed) << binPosInLeaf_before) >> (256 - binPosInLeaf_bits);
        __s.level3             = Field.wrap(uint(Local.unwrap(__packed) << level3_before) >> (256 - level3_bits));
        __s.level2             = Field.wrap(uint(Local.unwrap(__packed) << level2_before) >> (256 - level2_bits));
        __s.level1             = Field.wrap(uint(Local.unwrap(__packed) << level1_before) >> (256 - level1_bits));
        __s.root               = Field.wrap(uint(Local.unwrap(__packed) << root_before) >> (256 - root_bits));
        __s.kilo_offer_gasbase = uint(Local.unwrap(__packed) << kilo_offer_gasbase_before) >> (256 - kilo_offer_gasbase_bits);
        __s.lock               = ((Local.unwrap(__packed) & lock_mask_inv) > 0);
        __s.last               = uint(Local.unwrap(__packed) << last_before) >> (256 - last_bits);
      }}
    
      // equality checking
      function eq(Local __packed1, Local __packed2) internal pure returns (bool) { unchecked {
        return Local.unwrap(__packed1) == Local.unwrap(__packed2);
      }}
    
      // from packed to a tuple
      function unpack(Local __packed) internal pure returns (bool __active, uint __fee, Density __density, uint __binPosInLeaf, Field __level3, Field __level2, Field __level1, Field __root, uint __kilo_offer_gasbase, bool __lock, uint __last) { unchecked {
        __active             = ((Local.unwrap(__packed) & active_mask_inv) > 0);
        __fee                = uint(Local.unwrap(__packed) << fee_before) >> (256 - fee_bits);
        __density            = Density.wrap(uint(Local.unwrap(__packed) << density_before) >> (256 - density_bits));
        __binPosInLeaf       = uint(Local.unwrap(__packed) << binPosInLeaf_before) >> (256 - binPosInLeaf_bits);
        __level3             = Field.wrap(uint(Local.unwrap(__packed) << level3_before) >> (256 - level3_bits));
        __level2             = Field.wrap(uint(Local.unwrap(__packed) << level2_before) >> (256 - level2_bits));
        __level1             = Field.wrap(uint(Local.unwrap(__packed) << level1_before) >> (256 - level1_bits));
        __root               = Field.wrap(uint(Local.unwrap(__packed) << root_before) >> (256 - root_bits));
        __kilo_offer_gasbase = uint(Local.unwrap(__packed) << kilo_offer_gasbase_before) >> (256 - kilo_offer_gasbase_bits);
        __lock               = ((Local.unwrap(__packed) & lock_mask_inv) > 0);
        __last               = uint(Local.unwrap(__packed) << last_before) >> (256 - last_bits);
      }}
    
      // getters
      function active(Local __packed) internal pure returns(bool) { unchecked {
        return ((Local.unwrap(__packed) & active_mask_inv) > 0);
      }}
    
      // setters
      function active(Local __packed,bool val) internal pure returns(Local) { unchecked {
        return Local.wrap((Local.unwrap(__packed) & active_mask) | (uint_of_bool(val) << (256 - active_bits)) >> active_before);
      }}
      
      function fee(Local __packed) internal pure returns(uint) { unchecked {
        return uint(Local.unwrap(__packed) << fee_before) >> (256 - fee_bits);
      }}
    
      // setters
      function fee(Local __packed,uint val) internal pure returns(Local) { unchecked {
        return Local.wrap((Local.unwrap(__packed) & fee_mask) | (val << (256 - fee_bits)) >> fee_before);
      }}
      
      function density(Local __packed) internal pure returns(Density) { unchecked {
        return Density.wrap(uint(Local.unwrap(__packed) << density_before) >> (256 - density_bits));
      }}
    
      // setters
      function density(Local __packed,Density val) internal pure returns(Local) { unchecked {
        return Local.wrap((Local.unwrap(__packed) & density_mask) | (Density.unwrap(val) << (256 - density_bits)) >> density_before);
      }}
      
      function binPosInLeaf(Local __packed) internal pure returns(uint) { unchecked {
        return uint(Local.unwrap(__packed) << binPosInLeaf_before) >> (256 - binPosInLeaf_bits);
      }}
    
      // setters
      function binPosInLeaf(Local __packed,uint val) internal pure returns(Local) { unchecked {
        return Local.wrap((Local.unwrap(__packed) & binPosInLeaf_mask) | (val << (256 - binPosInLeaf_bits)) >> binPosInLeaf_before);
      }}
      
      function level3(Local __packed) internal pure returns(Field) { unchecked {
        return Field.wrap(uint(Local.unwrap(__packed) << level3_before) >> (256 - level3_bits));
      }}
    
      // setters
      function level3(Local __packed,Field val) internal pure returns(Local) { unchecked {
        return Local.wrap((Local.unwrap(__packed) & level3_mask) | (Field.unwrap(val) << (256 - level3_bits)) >> level3_before);
      }}
      
      function level2(Local __packed) internal pure returns(Field) { unchecked {
        return Field.wrap(uint(Local.unwrap(__packed) << level2_before) >> (256 - level2_bits));
      }}
    
      // setters
      function level2(Local __packed,Field val) internal pure returns(Local) { unchecked {
        return Local.wrap((Local.unwrap(__packed) & level2_mask) | (Field.unwrap(val) << (256 - level2_bits)) >> level2_before);
      }}
      
      function level1(Local __packed) internal pure returns(Field) { unchecked {
        return Field.wrap(uint(Local.unwrap(__packed) << level1_before) >> (256 - level1_bits));
      }}
    
      // setters
      function level1(Local __packed,Field val) internal pure returns(Local) { unchecked {
        return Local.wrap((Local.unwrap(__packed) & level1_mask) | (Field.unwrap(val) << (256 - level1_bits)) >> level1_before);
      }}
      
      function root(Local __packed) internal pure returns(Field) { unchecked {
        return Field.wrap(uint(Local.unwrap(__packed) << root_before) >> (256 - root_bits));
      }}
    
      // setters
      function root(Local __packed,Field val) internal pure returns(Local) { unchecked {
        return Local.wrap((Local.unwrap(__packed) & root_mask) | (Field.unwrap(val) << (256 - root_bits)) >> root_before);
      }}
      
      function kilo_offer_gasbase(Local __packed) internal pure returns(uint) { unchecked {
        return uint(Local.unwrap(__packed) << kilo_offer_gasbase_before) >> (256 - kilo_offer_gasbase_bits);
      }}
    
      // setters
      function kilo_offer_gasbase(Local __packed,uint val) internal pure returns(Local) { unchecked {
        return Local.wrap((Local.unwrap(__packed) & kilo_offer_gasbase_mask) | (val << (256 - kilo_offer_gasbase_bits)) >> kilo_offer_gasbase_before);
      }}
      
      function lock(Local __packed) internal pure returns(bool) { unchecked {
        return ((Local.unwrap(__packed) & lock_mask_inv) > 0);
      }}
    
      // setters
      function lock(Local __packed,bool val) internal pure returns(Local) { unchecked {
        return Local.wrap((Local.unwrap(__packed) & lock_mask) | (uint_of_bool(val) << (256 - lock_bits)) >> lock_before);
      }}
      
      function last(Local __packed) internal pure returns(uint) { unchecked {
        return uint(Local.unwrap(__packed) << last_before) >> (256 - last_bits);
      }}
    
      // setters
      function last(Local __packed,uint val) internal pure returns(Local) { unchecked {
        return Local.wrap((Local.unwrap(__packed) & last_mask) | (val << (256 - last_bits)) >> last_before);
      }}
      
    
      // from in-memory struct to packed
      function t_of_struct(LocalUnpacked memory __s) internal pure returns (Local) { unchecked {
        return pack(__s.active, __s.fee, __s.density, __s.binPosInLeaf, __s.level3, __s.level2, __s.level1, __s.root, __s.kilo_offer_gasbase, __s.lock, __s.last);
      }}
    
      // from arguments to packed
      function pack(bool __active, uint __fee, Density __density, uint __binPosInLeaf, Field __level3, Field __level2, Field __level1, Field __root, uint __kilo_offer_gasbase, bool __lock, uint __last) internal pure returns (Local) { unchecked {
        uint __packed;
        __packed |= (uint_of_bool(__active) << (256 - active_bits)) >> active_before;
        __packed |= (__fee << (256 - fee_bits)) >> fee_before;
        __packed |= (Density.unwrap(__density) << (256 - density_bits)) >> density_before;
        __packed |= (__binPosInLeaf << (256 - binPosInLeaf_bits)) >> binPosInLeaf_before;
        __packed |= (Field.unwrap(__level3) << (256 - level3_bits)) >> level3_before;
        __packed |= (Field.unwrap(__level2) << (256 - level2_bits)) >> level2_before;
        __packed |= (Field.unwrap(__level1) << (256 - level1_bits)) >> level1_before;
        __packed |= (Field.unwrap(__root) << (256 - root_bits)) >> root_before;
        __packed |= (__kilo_offer_gasbase << (256 - kilo_offer_gasbase_bits)) >> kilo_offer_gasbase_before;
        __packed |= (uint_of_bool(__lock) << (256 - lock_bits)) >> lock_before;
        __packed |= (__last << (256 - last_bits)) >> last_before;
        return Local.wrap(__packed);
      }}
    
      // input checking
      function active_check(bool __active) internal pure returns (bool) { unchecked {
        return (uint_of_bool(__active) & active_cast_mask) == uint_of_bool(__active);
      }}
      function fee_check(uint __fee) internal pure returns (bool) { unchecked {
        return (__fee & fee_cast_mask) == __fee;
      }}
      function density_check(Density __density) internal pure returns (bool) { unchecked {
        return (Density.unwrap(__density) & density_cast_mask) == Density.unwrap(__density);
      }}
      function binPosInLeaf_check(uint __binPosInLeaf) internal pure returns (bool) { unchecked {
        return (__binPosInLeaf & binPosInLeaf_cast_mask) == __binPosInLeaf;
      }}
      function level3_check(Field __level3) internal pure returns (bool) { unchecked {
        return (Field.unwrap(__level3) & level3_cast_mask) == Field.unwrap(__level3);
      }}
      function level2_check(Field __level2) internal pure returns (bool) { unchecked {
        return (Field.unwrap(__level2) & level2_cast_mask) == Field.unwrap(__level2);
      }}
      function level1_check(Field __level1) internal pure returns (bool) { unchecked {
        return (Field.unwrap(__level1) & level1_cast_mask) == Field.unwrap(__level1);
      }}
      function root_check(Field __root) internal pure returns (bool) { unchecked {
        return (Field.unwrap(__root) & root_cast_mask) == Field.unwrap(__root);
      }}
      function kilo_offer_gasbase_check(uint __kilo_offer_gasbase) internal pure returns (bool) { unchecked {
        return (__kilo_offer_gasbase & kilo_offer_gasbase_cast_mask) == __kilo_offer_gasbase;
      }}
      function lock_check(bool __lock) internal pure returns (bool) { unchecked {
        return (uint_of_bool(__lock) & lock_cast_mask) == uint_of_bool(__lock);
      }}
      function last_check(uint __last) internal pure returns (bool) { unchecked {
        return (__last & last_cast_mask) == __last;
      }}
    }
    
    
  • node_modules/@mangrovedao/mangrove-core/lib/TransferLib.sol
    // SPDX-License-Identifier: MIT
    pragma solidity ^0.8.10;
    
    import "@mgv/src/core/MgvLib.sol";
    
    ///@title This library helps with safely interacting with ERC20 tokens
    ///@notice Transferring 0 or to self will be skipped.
    ///@notice ERC20 tokens returning bool instead of reverting are handled.
    library TransferLib {
      ///@notice This transfer amount of token to recipient address
      ///@param token Token to be transferred
      ///@param recipient Address of the recipient the tokens will be transferred to
      ///@param amount The amount of tokens to be transferred
      ///@return true if transfer was successful; otherwise, false.
      function transferToken(IERC20 token, address recipient, uint amount) internal returns (bool) {
        if (amount == 0) {
          return true;
        }
        if (recipient == address(this)) {
          return token.balanceOf(recipient) >= amount;
        }
        return _transferToken(token, recipient, amount);
      }
    
      ///@notice This transfer amount of token to recipient address
      ///@param token Token to be transferred
      ///@param recipient Address of the recipient the tokens will be transferred to
      ///@param amount The amount of tokens to be transferred
      ///@return true if transfer was successful; otherwise, false.
      function _transferToken(IERC20 token, address recipient, uint amount) private returns (bool) {
        // This low level call will not revert but instead return success=false if callee reverts, so we
        // verify that it does not revert by checking success, but we also have to check
        // the returned data if any since some ERC20 tokens to not strictly follow the standard of reverting
        // but instead return false.
        (bool success, bytes memory data) =
          address(token).call(abi.encodeWithSelector(token.transfer.selector, recipient, amount));
        return (success && (data.length == 0 || abi.decode(data, (bool))));
      }
    
      ///@notice This transfer amount of token to recipient address from spender address
      ///@param token Token to be transferred
      ///@param spender Address of the spender, where the tokens will be transferred from
      ///@param recipient Address of the recipient, where the tokens will be transferred to
      ///@param amount The amount of tokens to be transferred
      ///@return true if transfer was successful; otherwise, false.
      function transferTokenFrom(IERC20 token, address spender, address recipient, uint amount) internal returns (bool) {
        if (amount == 0) {
          return true;
        }
        if (spender == recipient) {
          return token.balanceOf(spender) >= amount;
        }
        // optimization to avoid requiring contract to approve itself
        if (spender == address(this)) {
          return _transferToken(token, recipient, amount);
        }
        return _transferTokenFrom(token, spender, recipient, amount);
      }
    
      ///@notice This transfer amount of token to recipient address from spender address
      ///@param token Token to be transferred
      ///@param spender Address of the spender, where the tokens will be transferred from
      ///@param recipient Address of the recipient, where the tokens will be transferred to
      ///@param amount The amount of tokens to be transferred
      ///@return true if transfer was successful; otherwise, false.
      function _transferTokenFrom(IERC20 token, address spender, address recipient, uint amount) private returns (bool) {
        // This low level call will not revert but instead return success=false if callee reverts, so we
        // verify that it does not revert by checking success, but we also have to check
        // the returned data if there since some ERC20 tokens to not strictly follow the standard of reverting
        // but instead return false.
        (bool success, bytes memory data) =
          address(token).call(abi.encodeWithSelector(token.transferFrom.selector, spender, recipient, amount));
        return (success && (data.length == 0 || abi.decode(data, (bool))));
      }
    
      ///@notice ERC20 approval, handling non standard approvals that do not return a value
      ///@param token the ERC20
      ///@param spender the address whose allowance is to be given
      ///@param amount of the allowance
      ///@return true if approval was successful; otherwise, false.
      function _approveToken(IERC20 token, address spender, uint amount) private returns (bool) {
        // This low level call will not revert but instead return success=false if callee reverts, so we
        // verify that it does not revert by checking success, but we also have to check
        // the returned data if any since some ERC20 tokens to not strictly follow the standard of reverting
        // but instead return false.
        (bool success, bytes memory data) =
          address(token).call(abi.encodeWithSelector(token.approve.selector, spender, amount));
        return (success && (data.length == 0 || abi.decode(data, (bool))));
      }
    
      ///@notice ERC20 approval, handling non standard approvals that do not return a value
      ///@param token the ERC20
      ///@param spender the address whose allowance is to be given
      ///@param amount of the allowance
      ///@return true if approval was successful; otherwise, false.
      function approveToken(IERC20 token, address spender, uint amount) internal returns (bool) {
        return _approveToken(token, spender, amount);
      }
    }
    
  • src/strategies/interfaces/IOfferLogic.sol
    // SPDX-License-Identifier: MIT
    pragma solidity >=0.8.10;
    
    import {IMangrove} from "@mgv/src/IMangrove.sol";
    import {IERC20, IMaker, OLKey} from "@mgv/src/core/MgvLib.sol";
    import {AbstractRouter, RL} from "@mgv-strats/src/strategies/routers/abstract/AbstractRouter.sol";
    import {Tick} from "@mgv/lib/core/TickLib.sol";
    
    ///@title IOfferLogic interface for offer management
    ///@notice It is an IMaker for Mangrove.
    
    interface IOfferLogic is IMaker {
      ///@notice Log incident (during post trade execution)
      ///@param olKeyHash the hash of the offer list key. This is indexed so that RPC calls can filter on it.
      ///@param offerId the Mangrove offer id. This is indexed so that RPC calls can filter on it.
      ///@param makerData from the maker.
      ///@param mgvData from Mangrove.
      ///@notice By emitting this data, an indexer can keep track of what incidents has happened.
      event LogIncident(bytes32 indexed olKeyHash, uint indexed offerId, bytes32 makerData, bytes32 mgvData);
    
      ///@notice Approves a spender to transfer a certain amount of tokens on behalf of `this`.
      ///@param token the ERC20 token contract
      ///@param spender the approved spender
      ///@param amount the spending amount
      ///@return result of token approval.
      ///@dev admin may use this function to revoke specific approvals of `this` that are set after a call to `activate`.
      function approve(IERC20 token, address spender, uint amount) external returns (bool);
    
      ///@notice Performs approval that are necessary to trade a given asset
      ///@param token the traded asset
      ///@dev anyone can call since only max approval can be set
      function activate(IERC20 token) external;
    
      ///@notice computes the amount of native tokens that can be redeemed when deprovisioning a given offer.
      ///@param olKey the offer list key.
      ///@param offerId the identifier of the offer in the offer list
      ///@return provision the amount of native tokens that can be redeemed when deprovisioning the offer
      function provisionOf(OLKey memory olKey, uint offerId) external view returns (uint provision);
    
      ///@notice withdraws native tokens from `this` balance on Mangrove.
      ///@param amount the amount of WEI one wishes to withdraw.
      ///@param receiver the address of the receiver of the funds.
      ///@dev Since a call is made to the `receiver`, this function is subject to reentrancy.
      function withdrawFromMangrove(uint amount, address payable receiver) external;
    
      ///@notice Memory allocation for `_new/updateOffer`'s arguments.
      ///@param olKey the offer list key.
      ///@param tick the tick.
      ///@param gives the amount of outbound tokens the maker gives for a complete fill.
      ///@param gasreq the amount of gas units that are required to execute the trade
      ///@param gasprice the gasprice used to compute offer's provision (use 0 to use Mangrove's gasprice)
      ///@param fund WEIs in `this` contract's balance that are used to provision the offer.
      ///@param noRevert is set to true if calling function does not wish `_newOffer` to revert on error.
      struct OfferArgs {
        OLKey olKey;
        Tick tick;
        uint gives;
        uint gasreq;
        uint gasprice;
        uint fund;
        bool noRevert;
      }
    
      /// @notice Contract's Mangrove getter
      /// @return the Mangrove contract.
      function MGV() external view returns (IMangrove);
    
      ///@notice returns the contract that routes owner's funds
      ///@param owner the fund owner
      ///@return router the router contract.
      ///@dev returns `AbstractRouter(address(0))` if the strat is not using a router.
      function router(address owner) external view returns (AbstractRouter router);
    }
    
  • src/strategies/TakerOrderLib.sol
    // SPDX-License-Identifier:	MIT
    pragma solidity >=0.8.10;
    
    /// TakerOrderType is an enum that represents the type of a taker order.
    /// @param GTC Good till cancelled -> This order remains active until it is either filled or canceled by the trader.
    /// * If an expiry date is set, This will be a GTD (Good till date) order.
    /// * this will try a market order first, and if it is partially filled but the resting order fails to be posted, it won't revert the transaction.
    /// @param GTCE Good till cancelled enforced -> This order remains active until it is either filled or canceled by the trader. It doesn't have the restriction of avoiding immediate execution.
    /// * If the resting order fails to be posted, it will revert the transaction.
    /// @param PO Post only -> This order will not execute immediately against the market.
    /// @param IOC Immediate or cancel -> This order must be filled immediately at the limit price or better. If the full order cannot be filled, the unfilled portion is canceled.
    /// @param FOK Fill or kill -> This order must be filled in its entirety immediately at the limit price or better, or it is entirely canceled. There is no partial fulfillment.
    
    ///@dev This enum is used to represent the type of a taker order.
    ///@dev invariant must be that TakerOrderType >= 3 won't try posting a resting order.
    enum TakerOrderType {
      GTC, // = 0
      GTCE, // = 1
      PO, // = 2
      IOC, // = 3
      FOK // = 4
    
    }
    
    using TakerOrderTypeLib for TakerOrderType global;
    
    /// @title Library for TakerOrderType
    /// @notice This library contains functions to check the compatibility of an order type with a resting or market order.
    library TakerOrderTypeLib {
      /// @notice whether the order type requires a resting order.
      /// i.e. GTC, GTCE, PO
      /// @param orderType the order type
      /// @return postRestingOrder true if the order type is requires a resting order.
      function postRestingOrder(TakerOrderType orderType) internal pure returns (bool) {
        return uint8(orderType) < 3;
      }
    
      /// @notice whether the order type is requires a resting order, depending on whether the order was filled.
      /// @param orderType the order type
      /// @param filled true if the order is filled
      /// @return postRestingOrder true if the order's residual should be posted as a resting order.
      function postRestingOrder(TakerOrderType orderType, bool filled) internal pure returns (bool) {
        return !filled && orderType.postRestingOrder();
      }
    
      /// @notice whether the order type starts with a market order.
      /// i.e. GTC, GTCE, IOC, FOK
      /// @param orderType the order type
      /// @return executesMarketOrder true if the order type is compatible with a market order.
      function executesMarketOrder(TakerOrderType orderType) internal pure returns (bool) {
        return orderType != TakerOrderType.PO;
      }
    
      /// @notice whether the market order is successful according to the order type.
      /// @param orderType the order type
      /// @param filled true if the order is filled
      /// @return marketOrderSucceded true if the market order is successful according to the order type.
      function marketOrderSucceded(TakerOrderType orderType, bool filled) internal pure returns (bool) {
        // if post resting order is true, this means
        return filled || orderType.postRestingOrder() || orderType == TakerOrderType.IOC;
      }
    
      /// @notice whether order is PO
      /// @param orderType the order type
      /// @return shouldExecuteMarketOrder true if the market order should be executed.
      function shouldExecuteMarketOrder(TakerOrderType orderType) internal pure returns (bool) {
        return orderType != TakerOrderType.PO;
      }
    
      /// @notice whether the order requires posting a resting order to be successful.
      /// i.e. GTCE (Good till cancelled enforced) and PO (Post Only)
      /// @param orderType the order type
      /// @return enforcePostRestingOrder true if the order requires posting the resting order to be successful.
      function enforcePostRestingOrder(TakerOrderType orderType) internal pure returns (bool) {
        return orderType == TakerOrderType.GTCE || orderType == TakerOrderType.PO;
      }
    }
    
  • node_modules/@mangrovedao/mangrove-core/src/preprocessed/Offer.post.sol
    // SPDX-License-Identifier: MIT
    
    pragma solidity ^0.8.13;
    
    /* ************************************************** *
                GENERATED FILE. DO NOT EDIT.
     * ************************************************** */
    
    /* since you can't convert bool to uint in an expression without conditionals,
     * we add a file-level function and rely on compiler optimization
     */
    function uint_of_bool(bool b) pure returns (uint u) {
      assembly ("memory-safe") { u := b }
    }
    import "@mgv/lib/core/Constants.sol";
    
    struct OfferUnpacked {
      uint prev;
      uint next;
      Tick tick;
      uint gives;
    }
    
    //some type safety for each struct
    type Offer is uint;
    using OfferLib for Offer global;
    
    ////////////// ADDITIONAL DEFINITIONS, IF ANY ////////////////
    import {Bin} from "@mgv/lib/core/TickTreeLib.sol";
    import {Tick} from "@mgv/lib/core/TickLib.sol";
    import {OfferExtra,OfferUnpackedExtra} from "@mgv/lib/core/OfferExtra.sol";
    
    using OfferExtra for Offer global;
    using OfferUnpackedExtra for OfferUnpacked global;
    
    ////////////// END OF ADDITIONAL DEFINITIONS /////////////////
    
    library OfferLib {
    
      // number of bits in each field
      uint constant prev_bits  = 32;
      uint constant next_bits  = 32;
      uint constant tick_bits  = 21;
      uint constant gives_bits = 127;
    
      // number of bits before each field
      uint constant prev_before  = 0           + 0;
      uint constant next_before  = prev_before + prev_bits;
      uint constant tick_before  = next_before + next_bits;
      uint constant gives_before = tick_before + tick_bits;
    
      // focus-mask: 1s at field location, 0s elsewhere
      uint constant prev_mask_inv  = (ONES << 256 - prev_bits) >> prev_before;
      uint constant next_mask_inv  = (ONES << 256 - next_bits) >> next_before;
      uint constant tick_mask_inv  = (ONES << 256 - tick_bits) >> tick_before;
      uint constant gives_mask_inv = (ONES << 256 - gives_bits) >> gives_before;
    
      // cleanup-mask: 0s at field location, 1s elsewhere
      uint constant prev_mask  = ~prev_mask_inv;
      uint constant next_mask  = ~next_mask_inv;
      uint constant tick_mask  = ~tick_mask_inv;
      uint constant gives_mask = ~gives_mask_inv;
    
      // cast-mask: 0s followed by |field| trailing 1s
      uint constant prev_cast_mask  = ~(ONES << prev_bits);
      uint constant next_cast_mask  = ~(ONES << next_bits);
      uint constant tick_cast_mask  = ~(ONES << tick_bits);
      uint constant gives_cast_mask = ~(ONES << gives_bits);
    
      // size-related error message
      string constant prev_size_error  = "mgv/config/prev/32bits";
      string constant next_size_error  = "mgv/config/next/32bits";
      string constant tick_size_error  = "mgv/config/tick/21bits";
      string constant gives_size_error = "mgv/config/gives/127bits";
    
      // from packed to in-memory struct
      function to_struct(Offer __packed) internal pure returns (OfferUnpacked memory __s) { unchecked {
        __s.prev  = uint(Offer.unwrap(__packed) << prev_before) >> (256 - prev_bits);
        __s.next  = uint(Offer.unwrap(__packed) << next_before) >> (256 - next_bits);
        __s.tick  = Tick.wrap(int(int(Offer.unwrap(__packed) << tick_before) >> (256 - tick_bits)));
        __s.gives = uint(Offer.unwrap(__packed) << gives_before) >> (256 - gives_bits);
      }}
    
      // equality checking
      function eq(Offer __packed1, Offer __packed2) internal pure returns (bool) { unchecked {
        return Offer.unwrap(__packed1) == Offer.unwrap(__packed2);
      }}
    
      // from packed to a tuple
      function unpack(Offer __packed) internal pure returns (uint __prev, uint __next, Tick __tick, uint __gives) { unchecked {
        __prev  = uint(Offer.unwrap(__packed) << prev_before) >> (256 - prev_bits);
        __next  = uint(Offer.unwrap(__packed) << next_before) >> (256 - next_bits);
        __tick  = Tick.wrap(int(int(Offer.unwrap(__packed) << tick_before) >> (256 - tick_bits)));
        __gives = uint(Offer.unwrap(__packed) << gives_before) >> (256 - gives_bits);
      }}
    
      // getters
      function prev(Offer __packed) internal pure returns(uint) { unchecked {
        return uint(Offer.unwrap(__packed) << prev_before) >> (256 - prev_bits);
      }}
    
      // setters
      function prev(Offer __packed,uint val) internal pure returns(Offer) { unchecked {
        return Offer.wrap((Offer.unwrap(__packed) & prev_mask) | (val << (256 - prev_bits)) >> prev_before);
      }}
      
      function next(Offer __packed) internal pure returns(uint) { unchecked {
        return uint(Offer.unwrap(__packed) << next_before) >> (256 - next_bits);
      }}
    
      // setters
      function next(Offer __packed,uint val) internal pure returns(Offer) { unchecked {
        return Offer.wrap((Offer.unwrap(__packed) & next_mask) | (val << (256 - next_bits)) >> next_before);
      }}
      
      function tick(Offer __packed) internal pure returns(Tick) { unchecked {
        return Tick.wrap(int(int(Offer.unwrap(__packed) << tick_before) >> (256 - tick_bits)));
      }}
    
      // setters
      function tick(Offer __packed,Tick val) internal pure returns(Offer) { unchecked {
        return Offer.wrap((Offer.unwrap(__packed) & tick_mask) | (uint(Tick.unwrap(val)) << (256 - tick_bits)) >> tick_before);
      }}
      
      function gives(Offer __packed) internal pure returns(uint) { unchecked {
        return uint(Offer.unwrap(__packed) << gives_before) >> (256 - gives_bits);
      }}
    
      // setters
      function gives(Offer __packed,uint val) internal pure returns(Offer) { unchecked {
        return Offer.wrap((Offer.unwrap(__packed) & gives_mask) | (val << (256 - gives_bits)) >> gives_before);
      }}
      
    
      // from in-memory struct to packed
      function t_of_struct(OfferUnpacked memory __s) internal pure returns (Offer) { unchecked {
        return pack(__s.prev, __s.next, __s.tick, __s.gives);
      }}
    
      // from arguments to packed
      function pack(uint __prev, uint __next, Tick __tick, uint __gives) internal pure returns (Offer) { unchecked {
        uint __packed;
        __packed |= (__prev << (256 - prev_bits)) >> prev_before;
        __packed |= (__next << (256 - next_bits)) >> next_before;
        __packed |= (uint(Tick.unwrap(__tick)) << (256 - tick_bits)) >> tick_before;
        __packed |= (__gives << (256 - gives_bits)) >> gives_before;
        return Offer.wrap(__packed);
      }}
    
      // input checking
      function prev_check(uint __prev) internal pure returns (bool) { unchecked {
        return (__prev & prev_cast_mask) == __prev;
      }}
      function next_check(uint __next) internal pure returns (bool) { unchecked {
        return (__next & next_cast_mask) == __next;
      }}
      function tick_check(Tick __tick) internal pure returns (bool) { unchecked {
        return (uint(Tick.unwrap(__tick)) & tick_cast_mask) == uint(Tick.unwrap(__tick));
      }}
      function gives_check(uint __gives) internal pure returns (bool) { unchecked {
        return (__gives & gives_cast_mask) == __gives;
      }}
    }
    
    
  • node_modules/@mangrovedao/mangrove-core/src/preprocessed/Global.post.sol
    // SPDX-License-Identifier: MIT
    
    pragma solidity ^0.8.13;
    
    /* ************************************************** *
                GENERATED FILE. DO NOT EDIT.
     * ************************************************** */
    
    /* since you can't convert bool to uint in an expression without conditionals,
     * we add a file-level function and rely on compiler optimization
     */
    function uint_of_bool(bool b) pure returns (uint u) {
      assembly ("memory-safe") { u := b }
    }
    import "@mgv/lib/core/Constants.sol";
    
    struct GlobalUnpacked {
      address monitor;
      bool useOracle;
      bool notify;
      uint gasprice;
      uint gasmax;
      bool dead;
      uint maxRecursionDepth;
      uint maxGasreqForFailingOffers;
    }
    
    //some type safety for each struct
    type Global is uint;
    using GlobalLib for Global global;
    
    ////////////// ADDITIONAL DEFINITIONS, IF ANY ////////////////
    
    ////////////// END OF ADDITIONAL DEFINITIONS /////////////////
    
    library GlobalLib {
    
      // number of bits in each field
      uint constant monitor_bits                   = 160;
      uint constant useOracle_bits                 = 1;
      uint constant notify_bits                    = 1;
      uint constant gasprice_bits                  = 26;
      uint constant gasmax_bits                    = 24;
      uint constant dead_bits                      = 1;
      uint constant maxRecursionDepth_bits         = 8;
      uint constant maxGasreqForFailingOffers_bits = 32;
    
      // number of bits before each field
      uint constant monitor_before                   = 0                        + 0;
      uint constant useOracle_before                 = monitor_before           + monitor_bits;
      uint constant notify_before                    = useOracle_before         + useOracle_bits;
      uint constant gasprice_before                  = notify_before            + notify_bits;
      uint constant gasmax_before                    = gasprice_before          + gasprice_bits;
      uint constant dead_before                      = gasmax_before            + gasmax_bits;
      uint constant maxRecursionDepth_before         = dead_before              + dead_bits;
      uint constant maxGasreqForFailingOffers_before = maxRecursionDepth_before + maxRecursionDepth_bits;
    
      // focus-mask: 1s at field location, 0s elsewhere
      uint constant monitor_mask_inv                   = (ONES << 256 - monitor_bits) >> monitor_before;
      uint constant useOracle_mask_inv                 = (ONES << 256 - useOracle_bits) >> useOracle_before;
      uint constant notify_mask_inv                    = (ONES << 256 - notify_bits) >> notify_before;
      uint constant gasprice_mask_inv                  = (ONES << 256 - gasprice_bits) >> gasprice_before;
      uint constant gasmax_mask_inv                    = (ONES << 256 - gasmax_bits) >> gasmax_before;
      uint constant dead_mask_inv                      = (ONES << 256 - dead_bits) >> dead_before;
      uint constant maxRecursionDepth_mask_inv         = (ONES << 256 - maxRecursionDepth_bits) >> maxRecursionDepth_before;
      uint constant maxGasreqForFailingOffers_mask_inv = (ONES << 256 - maxGasreqForFailingOffers_bits) >> maxGasreqForFailingOffers_before;
    
      // cleanup-mask: 0s at field location, 1s elsewhere
      uint constant monitor_mask                   = ~monitor_mask_inv;
      uint constant useOracle_mask                 = ~useOracle_mask_inv;
      uint constant notify_mask                    = ~notify_mask_inv;
      uint constant gasprice_mask                  = ~gasprice_mask_inv;
      uint constant gasmax_mask                    = ~gasmax_mask_inv;
      uint constant dead_mask                      = ~dead_mask_inv;
      uint constant maxRecursionDepth_mask         = ~maxRecursionDepth_mask_inv;
      uint constant maxGasreqForFailingOffers_mask = ~maxGasreqForFailingOffers_mask_inv;
    
      // cast-mask: 0s followed by |field| trailing 1s
      uint constant monitor_cast_mask                   = ~(ONES << monitor_bits);
      uint constant useOracle_cast_mask                 = ~(ONES << useOracle_bits);
      uint constant notify_cast_mask                    = ~(ONES << notify_bits);
      uint constant gasprice_cast_mask                  = ~(ONES << gasprice_bits);
      uint constant gasmax_cast_mask                    = ~(ONES << gasmax_bits);
      uint constant dead_cast_mask                      = ~(ONES << dead_bits);
      uint constant maxRecursionDepth_cast_mask         = ~(ONES << maxRecursionDepth_bits);
      uint constant maxGasreqForFailingOffers_cast_mask = ~(ONES << maxGasreqForFailingOffers_bits);
    
      // size-related error message
      string constant monitor_size_error                   = "mgv/config/monitor/160bits";
      string constant useOracle_size_error                 = "mgv/config/useOracle/1bits";
      string constant notify_size_error                    = "mgv/config/notify/1bits";
      string constant gasprice_size_error                  = "mgv/config/gasprice/26bits";
      string constant gasmax_size_error                    = "mgv/config/gasmax/24bits";
      string constant dead_size_error                      = "mgv/config/dead/1bits";
      string constant maxRecursionDepth_size_error         = "mgv/config/maxRecursionDepth/8bits";
      string constant maxGasreqForFailingOffers_size_error = "mgv/config/maxGasreqForFailingOffers/32bits";
    
      // from packed to in-memory struct
      function to_struct(Global __packed) internal pure returns (GlobalUnpacked memory __s) { unchecked {
        __s.monitor                   = address(uint160(uint(Global.unwrap(__packed) << monitor_before) >> (256 - monitor_bits)));
        __s.useOracle                 = ((Global.unwrap(__packed) & useOracle_mask_inv) > 0);
        __s.notify                    = ((Global.unwrap(__packed) & notify_mask_inv) > 0);
        __s.gasprice                  = uint(Global.unwrap(__packed) << gasprice_before) >> (256 - gasprice_bits);
        __s.gasmax                    = uint(Global.unwrap(__packed) << gasmax_before) >> (256 - gasmax_bits);
        __s.dead                      = ((Global.unwrap(__packed) & dead_mask_inv) > 0);
        __s.maxRecursionDepth         = uint(Global.unwrap(__packed) << maxRecursionDepth_before) >> (256 - maxRecursionDepth_bits);
        __s.maxGasreqForFailingOffers = uint(Global.unwrap(__packed) << maxGasreqForFailingOffers_before) >> (256 - maxGasreqForFailingOffers_bits);
      }}
    
      // equality checking
      function eq(Global __packed1, Global __packed2) internal pure returns (bool) { unchecked {
        return Global.unwrap(__packed1) == Global.unwrap(__packed2);
      }}
    
      // from packed to a tuple
      function unpack(Global __packed) internal pure returns (address __monitor, bool __useOracle, bool __notify, uint __gasprice, uint __gasmax, bool __dead, uint __maxRecursionDepth, uint __maxGasreqForFailingOffers) { unchecked {
        __monitor                   = address(uint160(uint(Global.unwrap(__packed) << monitor_before) >> (256 - monitor_bits)));
        __useOracle                 = ((Global.unwrap(__packed) & useOracle_mask_inv) > 0);
        __notify                    = ((Global.unwrap(__packed) & notify_mask_inv) > 0);
        __gasprice                  = uint(Global.unwrap(__packed) << gasprice_before) >> (256 - gasprice_bits);
        __gasmax                    = uint(Global.unwrap(__packed) << gasmax_before) >> (256 - gasmax_bits);
        __dead                      = ((Global.unwrap(__packed) & dead_mask_inv) > 0);
        __maxRecursionDepth         = uint(Global.unwrap(__packed) << maxRecursionDepth_before) >> (256 - maxRecursionDepth_bits);
        __maxGasreqForFailingOffers = uint(Global.unwrap(__packed) << maxGasreqForFailingOffers_before) >> (256 - maxGasreqForFailingOffers_bits);
      }}
    
      // getters
      function monitor(Global __packed) internal pure returns(address) { unchecked {
        return address(uint160(uint(Global.unwrap(__packed) << monitor_before) >> (256 - monitor_bits)));
      }}
    
      // setters
      function monitor(Global __packed,address val) internal pure returns(Global) { unchecked {
        return Global.wrap((Global.unwrap(__packed) & monitor_mask) | (uint(uint160(val)) << (256 - monitor_bits)) >> monitor_before);
      }}
      
      function useOracle(Global __packed) internal pure returns(bool) { unchecked {
        return ((Global.unwrap(__packed) & useOracle_mask_inv) > 0);
      }}
    
      // setters
      function useOracle(Global __packed,bool val) internal pure returns(Global) { unchecked {
        return Global.wrap((Global.unwrap(__packed) & useOracle_mask) | (uint_of_bool(val) << (256 - useOracle_bits)) >> useOracle_before);
      }}
      
      function notify(Global __packed) internal pure returns(bool) { unchecked {
        return ((Global.unwrap(__packed) & notify_mask_inv) > 0);
      }}
    
      // setters
      function notify(Global __packed,bool val) internal pure returns(Global) { unchecked {
        return Global.wrap((Global.unwrap(__packed) & notify_mask) | (uint_of_bool(val) << (256 - notify_bits)) >> notify_before);
      }}
      
      function gasprice(Global __packed) internal pure returns(uint) { unchecked {
        return uint(Global.unwrap(__packed) << gasprice_before) >> (256 - gasprice_bits);
      }}
    
      // setters
      function gasprice(Global __packed,uint val) internal pure returns(Global) { unchecked {
        return Global.wrap((Global.unwrap(__packed) & gasprice_mask) | (val << (256 - gasprice_bits)) >> gasprice_before);
      }}
      
      function gasmax(Global __packed) internal pure returns(uint) { unchecked {
        return uint(Global.unwrap(__packed) << gasmax_before) >> (256 - gasmax_bits);
      }}
    
      // setters
      function gasmax(Global __packed,uint val) internal pure returns(Global) { unchecked {
        return Global.wrap((Global.unwrap(__packed) & gasmax_mask) | (val << (256 - gasmax_bits)) >> gasmax_before);
      }}
      
      function dead(Global __packed) internal pure returns(bool) { unchecked {
        return ((Global.unwrap(__packed) & dead_mask_inv) > 0);
      }}
    
      // setters
      function dead(Global __packed,bool val) internal pure returns(Global) { unchecked {
        return Global.wrap((Global.unwrap(__packed) & dead_mask) | (uint_of_bool(val) << (256 - dead_bits)) >> dead_before);
      }}
      
      function maxRecursionDepth(Global __packed) internal pure returns(uint) { unchecked {
        return uint(Global.unwrap(__packed) << maxRecursionDepth_before) >> (256 - maxRecursionDepth_bits);
      }}
    
      // setters
      function maxRecursionDepth(Global __packed,uint val) internal pure returns(Global) { unchecked {
        return Global.wrap((Global.unwrap(__packed) & maxRecursionDepth_mask) | (val << (256 - maxRecursionDepth_bits)) >> maxRecursionDepth_before);
      }}
      
      function maxGasreqForFailingOffers(Global __packed) internal pure returns(uint) { unchecked {
        return uint(Global.unwrap(__packed) << maxGasreqForFailingOffers_before) >> (256 - maxGasreqForFailingOffers_bits);
      }}
    
      // setters
      function maxGasreqForFailingOffers(Global __packed,uint val) internal pure returns(Global) { unchecked {
        return Global.wrap((Global.unwrap(__packed) & maxGasreqForFailingOffers_mask) | (val << (256 - maxGasreqForFailingOffers_bits)) >> maxGasreqForFailingOffers_before);
      }}
      
    
      // from in-memory struct to packed
      function t_of_struct(GlobalUnpacked memory __s) internal pure returns (Global) { unchecked {
        return pack(__s.monitor, __s.useOracle, __s.notify, __s.gasprice, __s.gasmax, __s.dead, __s.maxRecursionDepth, __s.maxGasreqForFailingOffers);
      }}
    
      // from arguments to packed
      function pack(address __monitor, bool __useOracle, bool __notify, uint __gasprice, uint __gasmax, bool __dead, uint __maxRecursionDepth, uint __maxGasreqForFailingOffers) internal pure returns (Global) { unchecked {
        uint __packed;
        __packed |= (uint(uint160(__monitor)) << (256 - monitor_bits)) >> monitor_before;
        __packed |= (uint_of_bool(__useOracle) << (256 - useOracle_bits)) >> useOracle_before;
        __packed |= (uint_of_bool(__notify) << (256 - notify_bits)) >> notify_before;
        __packed |= (__gasprice << (256 - gasprice_bits)) >> gasprice_before;
        __packed |= (__gasmax << (256 - gasmax_bits)) >> gasmax_before;
        __packed |= (uint_of_bool(__dead) << (256 - dead_bits)) >> dead_before;
        __packed |= (__maxRecursionDepth << (256 - maxRecursionDepth_bits)) >> maxRecursionDepth_before;
        __packed |= (__maxGasreqForFailingOffers << (256 - maxGasreqForFailingOffers_bits)) >> maxGasreqForFailingOffers_before;
        return Global.wrap(__packed);
      }}
    
      // input checking
      function monitor_check(address __monitor) internal pure returns (bool) { unchecked {
        return (uint(uint160(__monitor)) & monitor_cast_mask) == uint(uint160(__monitor));
      }}
      function useOracle_check(bool __useOracle) internal pure returns (bool) { unchecked {
        return (uint_of_bool(__useOracle) & useOracle_cast_mask) == uint_of_bool(__useOracle);
      }}
      function notify_check(bool __notify) internal pure returns (bool) { unchecked {
        return (uint_of_bool(__notify) & notify_cast_mask) == uint_of_bool(__notify);
      }}
      function gasprice_check(uint __gasprice) internal pure returns (bool) { unchecked {
        return (__gasprice & gasprice_cast_mask) == __gasprice;
      }}
      function gasmax_check(uint __gasmax) internal pure returns (bool) { unchecked {
        return (__gasmax & gasmax_cast_mask) == __gasmax;
      }}
      function dead_check(bool __dead) internal pure returns (bool) { unchecked {
        return (uint_of_bool(__dead) & dead_cast_mask) == uint_of_bool(__dead);
      }}
      function maxRecursionDepth_check(uint __maxRecursionDepth) internal pure returns (bool) { unchecked {
        return (__maxRecursionDepth & maxRecursionDepth_cast_mask) == __maxRecursionDepth;
      }}
      function maxGasreqForFailingOffers_check(uint __maxGasreqForFailingOffers) internal pure returns (bool) { unchecked {
        return (__maxGasreqForFailingOffers & maxGasreqForFailingOffers_cast_mask) == __maxGasreqForFailingOffers;
      }}
    }
    
    
  • node_modules/@mangrovedao/mangrove-core/src/preprocessed/OfferDetail.post.sol
    // SPDX-License-Identifier: MIT
    
    pragma solidity ^0.8.13;
    
    /* ************************************************** *
                GENERATED FILE. DO NOT EDIT.
     * ************************************************** */
    
    /* since you can't convert bool to uint in an expression without conditionals,
     * we add a file-level function and rely on compiler optimization
     */
    function uint_of_bool(bool b) pure returns (uint u) {
      assembly ("memory-safe") { u := b }
    }
    import "@mgv/lib/core/Constants.sol";
    
    struct OfferDetailUnpacked {
      address maker;
      uint gasreq;
      uint kilo_offer_gasbase;
      uint gasprice;
    }
    
    //some type safety for each struct
    type OfferDetail is uint;
    using OfferDetailLib for OfferDetail global;
    
    ////////////// ADDITIONAL DEFINITIONS, IF ANY ////////////////
    import {OfferDetailExtra,OfferDetailUnpackedExtra} from "@mgv/lib/core/OfferDetailExtra.sol";
    using OfferDetailExtra for OfferDetail global;
    using OfferDetailUnpackedExtra for OfferDetailUnpacked global;
    
    ////////////// END OF ADDITIONAL DEFINITIONS /////////////////
    
    library OfferDetailLib {
    
      // number of bits in each field
      uint constant maker_bits              = 160;
      uint constant gasreq_bits             = 24;
      uint constant kilo_offer_gasbase_bits = 9;
      uint constant gasprice_bits           = 26;
    
      // number of bits before each field
      uint constant maker_before              = 0                         + 0;
      uint constant gasreq_before             = maker_before              + maker_bits;
      uint constant kilo_offer_gasbase_before = gasreq_before             + gasreq_bits;
      uint constant gasprice_before           = kilo_offer_gasbase_before + kilo_offer_gasbase_bits;
    
      // focus-mask: 1s at field location, 0s elsewhere
      uint constant maker_mask_inv              = (ONES << 256 - maker_bits) >> maker_before;
      uint constant gasreq_mask_inv             = (ONES << 256 - gasreq_bits) >> gasreq_before;
      uint constant kilo_offer_gasbase_mask_inv = (ONES << 256 - kilo_offer_gasbase_bits) >> kilo_offer_gasbase_before;
      uint constant gasprice_mask_inv           = (ONES << 256 - gasprice_bits) >> gasprice_before;
    
      // cleanup-mask: 0s at field location, 1s elsewhere
      uint constant maker_mask              = ~maker_mask_inv;
      uint constant gasreq_mask             = ~gasreq_mask_inv;
      uint constant kilo_offer_gasbase_mask = ~kilo_offer_gasbase_mask_inv;
      uint constant gasprice_mask           = ~gasprice_mask_inv;
    
      // cast-mask: 0s followed by |field| trailing 1s
      uint constant maker_cast_mask              = ~(ONES << maker_bits);
      uint constant gasreq_cast_mask             = ~(ONES << gasreq_bits);
      uint constant kilo_offer_gasbase_cast_mask = ~(ONES << kilo_offer_gasbase_bits);
      uint constant gasprice_cast_mask           = ~(ONES << gasprice_bits);
    
      // size-related error message
      string constant maker_size_error              = "mgv/config/maker/160bits";
      string constant gasreq_size_error             = "mgv/config/gasreq/24bits";
      string constant kilo_offer_gasbase_size_error = "mgv/config/kilo_offer_gasbase/9bits";
      string constant gasprice_size_error           = "mgv/config/gasprice/26bits";
    
      // from packed to in-memory struct
      function to_struct(OfferDetail __packed) internal pure returns (OfferDetailUnpacked memory __s) { unchecked {
        __s.maker              = address(uint160(uint(OfferDetail.unwrap(__packed) << maker_before) >> (256 - maker_bits)));
        __s.gasreq             = uint(OfferDetail.unwrap(__packed) << gasreq_before) >> (256 - gasreq_bits);
        __s.kilo_offer_gasbase = uint(OfferDetail.unwrap(__packed) << kilo_offer_gasbase_before) >> (256 - kilo_offer_gasbase_bits);
        __s.gasprice           = uint(OfferDetail.unwrap(__packed) << gasprice_before) >> (256 - gasprice_bits);
      }}
    
      // equality checking
      function eq(OfferDetail __packed1, OfferDetail __packed2) internal pure returns (bool) { unchecked {
        return OfferDetail.unwrap(__packed1) == OfferDetail.unwrap(__packed2);
      }}
    
      // from packed to a tuple
      function unpack(OfferDetail __packed) internal pure returns (address __maker, uint __gasreq, uint __kilo_offer_gasbase, uint __gasprice) { unchecked {
        __maker              = address(uint160(uint(OfferDetail.unwrap(__packed) << maker_before) >> (256 - maker_bits)));
        __gasreq             = uint(OfferDetail.unwrap(__packed) << gasreq_before) >> (256 - gasreq_bits);
        __kilo_offer_gasbase = uint(OfferDetail.unwrap(__packed) << kilo_offer_gasbase_before) >> (256 - kilo_offer_gasbase_bits);
        __gasprice           = uint(OfferDetail.unwrap(__packed) << gasprice_before) >> (256 - gasprice_bits);
      }}
    
      // getters
      function maker(OfferDetail __packed) internal pure returns(address) { unchecked {
        return address(uint160(uint(OfferDetail.unwrap(__packed) << maker_before) >> (256 - maker_bits)));
      }}
    
      // setters
      function maker(OfferDetail __packed,address val) internal pure returns(OfferDetail) { unchecked {
        return OfferDetail.wrap((OfferDetail.unwrap(__packed) & maker_mask) | (uint(uint160(val)) << (256 - maker_bits)) >> maker_before);
      }}
      
      function gasreq(OfferDetail __packed) internal pure returns(uint) { unchecked {
        return uint(OfferDetail.unwrap(__packed) << gasreq_before) >> (256 - gasreq_bits);
      }}
    
      // setters
      function gasreq(OfferDetail __packed,uint val) internal pure returns(OfferDetail) { unchecked {
        return OfferDetail.wrap((OfferDetail.unwrap(__packed) & gasreq_mask) | (val << (256 - gasreq_bits)) >> gasreq_before);
      }}
      
      function kilo_offer_gasbase(OfferDetail __packed) internal pure returns(uint) { unchecked {
        return uint(OfferDetail.unwrap(__packed) << kilo_offer_gasbase_before) >> (256 - kilo_offer_gasbase_bits);
      }}
    
      // setters
      function kilo_offer_gasbase(OfferDetail __packed,uint val) internal pure returns(OfferDetail) { unchecked {
        return OfferDetail.wrap((OfferDetail.unwrap(__packed) & kilo_offer_gasbase_mask) | (val << (256 - kilo_offer_gasbase_bits)) >> kilo_offer_gasbase_before);
      }}
      
      function gasprice(OfferDetail __packed) internal pure returns(uint) { unchecked {
        return uint(OfferDetail.unwrap(__packed) << gasprice_before) >> (256 - gasprice_bits);
      }}
    
      // setters
      function gasprice(OfferDetail __packed,uint val) internal pure returns(OfferDetail) { unchecked {
        return OfferDetail.wrap((OfferDetail.unwrap(__packed) & gasprice_mask) | (val << (256 - gasprice_bits)) >> gasprice_before);
      }}
      
    
      // from in-memory struct to packed
      function t_of_struct(OfferDetailUnpacked memory __s) internal pure returns (OfferDetail) { unchecked {
        return pack(__s.maker, __s.gasreq, __s.kilo_offer_gasbase, __s.gasprice);
      }}
    
      // from arguments to packed
      function pack(address __maker, uint __gasreq, uint __kilo_offer_gasbase, uint __gasprice) internal pure returns (OfferDetail) { unchecked {
        uint __packed;
        __packed |= (uint(uint160(__maker)) << (256 - maker_bits)) >> maker_before;
        __packed |= (__gasreq << (256 - gasreq_bits)) >> gasreq_before;
        __packed |= (__kilo_offer_gasbase << (256 - kilo_offer_gasbase_bits)) >> kilo_offer_gasbase_before;
        __packed |= (__gasprice << (256 - gasprice_bits)) >> gasprice_before;
        return OfferDetail.wrap(__packed);
      }}
    
      // input checking
      function maker_check(address __maker) internal pure returns (bool) { unchecked {
        return (uint(uint160(__maker)) & maker_cast_mask) == uint(uint160(__maker));
      }}
      function gasreq_check(uint __gasreq) internal pure returns (bool) { unchecked {
        return (__gasreq & gasreq_cast_mask) == __gasreq;
      }}
      function kilo_offer_gasbase_check(uint __kilo_offer_gasbase) internal pure returns (bool) { unchecked {
        return (__kilo_offer_gasbase & kilo_offer_gasbase_cast_mask) == __kilo_offer_gasbase;
      }}
      function gasprice_check(uint __gasprice) internal pure returns (bool) { unchecked {
        return (__gasprice & gasprice_cast_mask) == __gasprice;
      }}
    }
    
    
  • src/strategies/routers/RouterProxyFactory.sol
    // SPDX-License-Identifier: MIT
    pragma solidity ^0.8.18;
    
    import {RouterProxy, AbstractRouter} from "./RouterProxy.sol";
    
    /// @title Mangrove Router Proxy Factory
    /// @notice Factory contract for the deployment of RouterProxy instances using CREATE2 for deterministic addresses.
    /// @dev Utilizes Ethereum's CREATE2 opcode for deploying contracts with predictable addresses.
    contract RouterProxyFactory {
      /// @notice Emitted when a new proxy is deployed through this factory.
      /// @param proxy the deployed proxy contract
      /// @param owner The address which will be the admin and owner of the newly deployed proxy.
      /// @param implementation The address of the router implementation used by the proxy.
      event ProxyDeployed(RouterProxy proxy, address indexed owner, AbstractRouter indexed implementation);
    
      /// @notice Computes the deterministic address of a proxy deployed for a specific owner using CREATE2.
      /// @param owner The prospective admin and owner of the new proxy contract.
      /// @param routerImplementation router contract which implements routing functions
      /// @return The address where the proxy will be deployed.
      /// @dev The computed address is determined by the owner's address and the factory's address
      function computeProxyAddress(address owner, AbstractRouter routerImplementation)
        public
        view
        returns (address payable)
      {
        bytes memory creationCode = type(RouterProxy).creationCode;
        bytes memory args = abi.encode(routerImplementation);
        bytes32 initcodeHash = keccak256(abi.encodePacked(creationCode, args));
        bytes32 salt = keccak256(abi.encode(owner));
        return _extractAddress(keccak256(abi.encodePacked(bytes1(0xff), address(this), salt, initcodeHash)));
      }
    
      /// @notice Converts a padded bytes32 value to a payable address.
      /// @param zeroPaddedAddress The bytes32 value representing an address with padding.
      /// @return res The corresponding payable address.
      function _extractAddress(bytes32 zeroPaddedAddress) private pure returns (address payable res) {
        assembly {
          res := zeroPaddedAddress
        }
      }
    
      /// @notice Deploys a new RouterProxy for a given owner.
      /// @param owner The address to be set as initial admin and immutable owner of the proxy.
      /// @param routerImplementation router contract which implements routing functions
      /// @return proxy The address of the newly deployed RouterProxy.
      /// @dev Emits a ProxyDeployed event upon successful deployment.
      ///      Note that the deployment can be initiated by any caller, on behalf of `owner`.
      function deployProxy(address owner, AbstractRouter routerImplementation) public returns (RouterProxy proxy) {
        proxy = new RouterProxy{salt: keccak256(abi.encode(owner))}(routerImplementation);
        // TODO: The access controlled admin must maybe be immutable (or this is a vector attack)
        // We will always link one user with a router address anyway
        _afterDeployProxy(proxy, owner);
        emit ProxyDeployed(proxy, owner, routerImplementation);
      }
    
      /// @notice Hook to be called after a proxy is deployed.
      /// @param proxy the deployed proxy contract
      /// @param owner The address which will be the admin and owner of the newly deployed proxy.
      function _afterDeployProxy(RouterProxy proxy, address owner) internal virtual {
        AbstractRouter(address(proxy)).setAdmin(owner);
      }
    
      /// @notice Deploys a RouterProxy for a given owner if one has not already been deployed.
      /// @param owner The address to be set as initial admin and immutable owner of the proxy.
      /// @param routerImplementation router contract which implements routing functions
      /// @return proxy The address of the RouterProxy.
      /// @return created A boolean indicating if the proxy was created during this call.
      /// @dev If the proxy already exists at the computed address, the function will not redeploy it.
      ///      The `created` return value indicates whether the proxy was created as a result of this call.
      function instantiate(address owner, AbstractRouter routerImplementation)
        public
        returns (RouterProxy proxy, bool created)
      {
        proxy = RouterProxy(computeProxyAddress(owner, routerImplementation));
        if (address(proxy).code.length == 0) {
          require(deployProxy(owner, routerImplementation) == proxy, "Deployed via create2 failed");
          created = true;
        }
      }
    }
    
  • node_modules/@mangrovedao/mangrove-core/lib/core/Constants.sol
    // SPDX-License-Identifier: MIT
    pragma solidity ^0.8.17;
    
    /* The constants below are written as literals to optimize gas. For all the relevant constants here, the non-literal expression that computes them is checked in `Constants.t.sol`. */
    
    uint constant ONE = 1;
    uint constant ONES = type(uint).max;
    uint constant TOPBIT = 0x8000000000000000000000000000000000000000000000000000000000000000;
    uint constant NOT_TOPBIT = 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff;
    
    /* **sizes must match field sizes in `structs.ts` where relevant** */
    
    /* Number of bits used to represent a tick */
    uint constant TICK_BITS = 21;
    /* Number of bits used to represent an offer id */
    uint constant OFFER_BITS = 32;
    /* Maximum possible size of a field -- protects against wrong code changes. Constraint given by `BitLib.ctz64`. */
    uint constant MAX_FIELD_SIZE = 64;
    
    /* `X_SIZE_BITS` is 1+log2 of the size of `X`, where size is the number of elements it holds.In a field, an element is a bit; in a leaf, an element is a pair of offer ids. For `LEVEL`s and `ROOT`, the value must be exact, so only power-of-2 sizes are allowed. */
    uint constant LEAF_SIZE_BITS = 2; 
    uint constant LEVEL_SIZE_BITS = 6;
    uint constant ROOT_SIZE_BITS = 1;
    
    /* `X_SIZE` is `2**X_SIZE_BITS` */
    int constant LEAF_SIZE = 4;
    int constant LEVEL_SIZE = 64;
    int constant ROOT_SIZE = 2;
    
    /* `X_SIZE_MASK` is `0...01...1` where the number of 1s is `X_SIZE_BITS` */
    uint constant LEAF_SIZE_MASK = 0x3;
    uint constant LEVEL_SIZE_MASK = 0x3f;
    uint constant ROOT_SIZE_MASK = 0x1;
    /* `0...01...1` with `OFFER_BITS` 1s at the end */
    uint constant OFFER_MASK = 0xffffffff;
    
    /* Same as `ROOT_SIZE` */
    int constant NUM_LEVEL1 = 2;
    /* Same as `NUM_LEVEL1 * LEVEL_SIZE` */
    int constant NUM_LEVEL2 = 128;
    /* Same as `NUM_LEVEL2 * LEVEL_SIZE` */
    int constant NUM_LEVEL3 = 8192;
    /* Same as `NUM_LEVEL3 * LEVEL_SIZE` */
    int constant NUM_LEAFS = 524288;
    /* Same as `NUM_LEAFS * LEAF` */
    int constant NUM_BINS = 2097152;
    /* min and max bins are defined like min and max int. */
    int constant MIN_BIN = -1048576;
    int constant MAX_BIN = 1048575;
    
    /* The tick range is the largest such that the mantissa of `1.0001^MAX_TICK` fits on 128 bits (and thus can be multiplied by volumes). */
    int constant MIN_TICK = -887272;
    int constant MAX_TICK = 887272;
    /* These are reference values for what the function `tickFromRatio` function will return, not the most possible accurate values for the min and max tick. */
    uint constant MIN_RATIO_MANTISSA = 170153974464283981435225617938057077692;
    int constant MIN_RATIO_EXP = 255;
    uint constant MAX_RATIO_MANTISSA = 340256786836388094050805785052946541084;
    int constant MAX_RATIO_EXP = 0;
    /* `MANTISSA_BITS` is the number of bits used in the mantissa of normalized floats that represent ratios. 128 means we can multiply all allowed volumes by the mantissa and not overflow. */
    uint constant MANTISSA_BITS = 128;
    uint constant MANTISSA_BITS_MINUS_ONE = 127;
    /* 
    With `|tick|<=887272` and normalized mantissas on 128 bits, the maximum possible mantissa is `340282295208261841796968287475569060645`, so the maximum safe volume before overflow is `NO_OVERFLOW_AMOUNT = 340282438633630198193436196978374475856` (slightly above `2**128`).
    
    For ease of use, we could pick a simpler, slightly smaller max safe volume: `(1<<128)-1`.
    
    However the `*ByVolume` functions get a price by (abstractly) performing `outboundAmount/inboundAmount`. If we limit all volumes to `NO_OVERFLOW_AMOUNT` but aren't more restrictive than that, since `type(uint128).max > 1.0001**MAX_TICK`, we will get ratios that are outside the price boundaries.
    
    We thus pick a uniform, easy to remember constraint on volumes that works everywhere: `(1<<127)-1`
    */
    uint constant MAX_SAFE_VOLUME = 170141183460469231731687303715884105727;
    
    /* When a market order consumes offers, the implementation uses recursion consumes additional EVM stack space at each new offer. To avoid reverting due to stack overflow, Mangrove keeps a counter and stops the market order when it reaches a maximum recursion depth. `INITIAL_MAX_RECURSION_DEPTH` is the maximum recursion depth given at deployment time.
    
    See `maxRecursionDepth` in `structs.ts`
    
    Without optimizer enabled it fails above 79. With optimizer and 200 runs it fails above 80. Set default a bit lower to be safe. */
    uint constant INITIAL_MAX_RECURSION_DEPTH = 75;
    
    /* When a market order consumes offers, it may use gas on offers that fail to deliver. To avoid reverts after a string of failing offers that consumes more gas than is available in a block, Mangrove stops a market order after it has gone through failing offers such that their cumulative `gasreq` is greater than the global `maxGasreqForFailingOffers` parameter. At deployment, `maxGasreqForFailingOffers` is set to:
    ```
    INITIAL_MAX_GASREQ_FOR_FAILING_OFFERS_MULTIPLIER * gasmax
    ``` */
    uint constant INITIAL_MAX_GASREQ_FOR_FAILING_OFFERS_MULTIPLIER = 3;
    
    /* Those two constants are used in `TickLib.ratioFromTick` to convert a log base 1.0001 to a log base 2. */
    uint constant LOG_BP_SHIFT = 235;
    uint constant LOG_BP_2X235 = 382733217082594961806491056566382061424140926068392360945012727618364717537;
  • src/strategies/utils/AccessControlled.sol
    // SPDX-License-Identifier: MIT
    pragma solidity ^0.8.10;
    
    /// @title This contract is used to restrict access to privileged functions of inheriting contracts through modifiers.
    /// @notice The contract stores an admin address which is checked against `msg.sender` in the `onlyAdmin` modifier.
    /// @notice Additionally, a specific `msg.sender` can be verified with the `onlyCaller` modifier.
    contract AccessControlled {
      /**
       * @notice logs new `admin` of `this`
       * @param admin The new admin.
       * @notice By emitting this data, an indexer will be able to keep track of what address is the admin of this contract.
       */
      event SetAdmin(address admin);
      /**
       * @notice The admin address.
       */
    
      address internal _admin;
    
      /**
       * @notice `AccessControlled`'s constructor
       * @param admin_ The address of the admin that can access privileged functions and also allowed to change the admin. Cannot be `address(0)`.
       */
      constructor(address admin_) {
        _setAdmin(admin_);
      }
    
      /**
       * @notice This modifier verifies that `msg.sender` is the admin.
       */
      modifier onlyAdmin() {
        require(msg.sender == _admin, "AccessControlled/Invalid");
        _;
      }
    
      /**
       * @notice This modifier verifies that `msg.sender` is the caller.
       * @param caller The address of the caller that can access the modified function.
       */
      modifier onlyCaller(address caller) {
        require(msg.sender == caller, "AccessControlled/Invalid");
        _;
      }
    
      /**
       * @notice This modifier verifies that `msg.sender` is either caller or the admin
       * @param caller The address of a caller that can access the modified function.
       */
      modifier adminOrCaller(address caller) {
        // test _admin second to save a storage read when possible
        require(msg.sender == caller || msg.sender == _admin, "AccessControlled/Invalid");
        _;
      }
    
      /**
       * @notice Retrieves the current admin.
       * @return current admin.
       */
      function admin() public view returns (address current) {
        return _admin;
      }
    
      /**
       * @notice This function is called to add logic on admin change for inheriting contracts.
       * @param admin_ The new admin. Cannot be `address(0)`.
       */
      function _onAdminChange(address admin_) internal virtual {}
    
      /**
       * @notice This sets and log a new admin
       * @param admin_ The new admin. Cannot be `address(0)`.
       */
      function _setAdmin(address admin_) internal {
        require(admin_ != address(0), "AccessControlled/0xAdmin");
        _admin = admin_;
        emit SetAdmin(admin_);
        _onAdminChange(admin_);
      }
    
      /**
       * @notice This sets the admin. Only the current admin can change the admin.
       * @param admin_ The new admin. Cannot be `address(0)`.
       */
      function setAdmin(address admin_) external onlyAdmin {
        _setAdmin(admin_);
      }
    }
    
  • node_modules/@mangrovedao/mangrove-core/src/IMangrove.sol
    // SPDX-License-Identifier: MIT
    // This file must be kept up-to-date with the actual Mangrove interface.
    
    pragma solidity >=0.7.0 <0.9.0;
    
    import "@mgv/src/core/MgvLib.sol";
    
    ///@title Interface for the Mangrove contract.
    interface IMangrove is HasMgvEvents {
      // # Permit functions
    
      ///@notice See {IERC20Permit-DOMAIN_SEPARATOR}.
      ///@return the domain separator.
      function DOMAIN_SEPARATOR() external view returns (bytes32);
    
      ///@notice See {IERC20Permit-PERMIT_TYPEHASH}.
      ///@return The permit type hash.
      function PERMIT_TYPEHASH() external pure returns (bytes32);
    
      ///@notice Approves the spender to spend the amount of tokens on behalf of the caller.
      ///@param outbound_tkn The address of the (maker) outbound token.
      ///@param inbound_tkn The address of the (maker) inbound token.
      ///@param spender The address of the spender.
      ///@param value The amount of tokens to approve.
      ///@return true If the approval succeeded; always true.
      function approve(address outbound_tkn, address inbound_tkn, address spender, uint value) external returns (bool);
    
      ///@notice Returns the allowance of the spender to spend tokens on behalf of the owner.
      ///@param outbound_tkn The address of the (maker) outbound token.
      ///@param inbound_tkn The address of the (maker) inbound token.
      ///@param owner The address of the owner.
      ///@param spender The address of the spender.
      ///@return amount The amount of tokens the spender is allowed to spend on behalf of the owner.
      function allowance(address outbound_tkn, address inbound_tkn, address owner, address spender)
        external
        view
        returns (uint amount);
    
      ///@notice Adapted from [Uniswap v2 contract](https://github.com/Uniswap/uniswap-v2-core/blob/55ae25109b7918565867e5c39f1e84b7edd19b2a/contracts/UniswapV2ERC20.sol#L81)
      ///@param outbound_tkn The address of the (maker) outbound token.
      ///@param inbound_tkn The address of the (maker) inbound token.
      ///@param owner The address of the owner.
      ///@param spender The address of the spender.
      ///@param value The amount of tokens to approve.
      ///@param deadline The deadline after which the permit is no longer valid.
      ///@param v The signature v parameter.
      ///@param r The signature r parameter.
      ///@param s The signature s parameter.
      function permit(
        address outbound_tkn,
        address inbound_tkn,
        address owner,
        address spender,
        uint value,
        uint deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
      ) external;
    
      ///@notice See {IERC20Permit-nonces}.
      ///@param owner The address of the owner.
      ///@return nonce The current nonce of the owner.
      function nonces(address owner) external view returns (uint nonce);
    
      // # Taker functions
    
      ///@notice Performs a market order on a specified offer list taking offers up to a limit price.
      ///@param olKey The offer list key given by (maker) `outbound_tkn`, (maker) `inbound_tkn`, and `tickSpacing`.
      ///@param maxTick Must be `>= MIN_TICK` and `<= MAX_TICK`. The log of the limit price the taker is ready to pay (meaning: the log base 1.0001 of the ratio of inbound tokens over outbound tokens).
      ///@param fillVolume Must be `<= MAX_SAFE_VOLUME`. If `fillWants` is true, the amount of `olKey.outbound_tkn` the taker wants to buy; otherwise, the amount of `olKey.inbound_tkn` the taker wants to sell.
      ///@param fillWants If true, the matching engine tries to get the taker all they want; otherwise, the matching engine tries to sell all that the taker gives. In both cases subject to the price limit.
      ///@return takerGot The amount of `olKey.outbound_tkn` the taker got.
      ///@return takerGave The amount of `olKey.inbound_tkn` the taker gave.
      ///@return bounty The amount of native token the taker got as a bounty due to failing offers (in wei).
      ///@return feePaid The amount of `olKey.outbound_tkn` the taker paid as a fee to Mangrove.
      ///@dev The market order stops when there are no more offers at or below `maxTick`, when the end of the book has been reached, or:
      ///@dev - If `fillWants` is true, the market order stops when `fillVolume` units of `olKey.outbound_tkn` have been obtained. To buy a specific volume of `olKey.outbound_tkn` at any price, set `fillWants` to true, set `fillVolume` to the volume you want to buy, and set `maxTick` to the `MAX_TICK` constant.
      ///@dev - If `fillWants` is false, the market order stops when `fillVolume` units of `olKey.inbound_tkn` have been paid. To sell a specific volume of `olKey.inbound_tkn` at any price, set `fillWants` to false, set `fillVolume` to the volume you want to sell, and set `maxTick` to the `MAX_TICK` constant.
      function marketOrderByTick(OLKey memory olKey, Tick maxTick, uint fillVolume, bool fillWants)
        external
        returns (uint takerGot, uint takerGave, uint bounty, uint feePaid);
    
      ///@notice Performs a market order on a specified offer list taking offers up to a limit price, while allowing to specify a custom `maxGasreqForFailingOffers`.
      ///@param olKey The offer list key given by (maker) `outbound_tkn`, (maker) `inbound_tkn`, and `tickSpacing`.
      ///@param maxTick Must be `>= MIN_TICK` and `<= MAX_TICK`. The log of the limit price the taker is ready to pay (meaning: the log base 1.0001 of the ratio of inbound tokens over outbound tokens).
      ///@param fillVolume Must be `<= MAX_SAFE_VOLUME`. If `fillWants` is true, the amount of `olKey.outbound_tkn` the taker wants to buy; otherwise, the amount of `olKey.inbound_tkn` the taker wants to sell.
      ///@param fillWants If true, the matching engine tries to get the taker all they want; otherwise, the matching engine tries to sell all that the taker gives. In both cases subject to the price limit.
      ///@param maxGasreqForFailingOffers The maximum allowed gas required for failing offers (in wei).
      ///@return takerGot The amount of `olKey.outbound_tkn` the taker got.
      ///@return takerGave The amount of `olKey.inbound_tkn` the taker gave.
      ///@return bounty The amount of native token the taker got as a bounty due to failing offers (in wei).
      ///@return feePaid The amount of `olKey.outbound_tkn` the taker paid as a fee to Mangrove.
      ///@dev Mangrove stops a market order after it has gone through failing offers such that their cumulative `gasreq` is greater than the global `maxGasreqForFailingOffers` parameter. This function can be used by the taker to override the default `maxGasreqForFailingOffers` parameter.
      function marketOrderByTickCustom(
        OLKey memory olKey,
        Tick maxTick,
        uint fillVolume,
        bool fillWants,
        uint maxGasreqForFailingOffers
      ) external returns (uint takerGot, uint takerGave, uint bounty, uint feePaid);
    
      ///@notice Performs a market order on a specified offer list taking offers up to a limit price defined by a ratio `inbound_tkn/outbound_tkn` of volumes.
      ///@param olKey The offer list key given by (maker) `outbound_tkn`, (maker) `inbound_tkn`, and `tickSpacing`.
      ///@param takerWants Must be `<= MAX_SAFE_VOLUME`. The amount the taker wants. This is used along with `takerGives` to derive a max price (`maxTick`) which is the lowest allowed tick in the offer list such that `log_1.0001(takerGives/takerWants) <= maxTick`.
      ///@param takerGives Must be `<= MAX_SAFE_VOLUME`. The amount the taker gives. This is used along with `takerWants` to derive a max price (`maxTick`) which is the lowest allowed tick in the offer list such that `log_1.0001(takerGives/takerWants) <= maxTick`.
      ///@param fillWants If true, the matching engine tries to get the taker all they want; otherwise, the matching engine tries to sell all that the taker gives. In both cases subject to the price limit.
      ///@return takerGot The amount of `olKey.outbound_tkn` the taker got.
      ///@return takerGave The amount of `olKey.inbound_tkn` the taker gave.
      ///@return bounty The amount of native token the taker got as a bounty due to failing offers (in wei).
      ///@return feePaid The amount of `olKey.outbound_tkn` the taker paid as a fee to Mangrove.
      ///@dev This function is just a wrapper for `marketOrderByTick`, see that function for details.
      ///@dev When deriving the tick, then `takerWants = 0` has a special meaning and the tick for the highest possible ratio between wants and gives will be used,
      ///@dev and if `takerGives = 0` and `takerWants != 0`, then the tick for the lowest possible ratio will be used.
      function marketOrderByVolume(OLKey memory olKey, uint takerWants, uint takerGives, bool fillWants)
        external
        returns (uint takerGot, uint takerGave, uint bounty, uint feePaid);
    
      ///@notice Performs a market order on a specified offer list taking offers up to a limit price for a specified taker.
      ///@param olKey The offer list key given by (maker) `outbound_tkn`, (maker) `inbound_tkn`, and `tickSpacing`.
      ///@param maxTick Must be `>= MIN_TICK` and `<= MAX_TICK`. The log of the limit price the taker is ready to pay (meaning: the log base 1.0001 of the ratio of inbound tokens over outbound tokens).
      ///@param fillVolume Must be `<= MAX_SAFE_VOLUME`. If `fillWants` is true, the amount of `olKey.outbound_tkn` the taker wants to buy; otherwise, the amount of `olKey.inbound_tkn` the taker wants to sell.
      ///@param fillWants If true, the matching engine tries to get the taker all they want; otherwise, the matching engine tries to sell all that the taker gives. In both cases subject to the price limit.
      ///@param taker The taker from which amounts will be transferred from and to. If the `msg.sender`'s allowance for the given `olKey.outbound_tkn`,`olKey.inbound_tkn` is strictly less than the total amount eventually spent by `taker`, the call will fail.
      ///@return takerGot The amount of `olKey.outbound_tkn` the taker got.
      ///@return takerGave The amount of `olKey.inbound_tkn` the taker gave.
      ///@return bounty The amount of native token the taker got as a bounty due to failing offers (in wei).
      ///@return feePaid The amount of `olKey.outbound_tkn` the taker paid as a fee to Mangrove.
      ///@dev The `bounty` will be sent to `msg.sender` but transfers will be for `taker`. Requires prior permission.
      ///@dev See also `marketOrderByTick`.
      function marketOrderForByTick(OLKey memory olKey, Tick maxTick, uint fillVolume, bool fillWants, address taker)
        external
        returns (uint takerGot, uint takerGave, uint bounty, uint feePaid);
    
      ///@notice Performs a market order on a specified offer list taking offers up to a limit price defined by a ratio `inbound_tkn/outbound_tkn` of volumes for a specified taker.
      ///@param olKey The offer list key given by (maker) `outbound_tkn`, (maker) `inbound_tkn`, and `tickSpacing`.
      ///@param takerWants Must be `<= MAX_SAFE_VOLUME`. The amount the taker wants. This is used along with `takerGives` to derive a max price (`maxTick`) which is the lowest allowed tick in the offer list such that `log_1.0001(takerGives/takerWants) <= maxTick`.
      ///@param takerGives Must be `<= MAX_SAFE_VOLUME`. The amount the taker gives. This is used along with `takerGives` to derive a max price (`maxTick`) which is the lowest allowed tick in the offer list such that `log_1.0001(takerGives/takerWants) <= maxTick`.
      ///@param fillWants If true, the matching engine tries to get the taker all they want; otherwise, the matching engine tries to sell all that the taker gives. In both cases subject to the price limit.
      ///@param taker The taker from which amounts will be transferred from and to the. If the `msg.sender`'s allowance for the given `olKey.outbound_tkn`,`olKey.inbound_tkn` are strictly less than the total amount eventually spent by `taker`, the call will fail.
      ///@return takerGot The amount of `olKey.outbound_tkn` the taker got.
      ///@return takerGave The amount of `olKey.inbound_tkn` the taker gave.
      ///@return bounty The amount of native token the taker got as a bounty due to failing offers (in wei).
      ///@return feePaid The amount of native token the taker paid as a fee (in wei of `olKey.outbound_tkn`).
      ///@dev The `bounty` will be send to `msg.sender` but transfers will be for `taker`. Requires prior permission.
      ///@dev See also `marketOrderByVolume`.
      function marketOrderForByVolume(OLKey memory olKey, uint takerWants, uint takerGives, bool fillWants, address taker)
        external
        returns (uint takerGot, uint takerGave, uint bounty, uint feePaid);
    
      // # Cleaning functions
    
      /* # Cleaning */
      ///@notice Cleans multiple offers, i.e. executes them and removes them from the book if they fail, transferring the failure penalty as bounty to the caller.
      ///@param olKey The offer list key given by (maker) `outbound_tkn`, (maker) `inbound_tkn`, and `tickSpacing`.
      ///@param targets The offers to clean, identified by their (`offerId, tick, gasreq, takerWants`) that will make them fail.
      ///@param taker The taker used for transfers (should be able to deliver token amounts).
      ///@return successes The number of successfully cleaned offers.
      ///@return bounty The total bounty received by the caller.
      ///@dev If an offer succeeds, the execution of that offer is reverted, it stays in the book, and no bounty is paid; The `cleanByImpersonation` function itself will not revert.
      ///@dev Note that Mangrove won't attempt to execute an offer if the values in a target don't match its offer. To distinguish between a non-executed clean and a failed clean (due to the offer itself not failing), you must inspect the log (see `MgvLib.sol`) or check the received bounty.
      ///@dev Any `taker` can be impersonated when cleaning because:
      ///@dev - The function reverts if the offer succeeds, reverting any token transfers.
      ///@dev - After a `clean` where the offer has failed, all ERC20 token transfers have also been reverted -- but the sender will still have received the bounty of the failing offers. */
      function cleanByImpersonation(OLKey memory olKey, MgvLib.CleanTarget[] calldata targets, address taker)
        external
        returns (uint successes, uint bounty);
    
      // # Maker functions
    
      ///@notice Adds funds to Mangrove for the caller (the maker) to use for provisioning offers.
      receive() external payable;
    
      ///@notice Adds funds to Mangrove for the caller (the maker) to use for provisioning offers.
      function fund() external payable;
    
      ///@notice Adds funds to Mangrove for the maker to use for provisioning offers.
      ///@param maker The maker to add funds for.
      function fund(address maker) external payable;
    
      ///@notice Withdraws the caller's (the maker's) free native tokens (funds for provisioning offers not locked by an offer) by transferring them to the caller.
      ///@param amount The amount to withdraw.
      ///@return noRevert Whether the transfer succeeded.
      function withdraw(uint amount) external returns (bool noRevert);
    
      ///@notice Gets the maker's free balance of native tokens (funds for provisioning offers not locked by an offer).
      ///@param maker The maker to get the balance for.
      ///@return balance The maker's free balance of native tokens (funds for provisioning offers not locked by an offer).
      function balanceOf(address maker) external view returns (uint balance);
    
      ///@notice Creates a new offer on Mangrove, where the caller is the maker. The maker can implement the `IMaker` interface to be called during offer execution.
      ///@param olKey The offer list key given by (maker) `outbound_tkn`, (maker) `inbound_tkn`, and `tickSpacing`.
      ///@param tick Must be `>= MIN_TICK` and `<= MAX_TICK`. The `tick` induces a price which is `1.0001^tick`. The actual tick of the offer will be the smallest tick `offerTick > tick` that satisfies `offerTick % tickSpacing == 0`.
      ///@param gives Must be `<= MAX_SAFE_VOLUME`. The amount of `olKey.outbound_tkn` the maker gives.
      ///@param gasreq The amount of gas required to execute the offer logic in the maker's `IMaker` implementation. This will limit the gas available, and the offer will fail if it spends more.
      ///@param gasprice The maximum gas price the maker is willing to pay a penalty for due to failing execution.
      ///@return offerId The id of the offer on Mangrove. Can be used to retract or update the offer (even to reuse a taken offer).
      ///@dev The gasreq and gasprice are used to derive the provision which will be used to pay a penalty if the offer fails.
      ///@dev This function is payable to enable delivery of the provision along with the offer creation.
      function newOfferByTick(OLKey memory olKey, Tick tick, uint gives, uint gasreq, uint gasprice)
        external
        payable
        returns (uint offerId);
    
      ///@notice Creates a new offer on Mangrove, where the caller is the maker. The maker can implement the `IMaker` interface to be called during offer execution.
      ///@param olKey The offer list key given by (maker) `outbound_tkn`, (maker) `inbound_tkn`, and `tickSpacing`.
      ///@param wants Must be less than MAX_SAFE_VOLUME. The amount of `olKey.inbound_tkn` the maker wants. This is used along with `gives` to derive a tick (price). which is the lowest allowed tick in the offer list such that `log_1.0001(takerGives/takerWants) <= maxTick`.
      ///@param gives Must be less than MAX_SAFE_VOLUME. The amount of `olKey.outbound_tkn` the maker gives. This is used along with `wants` to derive a tick (price). which is the lowest allowed tick in the offer list such that `log_1.0001(takerGives/takerWants) <= maxTick`. Must be less than MAX_SAFE_VOLUME.
      ///@param gasreq The amount of gas required to execute the offer logic in the maker's `IMaker` implementation. This will limit the gas available, and the offer will fail if it spends more.
      ///@param gasprice The maximum gas price the maker is willing to pay a penalty for due to failing execution.
      ///@return offerId The id of the offer on Mangrove. Can be used to retract or update the offer (even to reuse a taken offer).
      ///@dev This function is just a wrapper for `newOfferByTick`, see that function for details.
      function newOfferByVolume(OLKey memory olKey, uint wants, uint gives, uint gasreq, uint gasprice)
        external
        payable
        returns (uint offerId);
    
      ///@notice Updates an existing offer on Mangrove, where the caller is the maker.
      ///@param olKey The offer list key given by (maker) `outbound_tkn`, (maker) `inbound_tkn`, and `tickSpacing`.
      ///@param tick Must be `>= MIN_TICK` and `<= MAX_TICK`. The `tick` induces a price which is `1.0001^tick`. The actual tick of the offer will be the smallest tick `offerTick > tick` that satisfies `offerTick % tickSpacing == 0`.
      ///@param gives The amount of `olKey.outbound_tkn` the maker gives. Must be less than MAX_SAFE_VOLUME.
      ///@param gasreq The amount of gas required to execute the offer logic in the maker's `IMaker` implementation.
      ///@param gasprice The maximum gas price the maker is willing to pay a penalty for due to failing execution.
      ///@param offerId The id of the offer on Mangrove.
      ///@dev See `newOfferByTick` for additional details.
      function updateOfferByTick(OLKey memory olKey, Tick tick, uint gives, uint gasreq, uint gasprice, uint offerId)
        external
        payable;
    
      ///@notice Updates an existing, owned offer on Mangrove, where the caller is the maker.
      ///@param olKey The offer list key given by (maker) `outbound_tkn`, (maker) `inbound_tkn`, and `tickSpacing`.
      ///@param wants The amount of `olKey.inbound_tkn` the maker wants. This is used along with `gives` to derive a tick (price). which is the lowest allowed tick in the offer list such that `log_1.0001(takerGives/takerWants) <= maxTick`.
      ///@param gives The amount of `olKey.outbound_tkn` the maker gives. This is used along with `wants` to derive a tick (price). which is the lowest allowed tick in the offer list such that `log_1.0001(takerGives/takerWants) <= maxTick`. Must be less than MAX_SAFE_VOLUME.
      ///@param gasreq The amount of gas required to execute the offer logic in the maker's `IMaker` implementation.
      ///@param gasprice The maximum gas price the maker is willing to pay a penalty for due to failing execution.
      ///@param offerId The id of the offer on Mangrove.
      ///@dev This function is just a wrapper for `updateOfferByTick`, see that function for details.
      function updateOfferByVolume(OLKey memory olKey, uint wants, uint gives, uint gasreq, uint gasprice, uint offerId)
        external
        payable;
    
      ///@notice Retracts an offer from Mangrove, where the caller is the maker.
      ///@param olKey The offer list key given by (maker) `outbound_tkn`, (maker) `inbound_tkn`, and `tickSpacing`.
      ///@param offerId The id of the offer on Mangrove.
      ///@param deprovision Whether to deprovision the offer, i.e, return the provision to the maker's balance on Mangrove.
      ///@return provision The amount of native token deprovisioned for the offer (in wei).
      ///@dev `withdraw` can be used to withdraw the funds after deprovisioning.
      ///@dev Leaving funds provisioned can be used to save gas if offer is later updated.
      function retractOffer(OLKey memory olKey, uint offerId, bool deprovision) external returns (uint provision);
    
      // # Global config view functions
    
      ///@notice Gets the global configuration for Mangrove.
      ///@return _global The global configuration for Mangrove.
      function global() external view returns (Global _global);
    
      // # Offer list view functions
    
      ///@notice Gets the local configuration for a specific offer list.
      ///@param olKey The offer list key given by (maker) `outbound_tkn`, (maker) `inbound_tkn`, and `tickSpacing`.
      ///@return _local The local configuration for the offer list.
      function local(OLKey memory olKey) external view returns (Local _local);
    
      ///@notice Gets the global configuration for Mangrove and local the configuration for a specific offer list.
      ///@param olKey The offer list key given by (maker) `outbound_tkn`, (maker) `inbound_tkn`, and `tickSpacing`.
      ///@return _global The global configuration for Mangrove.
      ///@return _local The local configuration for the offer list.
      function config(OLKey memory olKey) external view returns (Global _global, Local _local);
    
      ///@notice Determines whether the reentrancy lock is in effect for the offer list.
      ///@param olKey The offer list key given by (maker) `outbound_tkn`, (maker) `inbound_tkn`, and `tickSpacing`.
      ///@return true If locked; otherwise, false.
      ///@dev The lock protects modifying or inspecting the offer list while an order is in progress.
      function locked(OLKey memory olKey) external view returns (bool);
    
      ///@notice Gets the `offerId` of the best offer in the offer list.
      ///@param olKey The offer list key given by (maker) `outbound_tkn`, (maker) `inbound_tkn`, and `tickSpacing`.
      ///@return offerId The `offerId` of the best offer on the offer list.
      function best(OLKey memory olKey) external view returns (uint offerId);
    
      ///@notice Gets the offer list key with the given hash (if the offer list key has been activated at least once).
      ///@param olKeyHash The hash of the offer list key.
      ///@return olKey The olKey.
      function olKeys(bytes32 olKeyHash) external view returns (OLKey memory olKey);
    
      // # Offer view functions
    
      ///@notice Gets an offer in packed format.
      ///@param olKey The offer list key given by (maker) `outbound_tkn`, (maker) `inbound_tkn`, and `tickSpacing`.
      ///@param offerId The `offerId` of the offer on the offer list.
      ///@return offer The offer in packed format.
      function offers(OLKey memory olKey, uint offerId) external view returns (Offer offer);
    
      ///@notice Gets an offer's details in packed format.
      ///@param olKey The offer list key given by (maker) `outbound_tkn`, (maker) `inbound_tkn`, and `tickSpacing`.
      ///@param offerId The `offerId` of the offer on the offer list.
      ///@return offerDetail The offer details in packed format.
      function offerDetails(OLKey memory olKey, uint offerId) external view returns (OfferDetail offerDetail);
    
      ///@notice Gets both an offer and its details in packed format.
      ///@param olKey The offer list key given by (maker) `outbound_tkn`, (maker) `inbound_tkn`, and `tickSpacing`.
      ///@param offerId The `offerId` of the offer on the offer list.
      ///@return offer The offer in packed format.
      ///@return offerDetail The offer details in packed format.
      function offerData(OLKey memory olKey, uint offerId) external view returns (Offer offer, OfferDetail offerDetail);
    
      // # Governance functions
    
      ///@notice Gets the governance address.
      ///@return The governance address.
      function governance() external view returns (address);
    
      ///@notice Activates an offer list.
      ///@param olKey The offer list key given by (maker) `outbound_tkn`, (maker) `inbound_tkn`, and `tickSpacing`.
      ///@param fee In basis points, of `olKey.outbound_tkn` given to the taker. This fee is sent to Mangrove. Fee is capped to ~2.5%.
      ///@param density96X32 The density of the offer list used to define a minimum offer volume. See `setDensity96X32`.
      ///@param offer_gasbase The gasbase of the offer list used to define a minimum provision necessary for offers. See `setGasbase`.
      ///@dev If the flipped offer list is active then the offer lists are expected to have the same `tickSpacing`.
      function activate(OLKey memory olKey, uint fee, uint density96X32, uint offer_gasbase) external;
    
      ///@notice Deactivates an offer list.
      ///@param olKey The offer list key given by (maker) `outbound_tkn`, (maker) `inbound_tkn`, and `tickSpacing`.
      function deactivate(OLKey memory olKey) external;
    
      ///@notice Kills the Mangrove instance. A dead instance cannot have offers executed or funds received, but offers can be retracted and funds can be withdrawn.
      function kill() external;
    
      ///@notice Sets the density.
      ///@param olKey The offer list key given by (maker) `outbound_tkn`, (maker) `inbound_tkn`, and `tickSpacing`.
      ///@param density96X32 Is given as a 96.32 fixed point number. It will be stored as a 9-bit float and be approximated towards 0. The maximum error is 20%. See `DensityLib` for more information.
      ///@dev Useless if `global.useOracle != 0` and oracle returns a valid density.
      function setDensity96X32(OLKey memory olKey, uint density96X32) external;
    
      ///@notice Sets the fee for the offer list.
      ///@param olKey The offer list key given by (maker) `outbound_tkn`, (maker) `inbound_tkn`, and `tickSpacing`.
      ///@param fee In basis points, of `olKey.outbound_tkn` given to the taker. This fee is sent to Mangrove. Fee is capped to ~2.5%.
      function setFee(OLKey memory olKey, uint fee) external;
    
      ///@notice Sets the gasbase for the offer list.
      ///@param olKey The offer list key given by (maker) `outbound_tkn`, (maker) `inbound_tkn`, and `tickSpacing`.
      ///@param offer_gasbase The gasbase of the offer list used to define a minimum provision necessary for offers. Represents the gas overhead used by processing the offer inside Mangrove + the overhead of initiating an entire order. Stored in thousands in a maximum of 9 bits.
      function setGasbase(OLKey memory olKey, uint offer_gasbase) external;
    
      ///@notice Sets the gasmax for Mangrove, the maximum amount of gas an offer can require to execute.
      ///@param gasmax The maximum amount of gas required to execute an offer. Must fit in 24 bits.
      function setGasmax(uint gasmax) external;
    
      ///@notice Sets the maximum number of times a market order can recursively execute offers. This is a protection against stack overflows.
      ///@param maxRecursionDepth The maximum number of times a market order can recursively execute offers.
      function setMaxRecursionDepth(uint maxRecursionDepth) external;
    
      ///@notice Sets the maximum cumulative `gasreq` for failing offers during a market order before doing a partial fill.
      ///@param maxGasreqForFailingOffers The maximum cumulative `gasreq` for failing offers during a market order before doing a partial fill. 32 bits.
      function setMaxGasreqForFailingOffers(uint maxGasreqForFailingOffers) external;
    
      ///@notice Sets the gasprice (in Mwei, 26 bits).
      ///@param gasprice The gasprice (in Mwei, 26 bits).
      function setGasprice(uint gasprice) external;
    
      ///@notice Sets a new governance address.
      ///@param governanceAddress The new governance address.
      function setGovernance(address governanceAddress) external;
    
      ///@notice Sets the monitor/oracle. The `monitor/oracle` can provide real-time values for `gasprice` and `density` to Mangrove. It can also receive liquidity event notifications.
      ///@param monitor The new monitor/oracle address.
      function setMonitor(address monitor) external;
    
      ///@notice Sets whether Mangrove notifies the Monitor when and offer is taken.
      ///@param notify Whether Mangrove notifies the Monitor when and offer is taken.
      function setNotify(bool notify) external;
    
      ///@notice Sets whether Mangrove uses the monitor as oracle for `gasprice` and `density` values.
      ///@param useOracle Whether Mangrove uses the monitor as oracle for `gasprice` and `density` values.
      function setUseOracle(bool useOracle) external;
    
      ///@notice Transfer ERC20 tokens to governance.
      ///@param tokenAddress The address of the ERC20 token.
      ///@param value The amount of tokens to transfer.
      function withdrawERC20(address tokenAddress, uint value) external;
    
      // # Tick tree view functions
    
      ///@notice Gets a leaf.
      ///@param olKey The offer list key given by (maker) `outbound_tkn`, (maker) `inbound_tkn`, and `tickSpacing`.
      ///@param index The index.
      ///@return The leaf.
      function leafs(OLKey memory olKey, int index) external view returns (Leaf);
    
      ///@notice Gets a level 3 field.
      ///@param olKey The offer list key given by (maker) `outbound_tkn`, (maker) `inbound_tkn`, and `tickSpacing`.
      ///@param index The index.
      ///@return The field.
      function level3s(OLKey memory olKey, int index) external view returns (Field);
    
      ///@notice Gets a level 2 field.
      ///@param olKey The offer list key given by (maker) `outbound_tkn`, (maker) `inbound_tkn`, and `tickSpacing`.
      ///@param index The index.
      ///@return The field.
      function level2s(OLKey memory olKey, int index) external view returns (Field);
    
      ///@notice Gets a level 1 field.
      ///@param olKey The offer list key given by (maker) `outbound_tkn`, (maker) `inbound_tkn`, and `tickSpacing`.
      ///@param index The index.
      ///@return Yhe field.
      function level1s(OLKey memory olKey, int index) external view returns (Field);
    
      ///@notice Gets the root from local.
      ///@param olKey The offer list key given by (maker) `outbound_tkn`, (maker) `inbound_tkn`, and `tickSpacing`.
      ///@return The root.
      function root(OLKey memory olKey) external view returns (Field);
    
      // # Internal functions
    
      ///@notice Internal function used to flashloan tokens from taker to maker, for maker to source the promised liquidity.
      ///@param sor Data about an order-offer match.
      ///@param taker The taker.
      ///@return gasused The amount of gas used for `makerExecute`.
      ///@return makerData The data returned by `makerExecute`.
      ///@dev Not to be called externally - only external to be able to revert.
      function flashloan(MgvLib.SingleOrder memory sor, address taker) external returns (uint gasused, bytes32 makerData);
    
      ///@notice Internal function used to clean failing offers.
      ///@param olKey The offer list key given by (maker) `outbound_tkn`, (maker) `inbound_tkn`, and `tickSpacing`.
      ///@param offerId The id of the offer on Mangrove.
      ///@param tick Must be `>= MIN_TICK` and `<= MAX_TICK`. The `tick` induces a price which is `1.0001^tick`. The actual tick of the offer will be the smallest tick `offerTick > tick` that satisfies `offerTick % tickSpacing == 0`.
      ///@param gasreq The gas required for the offer.
      ///@param takerWants Must be `<= MAX_SAFE_VOLUME`. The amount of `olKey.outbound_tkn` the taker wants.
      ///@param taker The taker used for transfers (should be able to deliver token amounts).
      ///@return bounty The bounty paid.
      ///@dev Not to be called externally - only external to be able to revert.
      function internalCleanByImpersonation(
        OLKey memory olKey,
        uint offerId,
        Tick tick,
        uint gasreq,
        uint takerWants,
        address taker
      ) external returns (uint bounty);
    
      ///@notice Fall back function (forwards calls to `MgvAppendix`).
      ///@param callData The call data.
      ///@return The result.
      fallback(bytes calldata callData) external returns (bytes memory);
    }
    
  • node_modules/@mangrovedao/mangrove-core/lib/core/BitLib.sol
    // SPDX-License-Identifier: MIT
    pragma solidity ^0.8.17;
    
    library BitLib {
      // - if x is a nonzero uint64: 
      //   return number of zeroes in x that do not have a 1 to their right
      // - otherwise:
      //    return 64
      function ctz64(uint x) internal pure returns (uint256 c) {
        unchecked {
          assembly ("memory-safe") {
            // clean
            x:= and(x,0xffffffffffffffff)
    
            // 7th bit
            c:= shl(6,iszero(x))
    
            // isolate lsb
            x := and(x, add(not(x), 1))
    
            // 6th bit
            c := or(c,shl(5, lt(0xffffffff, x)))
    
            // debruijn lookup
            c := or(c, byte(shr(251, mul(shr(c, x), shl(224, 0x077cb531))), 
                0x00011c021d0e18031e16140f191104081f1b0d17151310071a0c12060b050a09))
          }
        }
      }
    
      // Function fls is MIT License. Copyright (c) 2022 Solady.
    /// @dev find last set.
        /// Returns the index of the most significant bit of `x`,
        /// counting from the least significant bit position.
        /// If `x` is zero, returns 256.
        /// Equivalent to `log2(x)`, but without reverting for the zero case.
        function fls(uint256 x) internal pure returns (uint256 r) {
            assembly ("memory-safe") {
                r := shl(8, iszero(x))
    
                r := or(r, shl(7, lt(0xffffffffffffffffffffffffffffffff, x)))
                r := or(r, shl(6, lt(0xffffffffffffffff, shr(r, x))))
                r := or(r, shl(5, lt(0xffffffff, shr(r, x))))
    
                // For the remaining 32 bits, use a De Bruijn lookup.
                x := shr(r, x)
                x := or(x, shr(1, x))
                x := or(x, shr(2, x))
                x := or(x, shr(4, x))
                x := or(x, shr(8, x))
                x := or(x, shr(16, x))
    
                // forgefmt: disable-next-item
                r := or(r, byte(shr(251, mul(x, shl(224, 0x07c4acdd))),
                    0x0009010a0d15021d0b0e10121619031e080c141c0f111807131b17061a05041f))
            }
        }
    }
    
  • node_modules/@mangrovedao/mangrove-core/src/core/MgvLib.sol
    // SPDX-License-Identifier: MIT
    
    /* `MgvLib` contains data structures returned by external calls to Mangrove and the interfaces it uses for its own external calls. */
    
    pragma solidity ^0.8.10;
    
    import "@mgv/src/preprocessed/Structs.post.sol";
    import {IERC20} from "@mgv/lib/IERC20.sol";
    import {Density, DensityLib} from "@mgv/lib/core/DensityLib.sol";
    import "@mgv/lib/core/TickTreeLib.sol";
    import "@mgv/lib/core/TickLib.sol";
    
    /* `OLKey` (for "OfferListKey") contains the information that characterizes an offer list:
      * `outbound_tkn`, token that goes from the maker to the taker (to remember the direction, imagine the token as going out of the Mangrove offer, towards the taker)
      * `inbound_tkn`, token that goes from the taker to the maker (to remember the direction, imagine the token as going into Mangrove from the taker)
      * `tickSpacing`, how many ticks should be jumped between available price points. More volatile outbound/inbound pairs should have a larger `tickSpacing`. */
    struct OLKey {
      address outbound_tkn;
      address inbound_tkn;
      uint tickSpacing;
    }
    
    /* Globally enable `olKey.method(...)` */
    using OLLib for OLKey global;
    
    library OLLib {
      /* The id of an `OLKey` should be `keccak256(abi.encode(olKey))`
      To save gas, `id()` directly hashes the memory (which matches the ABI encoding; there is a fuzz test on that). If the memory layout changes, this function must be updated. */
      function hash(OLKey memory olKey) internal pure returns (bytes32 _id) {
        assembly ("memory-safe") {
          _id := keccak256(olKey, 96)
        }
      }
    
      /* Creates a flipped copy of the `olKey` with same `tickSpacing`. */
      function flipped(OLKey memory olKey) internal pure returns (OLKey memory) {
        return OLKey(olKey.inbound_tkn, olKey.outbound_tkn, olKey.tickSpacing);
      }
    
      /* Convert `tick` to bin according to `olKey.tickSpacing` */
      function nearestBin(OLKey memory olKey, Tick _tick) internal pure returns (Bin) {
        return _tick.nearestBin(olKey.tickSpacing);
      }
    
      /* Convert `bin` to `tick` according to `olKey.tickSpacing` */
      function tick(OLKey memory olKey, Bin bin) internal pure returns (Tick) {
        return bin.tick(olKey.tickSpacing);
      }
    }
    
    /* # Structs
    The structs defined in `structs.js` have their counterpart as solidity structs that are easy to manipulate for outside contracts / callers of view functions. */
    
    library MgvLib {
      /*
       Some miscellaneous data types useful to `Mangrove` and external contracts */
      //+clear+
    
      /* `SingleOrder` holds data about an order-offer match in a struct. Used by `marketOrder` (and some of its nested functions) to avoid stack too deep errors. It is used in market order and cleaning. */
      struct SingleOrder {
        OLKey olKey;
        uint offerId;
        /* The `offer` given to the maker will be cleaned of `prev`/`next` pointers. */
        Offer offer;
        /* `takerWants`/`takerGives` mutate over execution. Initially the `wants`/`gives` from the taker's pov, then actual `wants`/`gives` adjusted by offer's price and volume. */
        uint takerWants;
        uint takerGives;
        /* `offerDetail` is only populated when necessary. */
        OfferDetail offerDetail;
        Global global;
        /* The `local` given to the maker will be cleaned of tick tree information. */
        Local local;
      }
    
      /* <a id="MgvLib/OrderResult"></a> `OrderResult` holds additional data for the maker and is given to them _after_ they fulfilled an offer. It gives them their own returned data from the previous call and an `mgvData` specifying whether Mangrove encountered an error. */
    
      struct OrderResult {
        /* `makerData` holds a message that was either returned by the maker or passed as revert message at the end of the trade execution*/
        bytes32 makerData;
        /* `mgvData` is an [internal Mangrove status code](#MgvOfferTaking/statusCodes) code. */
        bytes32 mgvData;
      }
    
      /* `CleanTarget` holds data about an offer that should be cleaned, i.e. made to fail by executing it with the specified volume. It is used in `MgvOfferTaking.cleanByImpersonation`. */
      struct CleanTarget {
        uint offerId;
        Tick tick;
        uint gasreq;
        uint takerWants;
      }
    }
    
    /* # Events
    The events emitted are listed here: */
    interface HasMgvEvents {
      /* 
        Events in solidity is a hard thing to do in a optimal way. If you look at it as a purely gas efficient issue, you want to emit as few events as possible and with as few fields as possible. But as events also has to be usable for an off chain user, then doing this is not always the best solution.
    
        We tried to list the main forces that drive which events and data to emit:
    
        1. Use as little gas as possible.
        2. An indexer should be able to keep track of the state of Mangrove.
        3. Doing RPC calls directly, should be able to find offers and other information based on offer list, maker, taker, offer id, etc.
    
        These forces are in conflict and it is impossible to find a solution that is optimal for all three.
        The following events are therefore an attempt to balance the trade-offs.
      */
    
      /* ### Mangrove Creation
    
      Emitted at the creation of the new Mangrove contract */
      event NewMgv();
    
      /* ### Mangrove adds or removes wei from `maker`'s account */
      /* 
        * Credit event occurs when an offer is removed from Mangrove or when the `fund` function is called
          This is emitted when a user's account on Mangrove is credited with some native funds, to be used as provision for offers.
    
          It emits the `maker`'s address and the `amount` credited
    
          The `maker` address is indexed so that we can filter on it when doing RPC calls.
    
          These are the scenarios where it can happen:
    
          - Fund Mangrove directly
          - Fund Mangrove when posting an offer
          - When updating an offer
            - Funding Mangrove or
            - the updated offer needs less provision, than it already has. Meaning the user gets credited the difference.
          - When retracting an offer and deprovisioning it. Meaning the user gets credited the provision that was locked by the offer.
          - When an offer fails. The remaining provision gets credited back to the maker
    
          A challenge for an indexer is to know how much provision each offer locks. With the current events, an indexer is going to have to know the liveness, gasreq and gasprice of the offer. If the offer is not live, then also know if it has been deprovisioned. And know what the gasbase of the offer list was when the offer was posted. With this information an indexer can calculate the exact provision locked by the offer.
    
          The indexer also cannot deduce what scenario the credit event happened. E.g., we don't know if the credit event happened because an offer failed or because the user simply funded Mangrove.
      */
      event Credit(address indexed maker, uint amount);
      /* 
        * Debit event occurs when an offer is posted or when the `withdraw` function is called 
          This is emitted when a user's account on Mangrove is debited with some native funds.
    
          It emits the `maker`'s address and the `amount` debited.
    
          The `maker` address is indexed so that we can filter on it when doing RPC calls.
    
          These are the scenarios where it can happen:
    
          - Withdraw funds from Mangrove directly
          - When posting an offer. The user gets debited the provision that the offer locks.
          - When updating an offer and it requires more provision that it already has. Meaning the user gets debited the difference.
    
          Same challenges as for Credit
      */
      event Debit(address indexed maker, uint amount);
    
      /* ### Mangrove reconfiguration */
      /*  
      This event is emitted when an offer list is activated or deactivated. Meaning one half of a market is opened.
    
      It emits the `olKeyHash` and the boolean `value`. By emitting this, an indexer will be able to keep track of what offer lists are active and what their hash is.
    
      The `olKeyHash` and both token addresses are indexed, so that we can filter on it when doing RPC calls.
      */
      event SetActive(
        bytes32 indexed olKeyHash, address indexed outbound_tkn, address indexed inbound_tkn, uint tickSpacing, bool value
      );
    
      /*
      This event is emitted when the fee of an offer list is changed.
    
      It emits the `olKeyHash` and the `value`. By emitting this, an indexer will be able to keep track of what fee each offer list has.
    
      The `olKeyHash` is indexed, so that we can filter on it when doing RPC calls.
      */
    
      event SetFee(bytes32 indexed olKeyHash, uint value);
    
      /*
      This event is emitted when the gasbase of an offer list is changed.
    
      It emits the `olKeyHash` and the `offer_gasbase`. By emitting this, an indexer will be able to keep track of what gasbase each offer list has.
    
      The `olKeyHash` is indexed, so that we can filter on it when doing RPC calls.
      */
      event SetGasbase(bytes32 indexed olKeyHash, uint offer_gasbase);
    
      /*
      This event is emitted when the governance of Mangrove is set.
    
      It emits the `governance` address. By emitting this, an indexer will be able to keep track of what governance address Mangrove has.
    
      No fields are indexed as there is no need for RPC calls to filter on this.
      */
      event SetGovernance(address value);
    
      /*
      This event is emitted when the monitor address of Mangrove is set. Be aware that the address for Monitor is also the address for the oracle.
    
      It emits the `monitor` / `oracle` address. By emitting this, an indexer will be able to keep track of what monitor/oracle address Mangrove use.
    
      No fields are indexed as there is no need for RPC calls to filter on this.
      */
      event SetMonitor(address value);
    
      /*
      This event is emitted when the configuration for whether to use an oracle or not is set.
    
      It emits the `useOracle`, which is a boolean value, that controls whether or not Mangrove reads its `gasprice` and `density` from an oracle or uses its own local values. By emitting this, an indexer will be able to keep track of if Mangrove is using an oracle.
    
      No fields are indexed as there is no need for RPC calls to filter on this.
      */
      event SetUseOracle(bool value);
    
      /*
      This event is emitted when the configuration for notify on Mangrove is set.
    
      It emits a boolean value, to tell whether or not notify is active. By emitting this, an indexer will be able to keep track of whether or not Mangrove notifies the Monitor/Oracle when and offer is taken, either successfully or not.
    
      No fields are indexed as there is no need for RPC calls to filter on this.
      */
      event SetNotify(bool value);
    
      /*
      This event is emitted when the gasmax of Mangrove is set.
    
      It emits the `gasmax`. By emitting this, an indexer will be able to keep track of what gasmax Mangrove has. Read more about Mangroves gasmax on [docs.mangrove.exchange](https://docs.mangrove.exchange)
    
      No fields are indexed as there is no need for RPC calls to filter on this.
      */
      event SetGasmax(uint value);
    
      /*
      This event is emitted when the density of an offer list is changed.
    
      It emits the `olKeyHash` and the `density`. By emitting this, an indexer will be able to keep track of what density each offer list has.
    
      The `olKeyHash` is indexed, so that we can filter on it when doing RPC calls.
      */
      event SetDensity96X32(bytes32 indexed olKeyHash, uint value);
    
      /*
      This event is emitted when the max recursion depth of Mangrove is set.
    
      It emits the max depth `value`. By emitting this, an indexer will be able to keep track of what max recursion depth Mangrove has. Read more about Mangroves max recursion depth on [docs.mangrove.exchange](https://docs.mangrove.exchange)
      */
    
      event SetMaxRecursionDepth(uint value);
    
      /*
      This event is emitted when the max gasreq for failing offers of Mangrove is set.
    
      It emits the max gasreq for failing offers `value`. By emitting this, an indexer will be able to keep track of what max gasreq for failing offers Mangrove has. Read more about Mangroves max gasreq for failing offers on [docs.mangrove.exchange](https://docs.mangrove.exchange)
      */
      event SetMaxGasreqForFailingOffers(uint value);
    
      /*
      This event is emitted when the gasprice of Mangrove is set.
    
      It emits the `gasprice`. By emitting this, an indexer will be able to keep track of what gasprice Mangrove has. Read more about Mangroves gasprice on [docs.mangrove.exchange](https://docs.mangrove.exchange)
    
      No fields are indexed as there is no need for RPC calls to filter on this.
      */
      event SetGasprice(uint value);
      /* ### Clean order execution */
      /*
      This event is emitted when a user tries to clean offers on Mangrove, using the build in clean functionality.
    
      It emits the `olKeyHash`, the `taker` address and `offersToBeCleaned`, which is the number of offers that should be cleaned. By emitting this event, an indexer can save what `offer list` the user is trying to clean and what `taker` is being used. 
      This way it can keep a context for the following events being emitted (Just like `OrderStart`). The `offersToBeCleaned` is emitted so that an indexer can keep track of how many offers the user tried to clean. 
      Combining this with the amount of `OfferFail` events emitted, then an indexer can know how many offers the user actually managed to clean. This could be used for analytics.
    
      The fields `olKeyHash` and `taker` are indexed, so that we can filter on them when doing RPC calls.
      */
      event CleanStart(bytes32 indexed olKeyHash, address indexed taker, uint offersToBeCleaned);
    
      /*
      This event is emitted when a Clean operation is completed.
    
      It does not emit any fields. This event is only needed in order to know that the clean operation is completed. This way an indexer can know exactly in what context we are in. 
      It could emit the total bounty received, but in order to know who got the bounty, an indexer would still be needed or we would have to emit the taker address as well, in order for an RPC call to find the data. 
      But an indexer would still be able to find this info, by collecting all the previous events. So we do not emit the bounty.
      */
      event CleanComplete();
    
      /* ### Market order execution */
      /*
      This event is emitted when a market order is started on Mangrove.
    
      It emits the `olKeyHash`, the `taker`, the `maxTick`, `fillVolume` and `fillWants`.
    
      The fields `olKeyHash` and `taker` are indexed, so that we can filter on them when doing RPC calls.
    
      By emitting this an indexer can keep track of what context the current market order is in. 
      E.g. if a user starts a market order and one of the offers taken also starts a market order, then we can in an indexer have a stack of started market orders and thereby know exactly what offer list the order is running on and the taker.
    
      By emitting `maxTick`, `fillVolume` and `fillWants`, we can now also know how much of the market order was filled and if it matches the price given. See OrderComplete for more.
      */
      event OrderStart(bytes32 indexed olKeyHash, address indexed taker, Tick maxTick, uint fillVolume, bool fillWants);
    
      /*
      This event is emitted when a market order is finished.
    
      It emits `olKeyHash`, the `taker` and the total `fee paid`. We need this event, in order to know that a market order is completed. This way an indexer can know exactly in what context we are in.
      The total `fee paid` for that market order, is needed, as we do not emit this any other places. The fields `olKeyHash` and `taker` is not needed for an indexer, but they are emitted and indexed in order for RPC calls to filter and find the fee.
      */
      event OrderComplete(bytes32 indexed olKeyHash, address indexed taker, uint fee);
    
      /* ### Offer execution */
      /*
      This event is emitted when an offer is successfully taken. Meaning both maker and taker has gotten their funds.
    
      It emits the `olKeyHash`, `taker` address, the `offerId` and the `takerWants` and `takerGives` that the offer was taken at. 
      Just as for `OfferFail`, the `olKeyHash` and `taker` are not needed for an indexer to work, but are needed for RPC calls. 
      `olKeyHash` `taker` and `id` are indexed so that we can filter on them when doing RPC calls. As `maker` can be a strategy and not the actual owner, then we chose not to emit it here and to mark the field `id` indexed, 
      the strat should emit the relation between maker and offerId. This way you can still by RPC calls find the relevant offer successes
      So they are emitted and indexed for that reason. Just like `OfferFail` this event is emitted during posthook end, so it is emitted in reverse order compared to what order they are taken. 
      This event could be emitted at offer execution, but for consistency we emit it at posthook end.
    
      If the posthook of the offer fails. Then we emit `OfferSuccessWithPosthookData` instead of just `OfferSuccess`. This event has one extra field, which is the reason for the posthook failure. 
      By emitting the posthook data, an indexer can keep track of the reason posthook fails, this could for example be used for analytics.
    
      By emitting `offerId`, `wants` and `gives`, an indexer can keep track of whether the offer was partially or fully taken, by looking at the what the offer was posted at.
      */
      event OfferSuccess(
        bytes32 indexed olKeyHash, address indexed taker, uint indexed id, uint takerWants, uint takerGives
      );
    
      event OfferSuccessWithPosthookData(
        bytes32 indexed olKeyHash,
        address indexed taker,
        uint indexed id,
        uint takerWants,
        uint takerGives,
        bytes32 posthookData
      );
    
      /*
      This event is emitted when an offer fails, because of a maker error.
    
      It emits `olKeyHash`, `taker`, the `offerId`, the offers `takerWants`, `takerGives`, `penalty` and the `reason` for failure. 
      `olKeyHash` and `taker` are all fields that we do not need, in order for an indexer to work, as an indexer will be able the get that info from the former `OrderStart` and `OfferWrite` events. 
      But in order for RPC call to filter on this, we need to emit them. 
      `olKeyHash` `taker` and `id` are indexed so that we can filter on them when doing RPC calls. As `maker` can be a strategy and not the actual owner, then we chose to not emit it here and to mark the field `id` indexed, 
      the strat should emit the relation between maker and offerId. This way you can still by RPC calls find the relevant offer successes
    
      If the posthook of the offer fails. Then we emit `OfferFailWithPosthookData` instead of just `OfferFail`. 
      This event has one extra field, which is the reason for the posthook failure. By emitting the posthook data, an indexer can keep track of the reason posthook fails, this could for example be used for analytics.
    
      This event is emitted during posthook end, we wait to emit this event to the end, because we need the information of `penalty`, which is only available at the end of the posthook. 
      This means that `OfferFail` events are emitted in reverse order, compared to what order they are taken. This is due to the way we handle posthooks. The same goes for `OfferSuccess`.
    
      By emitting this event, an indexer can keep track of, if an offer failed and thereby if the offer is live. 
      By emitting the `takerWants` and `takerGives` that the offer was taken with, then an indexer can keep track of these amounts, which could be useful for e.g. strategy manager, to know if their offers fail at a certain amount.
      */
      event OfferFail(
        bytes32 indexed olKeyHash,
        address indexed taker,
        uint indexed id,
        uint takerWants,
        uint takerGives,
        uint penalty,
        // `mgvData` may only be `"mgv/makerRevert"`, `"mgv/makerTransferFail"` or `"mgv/makerReceiveFail"`
        bytes32 mgvData
      );
    
      event OfferFailWithPosthookData(
        bytes32 indexed olKeyHash,
        address indexed taker,
        uint indexed id,
        uint takerWants,
        uint takerGives,
        uint penalty,
        // `mgvData` may only be `"mgv/makerRevert"`, `"mgv/makerTransferFail"` or `"mgv/makerReceiveFail"`
        bytes32 mgvData,
        bytes32 posthookData
      );
    
      /* 
      ### After `permit` and `approve` 
        This is emitted when a user permits another address to use a certain amount of its funds to do market orders, or when a user revokes another address to use a certain amount of its funds.
    
        Approvals are based on the pair of outbound and inbound token. Be aware that it is not offer list bases, as an offer list also holds the tick spacing.
    
        We emit `outbound` token, `inbound` token, `owner`, msg.sender (`spender`), `value`. Where `owner` is the one who owns the funds, `spender` is the one who is allowed to use the funds and `value` is the amount of funds that is allowed to be used.
    
        Outbound, inbound and owner is indexed, this way one can filter on these fields when doing RPC calls. If we could somehow combine outbound and inbound into one field, then we could also index the spender.
      */
      event Approval(
        address indexed outbound_tkn, address indexed inbound_tkn, address indexed owner, address spender, uint value
      );
    
      /* ### Mangrove closure */
      event Kill();
    
      /* ### An offer was created or updated.
      This event is emitted when an offer is posted on Mangrove.
    
      It emits the `olKeyHash`, the `maker` address, the `tick`, the `gives`, the `gasprice`, `gasreq` and the offers `id`.
    
      By emitting the `olKeyHash` and `id`, an indexer will be able to keep track of each offer, because offer list and id together create a unique id for the offer. By emitting the `maker` address, we are able to keep track of who has posted what offer. The `tick` and `gives`, enables an indexer to know exactly how much an offer is willing to give and at what price, this could for example be used to calculate a return. The `gasprice` and `gasreq`, enables an indexer to calculate how much provision is locked by the offer, see `Credit` for more information.
    
      The fields `olKeyHash` and `maker` are indexed, so that we can filter on them when doing RPC calls.
      */
      event OfferWrite(
        bytes32 indexed olKeyHash, address indexed maker, int tick, uint gives, uint gasprice, uint gasreq, uint id
      );
    
      /*
      This event is emitted when a user retracts his offer.
    
      It emits the `olKeyHash`, the `maker` address, `offerId` and whether or not the user chose to `deprovision` the offer.
    
      By emitting this event an indexer knows whether or not an offer is live. And whether or not an offer is deprovisioned. This is important because we need to know this, when we try to calculate how much an offer locks in provision. See the description of `Credit` for more info.
    
      The `maker` is not needed for an indexer to work, but is needed for RPC calls, so it is emitted and indexed for that reason. The `olKeyHash` is only indexed because it is needed for RPC calls. 
      */
      event OfferRetract(bytes32 indexed olKeyHash, address indexed maker, uint id, bool deprovision);
    }
    
    /* # IMaker interface */
    interface IMaker {
      /* Called upon offer execution. 
      - If the call throws, Mangrove will not try to transfer funds and the first 32 bytes of revert reason are passed to `makerPosthook` as `makerData`
      - If the call returns normally, `returnData` is passed to `makerPosthook` as `makerData` and Mangrove will attempt to transfer the funds.
      */
      function makerExecute(MgvLib.SingleOrder calldata order) external returns (bytes32);
    
      /* Called after all offers of an order have been executed. Posthook of the last executed order is called first and full reentrancy into Mangrove is enabled at this time. `order` recalls key arguments of the order that was processed and `result` recalls important information for updating the current offer. (see [above](#MgvLib/OrderResult))*/
      function makerPosthook(MgvLib.SingleOrder calldata order, MgvLib.OrderResult calldata result) external;
    }
    
    /* # Monitor interface
    If enabled, the monitor receives notification after each offer execution and is read for each offer list's `gasprice` and `density`. */
    interface IMgvMonitor {
      function notifySuccess(MgvLib.SingleOrder calldata sor, address taker) external;
    
      function notifyFail(MgvLib.SingleOrder calldata sor, address taker) external;
    
      function read(OLKey memory olKey) external view returns (uint gasprice, Density density);
    }
    
  • node_modules/@mangrovedao/mangrove-core/lib/core/TickTreeLib.sol
    // SPDX-License-Identifier: BUSL-1.1
    pragma solidity ^0.8.17;
    
    import "@mgv/lib/core/Constants.sol";
    import {Tick} from "@mgv/lib/core/TickLib.sol";
    import {BitLib} from "@mgv/lib/core/BitLib.sol";
    import {console2 as csf} from "@mgv/forge-std/console2.sol";
    import {Local} from "@mgv/src/preprocessed/Local.post.sol";
    
    
    /* # Libraries for tick tree manipulation 
    
    Offers in Mangrove are structured in a tree so that offer insertion, removal, and update can happen in constant time.
    
    The tree has the following structure: nodes at height 0, 1, 2 and 3 are bit fields (type `Field`) and nodes at height 4 (leaves) are arrays of pairs of offers (type `Leaf`).
    
    - The root node is a 2-bit `Field` and has 2 child nodes.
    - The nodes below are called `level1` nodes and are 64-bit `Field`s, with 64 child nodes each.
    - The nodes below are called `level2` nodes and are 64-bit `Field`s with 64 child nodes each.
    - The nodes below are called `level3` nodes and are 64-bit `Field`s with 64 child nodes each.
    - The nodes below are called `leaf` nodes and are arrays of pairs of offers. Each pair of offers represents the first and last offer of a bin.
    - Bins are linked lists of offers.
    
    For each field, the i-th bit (starting from least significant) is set if there is at least one bin holding offers below the i-th child of the field. */
    
    
    /* Globally enable `leaf.method(...)` */
    type Leaf is uint;
    using LeafLib for Leaf global;
    
    /* Each `Leaf` holds information about 4 bins as follows: `[firstId,lastId] [firstId,lastId] [firstId,lastId] [firstId,lastId]`. For each bin `firstId` is used by `marketOrder` to start consuming offers in the bin (each offer contains a pointer to the next offer in the bin, until `lastId` is reacher). `lastId` is used when inserting offers in the bin: the newly inserted offer replaces the last offer.
    
    Each `leaf` has an `index`: it is the number of leaves before it.
    */
    library LeafLib {
      Leaf constant EMPTY = Leaf.wrap(uint(0));
    
      /* Checks if a leaf is dirty or not (see below for more on dirty leaves). */
      function dirty(Leaf leaf) internal pure returns (DirtyLeaf) {
        unchecked {
          assembly ("memory-safe") {
            leaf := or(iszero(leaf),leaf)
          }
          return DirtyLeaf.wrap(Leaf.unwrap(leaf));
        }
      }
    
      function eq(Leaf leaf1, Leaf leaf2) internal pure returns (bool) {
        unchecked {
          return Leaf.unwrap(leaf1) == Leaf.unwrap(leaf2);
        }
      }
    
      function isEmpty(Leaf leaf) internal pure returns (bool) {
        unchecked {
          return Leaf.unwrap(leaf) == Leaf.unwrap(EMPTY);
        }
      }
    
      /* `bool -> int` cast */
      function uint_of_bool(bool b) internal pure returns (uint u) {
        unchecked {
          assembly ("memory-safe") {
            u := b
          }
        }
      }
    
      /* Set either tick's first (at pos*size) or last (at pos*size + 1) */
      function setPosFirstOrLast(Leaf leaf, uint pos, uint id, bool last) internal pure returns (Leaf) {
        unchecked {
          uint before = OFFER_BITS * ((pos * 2) + uint_of_bool(last));
    
          uint cleanupMask = ~(OFFER_MASK << (256 - OFFER_BITS - before));
    
          uint shiftedId = id << (256 - OFFER_BITS - before);
          uint newLeaf = Leaf.unwrap(leaf) & cleanupMask | shiftedId;
          return Leaf.wrap(newLeaf);
        }
      }
    
      /* Assume `leaf` is the leaf of `bin`. Set the first offer of `bin` in leaf to `id`. */
      function setBinFirst(Leaf leaf, Bin bin, uint id) internal pure returns (Leaf) {
        unchecked {
          uint posInLeaf = bin.posInLeaf();
          return setPosFirstOrLast(leaf, posInLeaf, id, false);
        }
      }
    
      /* Assume `leaf` is the leaf of `bin`. Set the last offer of `bin` in leaf to `id`. */
      function setBinLast(Leaf leaf, Bin bin, uint id) internal pure returns (Leaf) {
        unchecked {
          uint posInLeaf = bin.posInLeaf();
          return setPosFirstOrLast(leaf, posInLeaf, id, true);
        }
      }
    
      // _This function is not used by Mangrove but is useful for MgvReader._ 
      /* Assumes `leaf` is the leaf of `bin`. Erase contents of bins in `leaf` to (not including) `bin`. */
      function eraseBelow(Leaf leaf, Bin bin) internal pure returns (Leaf) {
        unchecked {
          uint mask = ONES >> ((bin.posInLeaf() + 1) * OFFER_BITS * 2);
          return Leaf.wrap(Leaf.unwrap(leaf) & mask);
        }
      }
    
      // _This function is not used by Mangrove but is useful for MgvReader._ 
      /* Assumes `leaf` is the leaf of `bin`. Erase contents of bins in `leaf` from (not including) `bin`. */
      function eraseAbove(Leaf leaf, Bin bin) internal pure returns (Leaf) {
        unchecked {
          uint mask = ~(ONES >> (bin.posInLeaf() * OFFER_BITS * 2));
          return Leaf.wrap(Leaf.unwrap(leaf) & mask);
        }
      }
    
    
      // _This function is not used by Mangrove but is part of the library._
      /* Return the id of the first offer in `bin`, assuming `bin`'s leaf is `leaf`. */
      function firstOfBin(Leaf leaf, Bin bin) internal pure returns (uint) {
        unchecked {
          return firstOfPos(leaf, bin.posInLeaf());
        }
      }
    
      /* Return the id of the last offer in `bin`, assuming `bin`'s leaf is `leaf`. */
      function lastOfBin(Leaf leaf, Bin bin) internal pure returns (uint) {
        unchecked {
          return lastOfPos(leaf, bin.posInLeaf());
        }
      }
    
      /* Return first offer of pair in position `pos` */
      function firstOfPos(Leaf leaf, uint pos) internal pure returns (uint) {
        unchecked {
          uint raw = Leaf.unwrap(leaf);
          return uint(raw << (pos * OFFER_BITS * 2) >> (256 - OFFER_BITS));
        }
      }
    
      /* Return second offer of pair in position `pos` */
      function lastOfPos(Leaf leaf, uint pos) internal pure returns (uint) {
        unchecked {
          uint raw = Leaf.unwrap(leaf);
          return uint(raw << (OFFER_BITS * ((pos * 2) + 1)) >> (256 - OFFER_BITS));
        }
      }
    
      /* Return the position of the first pair of `leaf` (0, 1, 2 or 3) that has a nonzero offer.
    
      Explanation:
      Note that unlike in fields, have their low bin on the most significant bits.
      `pos` is initially 1 if `leaf` has some nonzero bit in its MSB half, 0 otherwise. Then `pos` is `A | B`, where `A` is `iszero(pos)<<1`, so `A` is 0 if leaf has some nonzero bit in its MSB half, 2 otherwise. And `B` is 0 if `leaf >> (pos << 7)` has some nonzero bit in its most significant 192 bits, 1 otherwise.
      */
      function bestNonEmptyBinPos(Leaf leaf) internal pure returns (uint pos) {
        assembly("memory-safe") {
          pos := gt(leaf,0xffffffffffffffffffffffffffffffff)
          pos := or(shl(1,iszero(pos)),iszero(gt(shr(shl(7,pos),leaf),0xffffffffffffffff)))
        }
      }
    
      /* Return the offer id of the first offer of the first non-empty pair in `leaf`. */
      function bestOfferId(Leaf leaf) internal pure returns (uint offerId) {
        unchecked {
          return firstOfPos(leaf,bestNonEmptyBinPos(leaf));
        }
      }
    }
    
    
    /* Bins are numbered from MIN_BIN to MAX_BIN (inclusive). Each bin contains the offers at a given price. For a given `tickSpacing`, bins represent the following prices (centered on the central bin): 
    ```
    ...
    1.0001^-(tickSpacing*2)
    1.0001^-(tickSpacing*1)
    1.0001
    1.0001^(tickSpacing*1)
    1.0001^(tickSpacing*2)
    ...
    ``` 
    
    There are 4 bins per leaf, `4 * 64` bins per level3, etc. The leaf of a bin is the leaf that holds its first/last offer id. The level3 of a bin is the level3 field above its leaf; the level2 of a bin is the level2 field above its level3, etc. */
    
    /* Globally enable `bin.method(...)` */
    type Bin is int;
    using TickTreeLib for Bin global;
    
    library TickTreeLib {
    
      function eq(Bin bin1, Bin bin2) internal pure returns (bool) {
        unchecked {
          return Bin.unwrap(bin1) == Bin.unwrap(bin2);
        }
      }
    
      function inRange(Bin bin) internal pure returns (bool) {
        unchecked {
          return Bin.unwrap(bin) >= MIN_BIN && Bin.unwrap(bin) <= MAX_BIN;
        }
      }
    
      // _This function is not used by Mangrove but is part of the library for convenience._
      
      /* Not optimized for gas. Returns the bin induced by the branch formed by the arguments. Returned value will make no sense if any of the `Field` arguments are empty. */
      function bestBinFromBranch(uint binPosInLeaf,Field level3, Field level2, Field level1, Field root) internal pure returns (Bin) {
        unchecked {
          Local local;
          local = local.binPosInLeaf(binPosInLeaf).level3(level3).level2(level2).level1(level1).root(root);
          return bestBinFromLocal(local);
        }
      }
    
      /* Returns tick held by the `bin`, given a `tickSpacing`. */
      function tick(Bin bin, uint tickSpacing) internal pure returns (Tick) {
        return Tick.wrap(Bin.unwrap(bin) * int(tickSpacing));
      }
    
      /* Returns the bin induced by the branch held in `local`. Returned value will make no sense if any of the fields of `local` are empty. */
      function bestBinFromLocal(Local local) internal pure returns (Bin) {
        unchecked {
          uint ubin = local.binPosInLeaf() |
            ((BitLib.ctz64(Field.unwrap(local.level3())) |
              (BitLib.ctz64(Field.unwrap(local.level2())) |
                (BitLib.ctz64(Field.unwrap(local.level1())) |
                  uint(
                    (int(BitLib.ctz64(Field.unwrap(local.root())))-ROOT_SIZE/2) << LEVEL_SIZE_BITS)) 
                  << LEVEL_SIZE_BITS)
                << LEVEL_SIZE_BITS)
              << LEAF_SIZE_BITS);
          return Bin.wrap(int(ubin));
        }
      }
    
      /* Returns the index of the leaf that holds `bin` */
      function leafIndex(Bin bin) internal pure returns (int) {
        unchecked {
          return Bin.unwrap(bin) >> LEAF_SIZE_BITS;
        }
      }
    
      /* Returns the position of `bin` in its leaf. */
      function posInLeaf(Bin bin) internal pure returns (uint) {
        unchecked {
          return uint(Bin.unwrap(bin)) & LEAF_SIZE_MASK;
        }
      }
    
      /* Returns the index of `bin`'s level3 */
      function level3Index(Bin bin) internal pure returns (int) {
        unchecked {
          return Bin.unwrap(bin) >> (LEAF_SIZE_BITS + LEVEL_SIZE_BITS);
        }
      }
    
      /* Returns the position of `bin`'s leaf in `bin`'s level3 */
      function posInLevel3(Bin bin) internal pure returns (uint) {
        unchecked {
          return uint(bin.leafIndex()) & LEVEL_SIZE_MASK;
        }
      }
    
      /* Returns the index of `bin`'s level2 */
      function level2Index(Bin bin) internal pure returns (int) {
        unchecked {
          return Bin.unwrap(bin) >> (LEAF_SIZE_BITS + 2* LEVEL_SIZE_BITS);
        }
      }
    
      /* Returns the position of `bin`'s level3 in `bin`'s level2 */
      function posInLevel2(Bin bin) internal pure returns (uint) {
        unchecked {
          return uint(bin.level3Index()) & LEVEL_SIZE_MASK;
        }
      }
    
      /* Returns the index of `bin`'s level1 */
      function level1Index(Bin bin) internal pure returns (int) {
        unchecked {
          return Bin.unwrap(bin) >> (LEAF_SIZE_BITS + 3 * LEVEL_SIZE_BITS);
        }
      }
    
      /* Returns the position of `bin`'s level2 in `bin`'s level1 */
      function posInLevel1(Bin bin) internal pure returns (uint) {
        unchecked {
          return uint(bin.level2Index()) & LEVEL_SIZE_MASK;
        }
      }
    
      /* Returns the position of `bin`'s level1 in root */
      function posInRoot(Bin bin) internal pure returns (uint) {
        unchecked {
          return uint(bin.level1Index() + ROOT_SIZE / 2);
        }
      }
    
      /* `<`, typed for bin */
      function strictlyBetter(Bin bin1, Bin bin2) internal pure returns (bool) {
        unchecked {
          return Bin.unwrap(bin1) < Bin.unwrap(bin2);
        }
      }
    
      /* `<=`, typed for bin */
      function better(Bin bin1, Bin bin2) internal pure returns (bool) {
        unchecked {
          return Bin.unwrap(bin1) <= Bin.unwrap(bin2);
        }
      }  
    
    }
    
    /* Globally enable `field.method(...)` */
    type Field is uint;
    using FieldLib for Field global;
    
    /* Fields are bit fields. Each bit position of a field corresponds to a child of the node in the tick tree. The i-th bit of a field is set iff its child is the parent of at least one non-emtpy field.
    
    Using bit fields market orders can locate the next bin containing an offer in constant time: once a leaf has been emptied, one must at most walk up along the branch of the current bin, up to the first 1 in a field, then go down to the of the tree, then go down the corresponding child to the nonempty bin found.
    */
    
    /* In fields, positions are counted from the least significant bits */
    library FieldLib {
      Field constant EMPTY = Field.wrap(uint(0));
    
      /* Checks if a field is dirty or not (see below for more on dirty fields). */
      function dirty(Field field) internal pure returns (DirtyField) {
        unchecked {
          assembly ("memory-safe") {
            field := or(TOPBIT,field)
          }
          return DirtyField.wrap(Field.unwrap(field));
        }
      }
    
      function eq(Field field1, Field field2) internal pure returns (bool) {
        unchecked {
          return Field.unwrap(field1) == Field.unwrap(field2);
        }
      }
    
      function isEmpty(Field field) internal pure returns (bool) {
        unchecked {
          return Field.unwrap(field) == Field.unwrap(EMPTY);
        }
      }
    
      /* Flip the bit at the position of `bin`'s leaf */
      function flipBitAtLevel3(Field level3, Bin bin) internal pure returns (Field) {
        unchecked {
          uint pos = bin.posInLevel3();
          level3 = Field.wrap(Field.unwrap(level3) ^ (1 << pos));
          return level3;
        }
      }
    
      /* Flip the bit at the position of `bin`'s level3 */
      function flipBitAtLevel2(Field level2, Bin bin) internal pure returns (Field) {
        unchecked {
          uint pos = bin.posInLevel2();
          level2 = Field.wrap(Field.unwrap(level2) ^ (1 << pos));
          return level2;
        }
      }
    
      /* Flip the bit at the position of `bin`'s level2 */
      function flipBitAtLevel1(Field level1, Bin bin) internal pure returns (Field) {
        unchecked {
          uint pos = bin.posInLevel1();
          level1 = Field.wrap(Field.unwrap(level1) ^ (1 << pos));
          return level1;
        }
      }
    
      /* Flip the bit at the position of `bin`'s level1 */
      function flipBitAtRoot(Field root, Bin bin) internal pure returns (Field) {
        unchecked {
          uint pos = bin.posInRoot();
          root = Field.wrap(Field.unwrap(root) ^ (1 << pos));
          return root;
        }
      }
    
      // _This function is not used by Mangrove but is useful for MgvReader._ 
      /* Assumes `field` is the level3 of `bin`. Erase contents of field up to (not including) bin. */
      function eraseBelowInLevel3(Field field, Bin bin) internal pure returns (Field) {
        unchecked {
          uint mask = ONES << (bin.posInLevel3() + 1);
          return Field.wrap(Field.unwrap(field) & mask);
        }
      }
    
      // _This function is not used by Mangrove but is useful for MgvReader._ 
      /* Assumes `field` is the level3 of `bin`. Erase contents of field from (not including) bin. */
      function eraseAboveInLevel3(Field field, Bin bin) internal pure returns (Field) {
        unchecked {
          uint mask = ~(ONES << bin.posInLevel3());
          return Field.wrap(Field.unwrap(field) & mask);
        }
      }
    
      // _This function is not used by Mangrove but is useful for MgvReader._ 
      /* Assumes `field` is the level2 of `bin`. Erase contents of field up to (not including) bin. */
      function eraseBelowInLevel2(Field field, Bin bin) internal pure returns (Field) {
        unchecked {
          uint mask = ONES << (bin.posInLevel2() + 1);
          return Field.wrap(Field.unwrap(field) & mask);
        }
      }
    
      // _This function is not used by Mangrove but is useful for MgvReader._ 
      /* Assumes `field` is the level2 of `bin`. Erase contents of field from (not including) bin. */
      function eraseAboveInLevel2(Field field, Bin bin) internal pure returns (Field) {
        unchecked {
          uint mask = ~(ONES << bin.posInLevel2());
          return Field.wrap(Field.unwrap(field) & mask);
        }
      }
    
      // _This function is not used by Mangrove but is useful for MgvReader._ 
      /* Assumes `field` is the level1 of `bin`. Erase contents of field up to (not including) bin. */
      function eraseBelowInLevel1(Field field, Bin bin) internal pure returns (Field) {
        unchecked {
          uint mask = ONES << (bin.posInLevel1() + 1);
          return Field.wrap(Field.unwrap(field) & mask);
        }
      }
    
      // _This function is not used by Mangrove but is useful for MgvReader._ 
      /* Assumes `field` is the level1 of `bin`. Erase contents of field from (not including) bin. */
      function eraseAboveInLevel1(Field field, Bin bin) internal pure returns (Field) {
        unchecked {
          uint mask = ~(ONES << bin.posInLevel1());
          return Field.wrap(Field.unwrap(field) & mask);
        }
      }
    
      // _This function is not used by Mangrove but is useful for MgvReader._ 
      /* Assumes `field` is the root of `bin`. Erase contents of field up to (not including) bin. */
      function eraseBelowInRoot(Field field, Bin bin) internal pure returns (Field) {
        unchecked {
          uint mask = ONES << (bin.posInRoot() + 1);
          return Field.wrap(Field.unwrap(field) & mask);
        }
      }
    
      // _This function is not used by Mangrove but is useful for MgvReader._ 
      /* Assumes `field` is the root of `bin`. Erase contents of field from (not including) bin. */
      function eraseAboveInRoot(Field field, Bin bin) internal pure returns (Field) {
        unchecked {
          uint mask = ~(ONES << bin.posInRoot());
          return Field.wrap(Field.unwrap(field) & mask);
        }
      }
    
      /* Returns first nonzero position of `field`. Will return 64 if field is empty */
      function firstOnePosition(Field field) internal pure returns (uint) {
        unchecked {
          return BitLib.ctz64(Field.unwrap(field));
        }
      }
    
      // _This function is not used by Mangrove but is useful for MgvReader._
      /* Returns last nonzero position of `field`. Will return 256 if field is empty */
      function lastOnePosition(Field field) internal pure returns (uint) {
        unchecked {
          return BitLib.fls(Field.unwrap(field));
        }
      }
    
      /* Return index of the first nonempty level1 below `root` (`root` should not be empty) */
      function firstLevel1Index(Field root) internal pure returns (int) {
        unchecked {
          return int(root.firstOnePosition()) - ROOT_SIZE / 2;
        }
      }
    
      // _This function is not used by Mangrove but is useful for MgvReader._
      /* Return index of the last nonempty level1 below `root` (root should not be empty) */
      function lastLevel1Index(Field root) internal pure returns (int) {
        unchecked {
          return int(root.lastOnePosition()) - ROOT_SIZE / 2;
        }
      }
    
      /* Return index of the first nonempty level2 below `level1` assuming its index is `level1Index` (`level1` should not be empty). */
      function firstLevel2Index(Field level1, int level1Index) internal pure returns (int) {
        unchecked {
          return level1Index * LEVEL_SIZE + int(level1.firstOnePosition());
        }
      }
      // _This function is not used by Mangrove but is useful for MgvReader._
      /* Return index of the last nonempty level2 below `level1` assuming its index is `level1Index` (`level1` should not be empty). */
      function lastLevel2Index(Field level1, int level1Index) internal pure returns (int) {
        unchecked {
          return level1Index * LEVEL_SIZE + int(level1.lastOnePosition());
        }
      }
    
      /* Return index of the first nonempty level3 below `level2` assuming its index is `level2Index` (`level2` should not be empty). */
      function firstLevel3Index(Field level2, int level2Index) internal pure returns (int) {
        unchecked {
          return level2Index * LEVEL_SIZE + int(level2.firstOnePosition());
        }
      }
    
      // _This function is not used by Mangrove but is useful for MgvReader._
      /* Return index of the last nonempty level3 below `level2` assuming its index is `level2Index` (`level2` should not be empty). */
      function lastLevel3Index(Field level2, int level2Index) internal pure returns (int) {
        unchecked {
          return level2Index * LEVEL_SIZE + int(level2.lastOnePosition());
        }
      }
    
      /* Return index of the first nonempty leaf below `level3` assuming its index is `level3Index` (`level3` should not be empty). */
      function firstLeafIndex(Field level3, int level3Index) internal pure returns (int) {
        unchecked {
          return level3Index * LEVEL_SIZE + int(level3.firstOnePosition());
        }
      }
    
      // _This function is not used by Mangrove but is useful for MgvReader._
      /* Return index of the last nonempty leaf below `level3` assuming its index is `level3Index` (`level3` should not be empty). */
      function lastLeafIndex(Field level3, int level3Index) internal pure returns (int) {
        unchecked {
          return level3Index * LEVEL_SIZE + int(level3.lastOnePosition());
        }
      }
    
     }
    
    /* ## Clean/Dirty Fields and Leaves
    
    To save gas, leaves and fields at never zeroed out but written with a dirty bit. This is especially helpful when the price oscillates quickly between two nodes.
    
    Leaves don't have 'available bits' so an empty leaf is encoded as 1. This is not a valid leaf because for every offer pair in a leaf, either both are 0 (the bin is empty) or both are nonzero (they are the same if the bin has a single offer).
    */
    
    /* Globally enable `dirtyLeaf.method(...)` */
    type DirtyLeaf is uint;
    using DirtyLeafLib for DirtyLeaf global;
    
    library DirtyLeafLib {
      
      /* Return 0 if leaf is 1, leaf otherwise */
      function clean(DirtyLeaf leaf) internal pure returns (Leaf) {
        unchecked {
          assembly ("memory-safe") {
            leaf := xor(eq(leaf,ONE),leaf)
          }
          return Leaf.wrap(DirtyLeaf.unwrap(leaf));
        }
      }
      function isDirty(DirtyLeaf leaf) internal pure returns (bool) {
        unchecked {
          return DirtyLeaf.unwrap(leaf) == ONE;
        }
      }
      function eq(DirtyLeaf leaf1, DirtyLeaf leaf2) internal pure returns (bool) {
        unchecked {
          return DirtyLeaf.unwrap(leaf1) == DirtyLeaf.unwrap(leaf2);
        }
      }
    }
    
    /* We use `TOPBIT` as the optimized in-storage value since 1 is a valid Field value */
    
    /* For fields, it's simpler, since they do not use 64 bits we store the word with top bit at 1 and everything else at 0 to mark emptiness. */
    
    /* Globally enable `dirtyField.method(...)` */
    type DirtyField is uint;
    using DirtyFieldLib for DirtyField global;
    
    library DirtyFieldLib {
      DirtyField constant DIRTY_EMPTY = DirtyField.wrap(TOPBIT);
      DirtyField constant CLEAN_EMPTY = DirtyField.wrap(0);
    
      /* Return clean field with topbit set to 0 */
      function clean(DirtyField field) internal pure returns (Field) {
        unchecked {
          assembly ("memory-safe") {
            field := and(NOT_TOPBIT,field)
          }
          return Field.wrap(DirtyField.unwrap(field));
        }
      }
      function isDirty(DirtyField field) internal pure returns (bool) {
        unchecked {
          return DirtyField.unwrap(field) & TOPBIT == TOPBIT;
        }
      }
    
      function eq(DirtyField leaf1, DirtyField leaf2) internal pure returns (bool) {
        unchecked {
          return DirtyField.unwrap(leaf1) == DirtyField.unwrap(leaf2);
        }
      }
    }
    
    
  • src/strategies/routing_logic/abstract/AbstractRoutingLogic.sol
    // SPDX-License-Identifier: MIT
    pragma solidity ^0.8.20;
    
    import {IERC20} from "@mgv/src/core/MgvLib.sol";
    
    /// @title Routing logic
    abstract contract AbstractRoutingLogic {
      /**
       * @notice Pulls a specific amount of tokens from the fund owner
       * @param token The token to pull
       * @param fundOwner The owner of the fund
       * @param amount The amount of tokens to pull
       * @param strict Whether to enforce strict pulling
       * @return pulled The actual amount of tokens pulled
       */
      function pullLogic(IERC20 token, address fundOwner, uint amount, bool strict) external virtual returns (uint pulled);
    
      /**
       * @notice Pushes a specific amount of tokens to the fund owner
       * @param token The token to push
       * @param fundOwner The owner of the fund
       * @param amount The amount of tokens to push
       * @return pushed The actual amount of tokens pushed
       */
      function pushLogic(IERC20 token, address fundOwner, uint amount) external virtual returns (uint pushed);
    
      /**
       * @notice Returns the token balance of the fund owner
       * @param token The token to check the balance for
       * @param fundOwner The owner of the fund
       * @return balance The balance of the token
       */
      function balanceLogic(IERC20 token, address fundOwner) external view virtual returns (uint balance);
    }
    
  • src/strategies/routers/SmartRouterStorage.sol
    // SPDX-License-Identifier: MIT
    pragma solidity ^0.8.18;
    
    import {IERC20} from "@mgv/lib/IERC20.sol";
    import {AbstractRoutingLogic} from "../routing_logic/abstract/AbstractRoutingLogic.sol";
    
    /// @title Mangrove Smart Router Storage
    /// @notice This library provides a storage layout for the Mangrove Smart Router, utilizing a unique namespace to avoid storage collisions.
    /// @dev The storage layout is defined within a struct and accessed via a constant offset to prevent conflicts with storage in other contracts when used with delegate calls.
    library SmartRouterStorage {
      /// @notice The unique offset for the library's storage layout to prevent storage slot collisions.
      bytes32 private constant OFFSET = keccak256("MangroveStrats.SmartRouterStorage.Layout");
    
      /// @notice Defines the structure of the storage layout used by the Smart Router.
      /// @dev Contains a nested mapping to associate tokens and offer IDs with their respective logic contracts.
      struct Layout {
        mapping(IERC20 token => mapping(bytes32 olKeyHash => mapping(uint offerId => AbstractRoutingLogic logic)))
          routeLogics;
      }
    
      /// @notice Retrieves a reference to the storage layout for the Smart Router.
      /// @return st A reference to the library's storage layout struct.
      function getStorage() internal pure returns (Layout storage st) {
        bytes32 offset = OFFSET;
        assembly ("memory-safe") {
          st.slot := offset
        }
      }
    
      /// @notice Propagates a revert with reason from a failed delegate call.
      /// @param retdata The return data from the delegate call that caused the revert.
      /// @dev This function uses inline assembly to revert with the exact error message from the delegate call.
      function revertWithData(bytes memory retdata) internal pure {
        if (retdata.length == 0) {
          revert("SmartRouter/revertNoReason");
        }
        assembly {
          revert(add(retdata, 32), mload(retdata))
        }
      }
    }
    
  • src/strategies/offer_forwarder/RenegingForwarder.sol
    // SPDX-License-Identifier: MIT
    pragma solidity ^0.8.10;
    
    import {
      MangroveOffer,
      Forwarder,
      IMangrove,
      RouterProxyFactory,
      RouterProxy,
      AbstractRouter,
      MgvLib,
      IERC20,
      OLKey
    } from "./abstract/Forwarder.sol";
    import {Tick} from "@mgv/lib/core/TickLib.sol";
    
    ///@title Forwarder contract with basic functionality
    ///@notice This contract can be used to post and update offers on Mangrove on behalf of a user. Users can also retract their offers and set/update their expiry.
    ///@dev Reneging of offers relies on the `__lastLook__` hook of the `MangroveOffer` contract which reverts when some predicate over timestamp and received order is met.
    contract RenegingForwarder is Forwarder {
      ///@notice Same as Forwarder's constructor
      ///@param mgv the deployed Mangrove contract on which this contract will post offers -- will revert if 0x
      ///@param factory the router proxy factory contract -- will revert if 0x
      ///@param routerImplementation the deployed SmartRouter contract used to generate proxys for offer owners -- will revert if 0x
      constructor(IMangrove mgv, RouterProxyFactory factory, AbstractRouter routerImplementation)
        Forwarder(mgv, factory, routerImplementation)
      {}
    
      ///@notice The expiry and max volume of the offer has been set
      ///@param olKeyHash the hash of the offer list key. It is indexed so RPC call can filter on it.
      ///@param offerId the Mangrove offer id.
      ///@param date in seconds since unix epoch
      ///@param volume the amount of outbound tokens above which the offer should renege on trade.
      ///@notice By emitting this data, an indexer will be able to keep track of the expiry date and the max volume of an offer.
      event SetReneging(bytes32 indexed olKeyHash, uint indexed offerId, uint date, uint volume);
    
      struct Condition {
        uint160 date;
        uint96 volume;
      }
    
      ///@notice `__renegeMap[olKey.hash()][offerId]` gives timestamp beyond which `offerId` on the `olKey.(outbound_tkn, inbound_tkn, tickSpacing)` offer list should renege on trade.
      ///@notice They also give the max volume of tokens upon which the offer should renege on trade.
      ///@notice if the order tx is included after the expiry date, it reverts.
      ///@dev 0 means no expiry and no max volume.
      mapping(bytes32 olKeyHash => mapping(uint offerId => Condition)) private __renegeMap;
    
      ///@notice returns expiry date and max volume of an offer
      ///@param olKeyHash the identifier of the offer list
      ///@param offerId the offer identifier
      ///@return expiry date and max Volume
      function reneging(bytes32 olKeyHash, uint offerId) public view returns (Condition memory) {
        return __renegeMap[olKeyHash][offerId];
      }
    
      ///@notice Updates the expiry date and the max volume for a specific offer if caller is the offer owner.
      ///@param olKeyHash the hash of the offer list key.
      ///@param offerId The offer id whose expiry date and max volume is to be set.
      ///@param expiryDate in seconds since unix epoch. Use 0 for no expiry.
      ///@param volume the amount of outbound tokens above which the offer should renege on trade.
      ///@dev If new date is in the past of the current block's timestamp, offer will renege on trade.
      /// * While updating, the volume should be set to 0. Otherwise, we create a renege for no reason.
      /// * If the user wants to reduce the promised volume, he should update his offer directly.
      /// * If we had to use max volume to avoid reneging with minimum volume, the user could update the offer by himself and set volume back to 0.
      function setReneging(bytes32 olKeyHash, uint offerId, uint expiryDate, uint volume)
        external
        onlyOwner(olKeyHash, offerId)
      {
        _setReneging(olKeyHash, offerId, expiryDate, volume);
      }
    
      ///@notice internal version of the above.
      ///@param olKeyHash the hash of the offer list key.
      ///@param offerId The offer id whose expiry date and max volume is to be set.
      ///@param expiryDate in seconds since unix epoch. Use 0 for no expiry.
      ///@param volume the amount of outbound tokens above which the offer should renege on trade.
      function _setReneging(bytes32 olKeyHash, uint offerId, uint expiryDate, uint volume) internal {
        Condition memory cond;
        cond.date = uint160(expiryDate);
        require(cond.date == expiryDate, "RenegingForwarder/dateOverflow");
        cond.volume = uint96(volume);
        require(cond.volume == volume, "RenegingForwarder/volumeOverflow");
        __renegeMap[olKeyHash][offerId] = cond;
        emit SetReneging(olKeyHash, offerId, expiryDate, volume);
      }
    
      ///@inheritdoc MangroveOffer
      ///@dev making sure offer does not over promise twice when reposting offer residual.
      function __residualValues__(MgvLib.SingleOrder calldata order)
        internal
        virtual
        override
        returns (uint newGives, Tick newTick)
      {
        Condition memory cond = __renegeMap[order.olKey.hash()][order.offerId];
        if (cond.volume == 0) {
          return super.__residualValues__(order);
        } else {
          // new volume to be offered
          // if __residualValues__ is called in __posthookFallback__ the offer reneged and cond.volume could be higher than takerWants
          newGives = order.takerWants < cond.volume ? cond.volume - order.takerWants : 0;
          // same price
          newTick = order.offer.tick();
        }
      }
    
      ///@inheritdoc MangroveOffer
      ///@notice Checks the current timestamps and `order.takerWants` and reneges on trade (by reverting) if the offer has expired or order is over sized.
      function __lastLook__(MgvLib.SingleOrder calldata order) internal virtual override returns (bytes32) {
        bytes32 olKeyHash = order.olKey.hash();
        Condition memory cond = __renegeMap[olKeyHash][order.offerId];
    
        require(cond.date == 0 || block.timestamp < uint(cond.date), "RenegingForwarder/expired");
        require(cond.volume == 0 || order.takerWants <= uint(cond.volume), "RenegingForwarder/overSized");
    
        return super.__lastLook__(order);
      }
    
      ///@notice updates an offer on Mangrove
      ///@dev this can be used to update price of the resting order
      ///@param olKey the offer list key.
      ///@param tick the tick
      ///@param gives new amount of `olKey.outbound_tkn` offer owner gives
      ///@param gasreq new gas req for the restingOrder
      ///@param offerId the id of the offer to be updated
      function updateOffer(OLKey memory olKey, Tick tick, uint gives, uint gasreq, uint offerId)
        external
        payable
        onlyOwner(olKey.hash(), offerId)
      {
        OfferArgs memory args;
    
        // funds to compute new gasprice is msg.value. Will use old gasprice if no funds are given
        args.fund = msg.value;
        args.olKey = olKey;
        args.tick = tick;
        args.gives = gives;
        args.gasreq = gasreq;
        args.noRevert = false; // will throw if Mangrove reverts
        _updateOffer(args, offerId);
      }
    
      ///@inheritdoc MangroveOffer
      function _updateOffer(OfferArgs memory args, uint offerId) internal override returns (bytes32 reason) {
        Condition memory cond = reneging(args.olKey.hash(), offerId);
        if (cond.volume > 0) {
          // resetting reneging volume to 0 when updating offer
          // When reposting on partial fill, residual values are computed based on the reneging volume.
          // So new we can safely set back the max volume to 0.
          _setReneging(args.olKey.hash(), offerId, cond.date, 0);
        }
        return super._updateOffer(args, offerId);
      }
    
      ///@notice Retracts an offer from an Offer List of Mangrove.
      ///@param olKey the offer list key.
      ///@param offerId the identifier of the offer in the offer list
      ///@param deprovision if set to `true` if offer owner wishes to redeem the offer's provision.
      ///@return freeWei the amount of native tokens (in WEI) that have been retrieved by retracting the offer.
      ///@dev An offer that is retracted without `deprovision` is retracted from the offer list, but still has its provisions locked by Mangrove.
      ///@dev Calling this function, with the `deprovision` flag, on an offer that is already retracted must be used to retrieve the locked provisions.
      function retractOffer(OLKey memory olKey, uint offerId, bool deprovision)
        external
        onlyOwner(olKey.hash(), offerId)
        returns (uint freeWei)
      {
        (freeWei,) = _retractOffer(olKey, offerId, false, deprovision);
        if (freeWei > 0) {
          (bool noRevert,) = msg.sender.call{value: freeWei}("");
          require(noRevert, "RenegingForwarder/weiTransferFail");
        }
      }
    }
    
  • src/strategies/routers/SmartRouter.sol
    // SPDX-License-Identifier:	MIT
    pragma solidity ^0.8.18;
    
    import {SmartRouterStorage, AbstractRoutingLogic, IERC20} from "./SmartRouterStorage.sol";
    import {AbstractRouter, SimpleRouter, RL} from "./SimpleRouter.sol";
    
    ///@title Mangrove Smart Router implementation
    ///@notice Router delegates pull and push logic implementation to arbitrary contracts that implement the `AbstractRoutingLogic` interface.
    contract SmartRouter is SimpleRouter {
      /// @notice This value is a forced binding, It will be mainly used to refer to the `MangroveOrder` contract.
      /// * this will allow making a single transaction to deploy, bind and take an offer at the same time with the most common strategy.
      address public immutable forcedBinding;
    
      /// @notice Contract's constructor
      /// @param _forcedBinding the address of the contract that will be forced to be bound to this router.
      constructor(address _forcedBinding) {
        forcedBinding = _forcedBinding;
      }
    
      /// @inheritdoc AbstractRouter
      function isBound(address mkr) public view virtual override returns (bool) {
        return mkr == forcedBinding || super.isBound(mkr);
      }
    
      ///@notice logs new setting of routing logic for a specific offer
      ///@param token the asset whose route is being set
      ///@param olKeyHash the hash of the offer list
      ///@param offerId the offer identifier for which the routing is set
      ///@param logic the contract implementing the pull and push function for that specific route
      event SetRouteLogic(IERC20 indexed token, bytes32 indexed olKeyHash, uint offerId, AbstractRoutingLogic logic);
    
      ///@notice This function is used to set the routing strategy for a given offer.
      ///@param routingOrder the routing order template for which a logic should be used
      ///@param logic the logic to use for the given offer.xw
      ///@dev `routingOrder.reserveId` is ignored as there is a unique mapping in storage per `reserveId`
      function setLogic(RL.RoutingOrder calldata routingOrder, AbstractRoutingLogic logic) external boundOrAdmin {
        SmartRouterStorage.getStorage().routeLogics[routingOrder.token][routingOrder.olKeyHash][routingOrder.offerId] =
          logic;
        emit SetRouteLogic(routingOrder.token, routingOrder.olKeyHash, routingOrder.offerId, logic);
      }
    
      ///@notice This function is used to get the routing strategy for a given offer.
      ///@param routingOrder the routing order template for which a logic should be used
      ///@return logic the logic used for the given routing order.
      function getLogic(RL.RoutingOrder calldata routingOrder) external view returns (AbstractRoutingLogic) {
        return SmartRouterStorage.getStorage().routeLogics[routingOrder.token][routingOrder.olKeyHash][routingOrder.offerId];
      }
    
      /// @inheritdoc AbstractRouter
      /// @dev pulls liquidity using the logic specified by offer owner
      function __pull__(RL.RoutingOrder memory routingOrder, uint amount, bool strict)
        internal
        virtual
        override
        returns (uint)
      {
        AbstractRoutingLogic logic =
          SmartRouterStorage.getStorage().routeLogics[routingOrder.token][routingOrder.olKeyHash][routingOrder.offerId];
        if (address(logic) != address(0)) {
          // delegating routing to the specific chosen route
          (bool success, bytes memory retdata) = address(logic).delegatecall(
            abi.encodeWithSelector(
              AbstractRoutingLogic.pullLogic.selector, routingOrder.token, routingOrder.fundOwner, amount, strict
            )
          );
          if (!success) {
            SmartRouterStorage.revertWithData(retdata);
          }
          return (abi.decode(retdata, (uint)));
        } else {
          // default route is to pull funds directly from `routingOrder.fundOwner`
          return super.__pull__(routingOrder, amount, strict);
        }
      }
    
      /// @inheritdoc AbstractRouter
      /// @dev pushes liquidity using the logic specified by offer owner
      function __push__(RL.RoutingOrder memory routingOrder, uint amount) internal virtual override returns (uint) {
        AbstractRoutingLogic logic =
          SmartRouterStorage.getStorage().routeLogics[routingOrder.token][routingOrder.olKeyHash][routingOrder.offerId];
        if (address(logic) != address(0)) {
          (bool success, bytes memory retdata) = address(logic).delegatecall(
            abi.encodeWithSelector(
              AbstractRoutingLogic.pushLogic.selector, routingOrder.token, routingOrder.fundOwner, amount
            )
          );
          if (!success) {
            SmartRouterStorage.revertWithData(retdata);
          }
          return (abi.decode(retdata, (uint)));
        } else {
          return super.__push__(routingOrder, amount);
        }
      }
    
      ///@inheritdoc AbstractRouter
      function tokenBalanceOf(RL.RoutingOrder calldata routingOrder) public view override returns (uint) {
        AbstractRoutingLogic customLogic =
          SmartRouterStorage.getStorage().routeLogics[routingOrder.token][routingOrder.olKeyHash][routingOrder.offerId];
        if (address(customLogic) != address(0)) {
          return customLogic.balanceLogic(routingOrder.token, routingOrder.fundOwner);
        } else {
          return super.tokenBalanceOf(routingOrder);
        }
      }
    }
    
  • src/strategies/routers/RouterProxy.sol
    // SPDX-License-Identifier:	MIT
    pragma solidity ^0.8.18;
    
    import {AbstractRouter} from "./abstract/AbstractRouter.sol";
    
    /// @title Minimalistic Mangrove Router Proxy
    /// @notice A proxy contract that delegates calls to an instance of an AbstractRouter contract.
    ///         It does not allow updates of implementation contract.
    /// @dev it assumes the IMPLEMENTATION uses first storage slot to hold the admin address (this is guaranteed as long as `AbstractRouter` is `AccessControlled`)
    contract RouterProxy {
      /// @notice The address of the deployed SmartRouter contract acting as the delegate implementation.
      /// @dev The SmartRouter instance must be AccessControlled to ensure the storage layout is matched.
      AbstractRouter public immutable IMPLEMENTATION;
    
      /// @notice Emitted when the admin of the proxy is set.
      /// * This is a copy of the AccessControlled event to set without calling the implementation.
      /// @param admin The address of the admin of the proxy.
      event SetAdmin(address admin);
    
      /// @notice Deploys a Proxy for the SmartRouter that handles incoming transactions and delegates them to the implementation.
      /// @param implementation The address of the deployed SmartRouter contract to which calls will be delegated.
      /// @dev Initializes the contract with an AccessControlled base to set up access control.
      constructor(AbstractRouter implementation) {
        IMPLEMENTATION = implementation;
        // store the msg sender at address 0 (_admin storage slot on access controlled)
        assembly {
          sstore(0, caller())
        }
        emit SetAdmin(msg.sender);
      }
    
      /// @notice Fallback function to delegate calls to the implementation contract.
      fallback() external {
        AbstractRouter implementation = IMPLEMENTATION;
        assembly {
          // Copy msg.data. We take full control of memory in this inline assembly
          // block because it will not return to Solidity code. We overwrite the
          // Solidity scratch pad at memory position 0.
          calldatacopy(0, 0, calldatasize())
    
          // Call the implementation.
          // out and outsize are 0 because we don't know the size yet.
          let result := delegatecall(gas(), implementation, 0, calldatasize(), 0, 0)
    
          // Copy the returned data.
          returndatacopy(0, 0, returndatasize())
    
          switch result
          // delegatecall returns 0 on error.
          case 0 { revert(0, returndatasize()) }
          default { return(0, returndatasize()) }
        }
      }
    }
    
ABI · 26 entries
json
[
  {
    "type": "constructor",
    "inputs": [
      {
        "name": "mgv",
        "type": "address",
        "internalType": "contract IMangrove"
      },
      {
        "name": "factory",
        "type": "address",
        "internalType": "contract RouterProxyFactory"
      },
      {
        "name": "deployer",
        "type": "address",
        "internalType": "address"
      }
    ],
    "stateMutability": "nonpayable"
  },
  {
    "name": "LogIncident",
    "type": "event",
    "inputs": [
      {
        "name": "olKeyHash",
        "type": "bytes32",
        "indexed": true,
        "internalType": "bytes32"
      },
      {
        "name": "offerId",
        "type": "uint256",
        "indexed": true,
        "internalType": "uint256"
      },
      {
        "name": "makerData",
        "type": "bytes32",
        "indexed": false,
        "internalType": "bytes32"
      },
      {
        "name": "mgvData",
        "type": "bytes32",
        "indexed": false,
        "internalType": "bytes32"
      }
    ],
    "anonymous": false
  },
  {
    "name": "MangroveOrderComplete",
    "type": "event",
    "inputs": [],
    "anonymous": false
  },
  {
    "name": "MangroveOrderStart",
    "type": "event",
    "inputs": [
      {
        "name": "olKeyHash",
        "type": "bytes32",
        "indexed": true,
        "internalType": "bytes32"
      },
      {
        "name": "taker",
        "type": "address",
        "indexed": true,
        "internalType": "address"
      },
      {
        "name": "tick",
        "type": "int256",
        "indexed": false,
        "internalType": "Tick"
      },
      {
        "name": "orderType",
        "type": "uint8",
        "indexed": false,
        "internalType": "enum TakerOrderType"
      },
      {
        "name": "fillVolume",
        "type": "uint256",
        "indexed": false,
        "internalType": "uint256"
      },
      {
        "name": "fillWants",
        "type": "bool",
        "indexed": false,
        "internalType": "bool"
      },
      {
        "name": "offerId",
        "type": "uint256",
        "indexed": false,
        "internalType": "uint256"
      },
      {
        "name": "takerGivesLogic",
        "type": "address",
        "indexed": false,
        "internalType": "contract AbstractRoutingLogic"
      },
      {
        "name": "takerWantsLogic",
        "type": "address",
        "indexed": false,
        "internalType": "contract AbstractRoutingLogic"
      }
    ],
    "anonymous": false
  },
  {
    "name": "NewOwnedOffer",
    "type": "event",
    "inputs": [
      {
        "name": "olKeyHash",
        "type": "bytes32",
        "indexed": true,
        "internalType": "bytes32"
      },
      {
        "name": "offerId",
        "type": "uint256",
        "indexed": true,
        "internalType": "uint256"
      },
      {
        "name": "owner",
        "type": "address",
        "indexed": true,
        "internalType": "address"
      }
    ],
    "anonymous": false
  },
  {
    "name": "SetAdmin",
    "type": "event",
    "inputs": [
      {
        "name": "admin",
        "type": "address",
        "indexed": false,
        "internalType": "address"
      }
    ],
    "anonymous": false
  },
  {
    "name": "SetReneging",
    "type": "event",
    "inputs": [
      {
        "name": "olKeyHash",
        "type": "bytes32",
        "indexed": true,
        "internalType": "bytes32"
      },
      {
        "name": "offerId",
        "type": "uint256",
        "indexed": true,
        "internalType": "uint256"
      },
      {
        "name": "date",
        "type": "uint256",
        "indexed": false,
        "internalType": "uint256"
      },
      {
        "name": "volume",
        "type": "uint256",
        "indexed": false,
        "internalType": "uint256"
      }
    ],
    "anonymous": false
  },
  {
    "name": "MGV",
    "type": "function",
    "inputs": [],
    "outputs": [
      {
        "name": "",
        "type": "address",
        "internalType": "contract IMangrove"
      }
    ],
    "stateMutability": "view"
  },
  {
    "name": "ROUTER_FACTORY",
    "type": "function",
    "inputs": [],
    "outputs": [
      {
        "name": "",
        "type": "address",
        "internalType": "contract RouterProxyFactory"
      }
    ],
    "stateMutability": "view"
  },
  {
    "name": "ROUTER_IMPLEMENTATION",
    "type": "function",
    "inputs": [],
    "outputs": [
      {
        "name": "",
        "type": "address",
        "internalType": "contract AbstractRouter"
      }
    ],
    "stateMutability": "view"
  },
  {
    "name": "activate",
    "type": "function",
    "inputs": [
      {
        "name": "token",
        "type": "address",
        "internalType": "contract IERC20"
      }
    ],
    "outputs": [],
    "stateMutability": "nonpayable"
  },
  {
    "name": "admin",
    "type": "function",
    "inputs": [],
    "outputs": [
      {
        "name": "current",
        "type": "address",
        "internalType": "address"
      }
    ],
    "stateMutability": "view"
  },
  {
    "name": "approve",
    "type": "function",
    "inputs": [
      {
        "name": "token",
        "type": "address",
        "internalType": "contract IERC20"
      },
      {
        "name": "spender",
        "type": "address",
        "internalType": "address"
      },
      {
        "name": "amount",
        "type": "uint256",
        "internalType": "uint256"
      }
    ],
    "outputs": [
      {
        "name": "",
        "type": "bool",
        "internalType": "bool"
      }
    ],
    "stateMutability": "nonpayable"
  },
  {
    "name": "makerExecute",
    "type": "function",
    "inputs": [
      {
        "name": "order",
        "type": "tuple",
        "components": [
          {
            "name": "olKey",
            "type": "tuple",
            "components": [
              {
                "name": "outbound_tkn",
                "type": "address",
                "internalType": "address"
              },
              {
                "name": "inbound_tkn",
                "type": "address",
                "internalType": "address"
              },
              {
                "name": "tickSpacing",
                "type": "uint256",
                "internalType": "uint256"
              }
            ],
            "internalType": "struct OLKey"
          },
          {
            "name": "offerId",
            "type": "uint256",
            "internalType": "uint256"
          },
          {
            "name": "offer",
            "type": "uint256",
            "internalType": "Offer"
          },
          {
            "name": "takerWants",
            "type": "uint256",
            "internalType": "uint256"
          },
          {
            "name": "takerGives",
            "type": "uint256",
            "internalType": "uint256"
          },
          {
            "name": "offerDetail",
            "type": "uint256",
            "internalType": "OfferDetail"
          },
          {
            "name": "global",
            "type": "uint256",
            "internalType": "Global"
          },
          {
            "name": "local",
            "type": "uint256",
            "internalType": "Local"
          }
        ],
        "internalType": "struct MgvLib.SingleOrder"
      }
    ],
    "outputs": [
      {
        "name": "ret",
        "type": "bytes32",
        "internalType": "bytes32"
      }
    ],
    "stateMutability": "nonpayable"
  },
  {
    "name": "makerPosthook",
    "type": "function",
    "inputs": [
      {
        "name": "order",
        "type": "tuple",
        "components": [
          {
            "name": "olKey",
            "type": "tuple",
            "components": [
              {
                "name": "outbound_tkn",
                "type": "address",
                "internalType": "address"
              },
              {
                "name": "inbound_tkn",
                "type": "address",
                "internalType": "address"
              },
              {
                "name": "tickSpacing",
                "type": "uint256",
                "internalType": "uint256"
              }
            ],
            "internalType": "struct OLKey"
          },
          {
            "name": "offerId",
            "type": "uint256",
            "internalType": "uint256"
          },
          {
            "name": "offer",
            "type": "uint256",
            "internalType": "Offer"
          },
          {
            "name": "takerWants",
            "type": "uint256",
            "internalType": "uint256"
          },
          {
            "name": "takerGives",
            "type": "uint256",
            "internalType": "uint256"
          },
          {
            "name": "offerDetail",
            "type": "uint256",
            "internalType": "OfferDetail"
          },
          {
            "name": "global",
            "type": "uint256",
            "internalType": "Global"
          },
          {
            "name": "local",
            "type": "uint256",
            "internalType": "Local"
          }
        ],
        "internalType": "struct MgvLib.SingleOrder"
      },
      {
        "name": "result",
        "type": "tuple",
        "components": [
          {
            "name": "makerData",
            "type": "bytes32",
            "internalType": "bytes32"
          },
          {
            "name": "mgvData",
            "type": "bytes32",
            "internalType": "bytes32"
          }
        ],
        "internalType": "struct MgvLib.OrderResult"
      }
    ],
    "outputs": [],
    "stateMutability": "nonpayable"
  },
  {
    "name": "ownerOf",
    "type": "function",
    "inputs": [
      {
        "name": "olKeyHash",
        "type": "bytes32",
        "internalType": "bytes32"
      },
      {
        "name": "offerId",
        "type": "uint256",
        "internalType": "uint256"
      }
    ],
    "outputs": [
      {
        "name": "owner",
        "type": "address",
        "internalType": "address"
      }
    ],
    "stateMutability": "view"
  },
  {
    "name": "provisionOf",
    "type": "function",
    "inputs": [
      {
        "name": "olKey",
        "type": "tuple",
        "components": [
          {
            "name": "outbound_tkn",
            "type": "address",
            "internalType": "address"
          },
          {
            "name": "inbound_tkn",
            "type": "address",
            "internalType": "address"
          },
          {
            "name": "tickSpacing",
            "type": "uint256",
            "internalType": "uint256"
          }
        ],
        "internalType": "struct OLKey"
      },
      {
        "name": "offerId",
        "type": "uint256",
        "internalType": "uint256"
      }
    ],
    "outputs": [
      {
        "name": "provision",
        "type": "uint256",
        "internalType": "uint256"
      }
    ],
    "stateMutability": "view"
  },
  {
    "name": "reneging",
    "type": "function",
    "inputs": [
      {
        "name": "olKeyHash",
        "type": "bytes32",
        "internalType": "bytes32"
      },
      {
        "name": "offerId",
        "type": "uint256",
        "internalType": "uint256"
      }
    ],
    "outputs": [
      {
        "name": "",
        "type": "tuple",
        "components": [
          {
            "name": "date",
            "type": "uint160",
            "internalType": "uint160"
          },
          {
            "name": "volume",
            "type": "uint96",
            "internalType": "uint96"
          }
        ],
        "internalType": "struct RenegingForwarder.Condition"
      }
    ],
    "stateMutability": "view"
  },
  {
    "name": "retractOffer",
    "type": "function",
    "inputs": [
      {
        "name": "olKey",
        "type": "tuple",
        "components": [
          {
            "name": "outbound_tkn",
            "type": "address",
            "internalType": "address"
          },
          {
            "name": "inbound_tkn",
            "type": "address",
            "internalType": "address"
          },
          {
            "name": "tickSpacing",
            "type": "uint256",
            "internalType": "uint256"
          }
        ],
        "internalType": "struct OLKey"
      },
      {
        "name": "offerId",
        "type": "uint256",
        "internalType": "uint256"
      },
      {
        "name": "deprovision",
        "type": "bool",
        "internalType": "bool"
      }
    ],
    "outputs": [
      {
        "name": "freeWei",
        "type": "uint256",
        "internalType": "uint256"
      }
    ],
    "stateMutability": "nonpayable"
  },
  {
    "name": "router",
    "type": "function",
    "inputs": [
      {
        "name": "fundOwner",
        "type": "address",
        "internalType": "address"
      }
    ],
    "outputs": [
      {
        "name": "",
        "type": "address",
        "internalType": "contract AbstractRouter"
      }
    ],
    "stateMutability": "view"
  },
  {
    "name": "setAdmin",
    "type": "function",
    "inputs": [
      {
        "name": "admin_",
        "type": "address",
        "internalType": "address"
      }
    ],
    "outputs": [],
    "stateMutability": "nonpayable"
  },
  {
    "name": "setReneging",
    "type": "function",
    "inputs": [
      {
        "name": "olKeyHash",
        "type": "bytes32",
        "internalType": "bytes32"
      },
      {
        "name": "offerId",
        "type": "uint256",
        "internalType": "uint256"
      },
      {
        "name": "expiryDate",
        "type": "uint256",
        "internalType": "uint256"
      },
      {
        "name": "volume",
        "type": "uint256",
        "internalType": "uint256"
      }
    ],
    "outputs": [],
    "stateMutability": "nonpayable"
  },
  {
    "name": "take",
    "type": "function",
    "inputs": [
      {
        "name": "tko",
        "type": "tuple",
        "components": [
          {
            "name": "olKey",
            "type": "tuple",
            "components": [
              {
                "name": "outbound_tkn",
                "type": "address",
                "internalType": "address"
              },
              {
                "name": "inbound_tkn",
                "type": "address",
                "internalType": "address"
              },
              {
                "name": "tickSpacing",
                "type": "uint256",
                "internalType": "uint256"
              }
            ],
            "internalType": "struct OLKey"
          },
          {
            "name": "tick",
            "type": "int256",
            "internalType": "Tick"
          },
          {
            "name": "orderType",
            "type": "uint8",
            "internalType": "enum TakerOrderType"
          },
          {
            "name": "fillVolume",
            "type": "uint256",
            "internalType": "uint256"
          },
          {
            "name": "fillWants",
            "type": "bool",
            "internalType": "bool"
          },
          {
            "name": "expiryDate",
            "type": "uint256",
            "internalType": "uint256"
          },
          {
            "name": "offerId",
            "type": "uint256",
            "internalType": "uint256"
          },
          {
            "name": "restingOrderGasreq",
            "type": "uint256",
            "internalType": "uint256"
          },
          {
            "name": "takerGivesLogic",
            "type": "address",
            "internalType": "contract AbstractRoutingLogic"
          },
          {
            "name": "takerWantsLogic",
            "type": "address",
            "internalType": "contract AbstractRoutingLogic"
          }
        ],
        "internalType": "struct IOrderLogic.TakerOrder"
      }
    ],
    "outputs": [
      {
        "name": "res",
        "type": "tuple",
        "components": [
          {
            "name": "takerGot",
            "type": "uint256",
            "internalType": "uint256"
          },
          {
            "name": "takerGave",
            "type": "uint256",
            "internalType": "uint256"
          },
          {
            "name": "bounty",
            "type": "uint256",
            "internalType": "uint256"
          },
          {
            "name": "fee",
            "type": "uint256",
            "internalType": "uint256"
          },
          {
            "name": "offerId",
            "type": "uint256",
            "internalType": "uint256"
          },
          {
            "name": "offerWriteData",
            "type": "bytes32",
            "internalType": "bytes32"
          }
        ],
        "internalType": "struct IOrderLogic.TakerOrderResult"
      }
    ],
    "stateMutability": "payable"
  },
  {
    "name": "updateOffer",
    "type": "function",
    "inputs": [
      {
        "name": "olKey",
        "type": "tuple",
        "components": [
          {
            "name": "outbound_tkn",
            "type": "address",
            "internalType": "address"
          },
          {
            "name": "inbound_tkn",
            "type": "address",
            "internalType": "address"
          },
          {
            "name": "tickSpacing",
            "type": "uint256",
            "internalType": "uint256"
          }
        ],
        "internalType": "struct OLKey"
      },
      {
        "name": "tick",
        "type": "int256",
        "internalType": "Tick"
      },
      {
        "name": "gives",
        "type": "uint256",
        "internalType": "uint256"
      },
      {
        "name": "gasreq",
        "type": "uint256",
        "internalType": "uint256"
      },
      {
        "name": "offerId",
        "type": "uint256",
        "internalType": "uint256"
      }
    ],
    "outputs": [],
    "stateMutability": "payable"
  },
  {
    "name": "withdrawFromMangrove",
    "type": "function",
    "inputs": [
      {
        "name": "amount",
        "type": "uint256",
        "internalType": "uint256"
      },
      {
        "name": "receiver",
        "type": "address",
        "internalType": "address payable"
      }
    ],
    "outputs": [],
    "stateMutability": "nonpayable"
  },
  {
    "type": "receive",
    "stateMutability": "payable"
  }
]