Contract
0xf3e339d8a0b989114412fa157cc846ebaf4bcbd8
1
Contract Overview
Balance:
25.880614563 MATIC
My Name Tag:
Not Available
[ Download CSV Export ]
Latest 25 internal transaction
[ Download CSV Export ]
Contract Name:
Mangrove
Compiler Version
v0.8.10+commit.fc410830
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: AGPL-3.0 // AbstractMangrove.sol // Copyright (C) 2021 Giry SAS. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU Affero General Public License as published // by the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU Affero General Public License for more details. // // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see <https://www.gnu.org/licenses/>. pragma solidity ^0.8.10; pragma abicoder v2; import {MgvLib as ML} from "./MgvLib.sol"; import {MgvOfferMaking} from "./MgvOfferMaking.sol"; import {MgvOfferTakingWithPermit} from "./MgvOfferTakingWithPermit.sol"; import {MgvGovernable} from "./MgvGovernable.sol"; /* `AbstractMangrove` inherits the three contracts that implement generic Mangrove functionality (`MgvGovernable`,`MgvOfferTakingWithPermit` and `MgvOfferMaking`) but does not implement the abstract functions. */ abstract contract AbstractMangrove is MgvGovernable, MgvOfferTakingWithPermit, MgvOfferMaking { constructor( address governance, uint gasprice, uint gasmax, string memory contractName ) MgvOfferTakingWithPermit(contractName) MgvGovernable(governance, gasprice, gasmax) {} }
// SPDX-License-Identifier: Unlicense // IERC20.sol // This is free and unencumbered software released into the public domain. // Anyone is free to copy, modify, publish, use, compile, sell, or distribute this software, either in source code form or as a compiled binary, for any purpose, commercial or non-commercial, and by any means. // In jurisdictions that recognize copyright laws, the author or authors of this software dedicate any and all copyright interest in the software to the public domain. We make this dedication for the benefit of the public at large and to the detriment of our heirs and successors. We intend this dedication to be an overt act of relinquishment in perpetuity of all present and future rights to this software under copyright law. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // For more information, please refer to <https://unlicense.org/> /* `MgvLib` contains data structures returned by external calls to Mangrove and the interfaces it uses for its own external calls. */ pragma solidity ^0.8.10; pragma abicoder v2; interface IERC20 { function totalSupply() external view returns (uint); function balanceOf(address account) external view returns (uint); function transfer(address recipient, uint amount) external returns (bool); function allowance(address owner, address spender) external view returns (uint); function approve(address spender, uint amount) external returns (bool); function transferFrom( address sender, address recipient, uint amount ) external returns (bool); function symbol() external view returns (string memory); event Transfer(address indexed from, address indexed to, uint value); event Approval(address indexed owner, address indexed spender, uint value); /// for wETH contract function deposit() external payable; function withdraw(uint) external; function decimals() external view returns (uint8); }
// SPDX-License-Identifier: AGPL-3.0 // Mangrove.sol // Copyright (C) 2021 Giry SAS. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU Affero General Public License as published // by the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU Affero General Public License for more details. // // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see <https://www.gnu.org/licenses/>. pragma solidity ^0.8.10; pragma abicoder v2; import {MgvLib as ML, P} from "./MgvLib.sol"; import {AbstractMangrove} from "./AbstractMangrove.sol"; /* <a id="Mangrove"></a> The `Mangrove` contract implements the "normal" version of Mangrove, where the taker flashloans the desired amount to each maker. Each time, makers are called after the loan. When the order is complete, each maker is called once again (with the orderbook unlocked). */ contract Mangrove is AbstractMangrove { using P.OfferDetail for P.OfferDetail.t; constructor( address governance, uint gasprice, uint gasmax ) AbstractMangrove(governance, gasprice, gasmax, "Mangrove") {} function executeEnd(MultiOrder memory mor, ML.SingleOrder memory sor) internal override {} function beforePosthook(ML.SingleOrder memory sor) internal override {} /* ## Flashloan */ /* `flashloan` is for the 'normal' mode of operation. It: 1. Flashloans `takerGives` `inbound_tkn` from the taker to the maker and returns false if the loan fails. 2. Runs `offerDetail.maker`'s `execute` function. 3. Returns the result of the operations, with optional makerData to help the maker debug. */ function flashloan(ML.SingleOrder calldata sor, address taker) external override returns (uint gasused) { unchecked { /* `flashloan` must be used with a call (hence the `external` modifier) so its effect can be reverted. But a call from the outside would be fatal. */ require(msg.sender == address(this), "mgv/flashloan/protected"); /* The transfer taker -> maker is in 2 steps. First, taker->mgv. Then mgv->maker. With a direct taker->maker transfer, if one of taker/maker is blacklisted, we can't tell which one. We need to know which one: if we incorrectly blame the taker, a blacklisted maker can block a pair forever; if we incorrectly blame the maker, a blacklisted taker can unfairly make makers fail all the time. Of course we assume the Mangrove is not blacklisted. Also note that this setup doesn't not work well with tokens that take fees or recompute balances at transfer time. */ if (transferTokenFrom(sor.inbound_tkn, taker, address(this), sor.gives)) { if ( transferToken(sor.inbound_tkn, sor.offerDetail.maker(), sor.gives) ) { gasused = makerExecute(sor); } else { innerRevert([bytes32("mgv/makerReceiveFail"), bytes32(0), ""]); } } else { innerRevert([bytes32("mgv/takerTransferFail"), "", ""]); } } } }
// SPDX-License-Identifier: AGPL-3.0 // MgvGovernable.sol // Copyright (C) 2021 Giry SAS. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU Affero General Public License as published // by the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU Affero General Public License for more details. // // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see <https://www.gnu.org/licenses/>. pragma solidity ^0.8.10; pragma abicoder v2; import {HasMgvEvents, P} from "./MgvLib.sol"; import {MgvRoot} from "./MgvRoot.sol"; contract MgvGovernable is MgvRoot { // using P.Offer for P.Offer.t; // using P.OfferDetail for P.OfferDetail.t; using P.Global for P.Global.t; using P.Local for P.Local.t; /* The `governance` address. Governance is the only address that can configure parameters. */ address public governance; constructor( address _governance, uint _gasprice, uint gasmax ) MgvRoot() { unchecked { emit NewMgv(); /* Initially, governance is open to anyone. */ /* Initialize vault to governance address, and set initial gasprice and gasmax. */ setVault(_governance); setGasprice(_gasprice); setGasmax(gasmax); /* Initialize governance to `_governance` after parameter setting. */ setGovernance(_governance); } } /* ## `authOnly` check */ function authOnly() internal view { unchecked { require( msg.sender == governance || msg.sender == address(this) || governance == address(0), "mgv/unauthorized" ); } } /* # Set configuration and Mangrove state */ /* ## Locals */ /* ### `active` */ function activate( address outbound_tkn, address inbound_tkn, uint fee, uint density, uint offer_gasbase ) public { unchecked { authOnly(); locals[outbound_tkn][inbound_tkn] = locals[outbound_tkn][inbound_tkn] .active(true); emit SetActive(outbound_tkn, inbound_tkn, true); setFee(outbound_tkn, inbound_tkn, fee); setDensity(outbound_tkn, inbound_tkn, density); setGasbase(outbound_tkn, inbound_tkn, offer_gasbase); } } function deactivate(address outbound_tkn, address inbound_tkn) public { authOnly(); locals[outbound_tkn][inbound_tkn] = locals[outbound_tkn][inbound_tkn] .active(false); emit SetActive(outbound_tkn, inbound_tkn, false); } /* ### `fee` */ function setFee( address outbound_tkn, address inbound_tkn, uint fee ) public { unchecked { authOnly(); /* `fee` is in basis points, i.e. in percents of a percent. */ require(fee <= 500, "mgv/config/fee/<=500"); // at most 5% locals[outbound_tkn][inbound_tkn] = locals[outbound_tkn][inbound_tkn].fee( fee ); emit SetFee(outbound_tkn, inbound_tkn, fee); } } /* ### `density` */ /* Useless if `global.useOracle != 0` */ function setDensity( address outbound_tkn, address inbound_tkn, uint density ) public { unchecked { authOnly(); require(checkDensity(density), "mgv/config/density/112bits"); //+clear+ locals[outbound_tkn][inbound_tkn] = locals[outbound_tkn][inbound_tkn] .density(density); emit SetDensity(outbound_tkn, inbound_tkn, density); } } /* ### `gasbase` */ function setGasbase( address outbound_tkn, address inbound_tkn, uint offer_gasbase ) public { unchecked { authOnly(); /* Checking the size of `offer_gasbase` is necessary to prevent a) data loss when copied to an `OfferDetail` struct, and b) overflow when used in calculations. */ require( uint24(offer_gasbase) == offer_gasbase, "mgv/config/offer_gasbase/24bits" ); //+clear+ locals[outbound_tkn][inbound_tkn] = locals[outbound_tkn][inbound_tkn] .offer_gasbase(offer_gasbase); emit SetGasbase(outbound_tkn, inbound_tkn, offer_gasbase); } } /* ## Globals */ /* ### `kill` */ function kill() public { unchecked { authOnly(); internal_global = internal_global.dead(true); emit Kill(); } } /* ### `gasprice` */ /* Useless if `global.useOracle is != 0` */ function setGasprice(uint gasprice) public { unchecked { authOnly(); require(checkGasprice(gasprice), "mgv/config/gasprice/16bits"); //+clear+ internal_global = internal_global.gasprice(gasprice); emit SetGasprice(gasprice); } } /* ### `gasmax` */ function setGasmax(uint gasmax) public { unchecked { authOnly(); /* Since any new `gasreq` is bounded above by `config.gasmax`, this check implies that all offers' `gasreq` is 24 bits wide at most. */ require(uint24(gasmax) == gasmax, "mgv/config/gasmax/24bits"); //+clear+ internal_global = internal_global.gasmax(gasmax); emit SetGasmax(gasmax); } } /* ### `governance` */ function setGovernance(address governanceAddress) public { unchecked { authOnly(); require(governanceAddress != address(0), "mgv/config/gov/not0"); governance = governanceAddress; emit SetGovernance(governanceAddress); } } /* ### `vault` */ function setVault(address vaultAddress) public { unchecked { authOnly(); vault = vaultAddress; emit SetVault(vaultAddress); } } /* ### `monitor` */ function setMonitor(address monitor) public { unchecked { authOnly(); internal_global = internal_global.monitor(monitor); emit SetMonitor(monitor); } } /* ### `useOracle` */ function setUseOracle(bool useOracle) public { unchecked { authOnly(); internal_global = internal_global.useOracle(useOracle); emit SetUseOracle(useOracle); } } /* ### `notify` */ function setNotify(bool notify) public { unchecked { authOnly(); internal_global = internal_global.notify(notify); emit SetNotify(notify); } } }
// SPDX-License-Identifier: AGPL-3.0 // MgvHasOffers.sol // Copyright (C) 2021 Giry SAS. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU Affero General Public License as published // by the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU Affero General Public License for more details. // // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see <https://www.gnu.org/licenses/>. pragma solidity ^0.8.10; pragma abicoder v2; import {MgvLib as ML, HasMgvEvents, IMgvMonitor, P} from "./MgvLib.sol"; import {MgvRoot} from "./MgvRoot.sol"; /* `MgvHasOffers` contains the state variables and functions common to both market-maker operations and market-taker operations. Mostly: storing offers, removing them, updating market makers' provisions. */ contract MgvHasOffers is MgvRoot { using P.Offer for P.Offer.t; using P.OfferDetail for P.OfferDetail.t; using P.Local for P.Local.t; /* # State variables */ /* Given a `outbound_tkn`,`inbound_tkn` pair, the mappings `offers` and `offerDetails` associate two 256 bits words to each offer id. Those words encode information detailed in [`structs.js`](#structs.js). The mappings are `outbound_tkn => inbound_tkn => offerId => P.Offer.t|P.OfferDetail.t`. */ mapping(address => mapping(address => mapping(uint => P.Offer.t))) public offers; mapping(address => mapping(address => mapping(uint => P.OfferDetail.t))) public offerDetails; /* Makers provision their possible penalties in the `balanceOf` mapping. Offers specify the amount of gas they require for successful execution ([`gasreq`](#structs.js/gasreq)). To minimize book spamming, market makers must provision a *penalty*, which depends on their `gasreq` and on the pair's [`offer_gasbase`](#structs.js/gasbase). This provision is deducted from their `balanceOf`. If an offer fails, part of that provision is given to the taker, as retribution. The exact amount depends on the gas used by the offer before failing. The Mangrove keeps track of their available balance in the `balanceOf` map, which is decremented every time a maker creates a new offer, and may be modified on offer updates/cancelations/takings. */ mapping(address => uint) public balanceOf; /* # Read functions */ /* Convenience function to get best offer of the given pair */ function best(address outbound_tkn, address inbound_tkn) external view returns (uint) { unchecked { P.Local.t local = locals[outbound_tkn][inbound_tkn]; return local.best(); } } /* Returns information about an offer in ABI-compatible structs. Do not use internally, would be a huge memory-copying waste. Use `offers[outbound_tkn][inbound_tkn]` and `offerDetails[outbound_tkn][inbound_tkn]` instead. */ function offerInfo( address outbound_tkn, address inbound_tkn, uint offerId ) external view returns (P.OfferStruct memory offer, P.OfferDetailStruct memory offerDetail) { unchecked { P.Offer.t _offer = offers[outbound_tkn][inbound_tkn][offerId]; offer = _offer.to_struct(); P.OfferDetail.t _offerDetail = offerDetails[outbound_tkn][inbound_tkn][ offerId ]; offerDetail = _offerDetail.to_struct(); } } /* # Provision debit/credit utility functions */ /* `balanceOf` is in wei of ETH. */ function debitWei(address maker, uint amount) internal { unchecked { uint makerBalance = balanceOf[maker]; require(makerBalance >= amount, "mgv/insufficientProvision"); balanceOf[maker] = makerBalance - amount; emit Debit(maker, amount); } } function creditWei(address maker, uint amount) internal { unchecked { balanceOf[maker] += amount; emit Credit(maker, amount); } } /* # Misc. low-level functions */ /* ## Offer deletion */ /* When an offer is deleted, it is marked as such by setting `gives` to 0. Note that provision accounting in the Mangrove aims to minimize writes. Each maker `fund`s the Mangrove to increase its balance. When an offer is created/updated, we compute how much should be reserved to pay for possible penalties. That amount can always be recomputed with `offerDetail.gasprice * (offerDetail.gasreq + offerDetail.offer_gasbase)`. The balance is updated to reflect the remaining available ethers. Now, when an offer is deleted, the offer can stay provisioned, or be `deprovision`ed. In the latter case, we set `gasprice` to 0, which induces a provision of 0. All code calling `dirtyDeleteOffer` with `deprovision` set to `true` must be careful to correctly account for where that provision is going (back to the maker's `balanceOf`, or sent to a taker as compensation). */ function dirtyDeleteOffer( address outbound_tkn, address inbound_tkn, uint offerId, P.Offer.t offer, P.OfferDetail.t offerDetail, bool deprovision ) internal { unchecked { offer = offer.gives(0); if (deprovision) { offerDetail = offerDetail.gasprice(0); } offers[outbound_tkn][inbound_tkn][offerId] = offer; offerDetails[outbound_tkn][inbound_tkn][offerId] = offerDetail; } } /* ## Stitching the orderbook */ /* Connect the offers `betterId` and `worseId` through their `next`/`prev` pointers. For more on the book structure, see [`structs.js`](#structs.js). Used after executing an offer (or a segment of offers), after removing an offer, or moving an offer. **Warning**: calling with `betterId = 0` will set `worseId` as the best. So with `betterId = 0` and `worseId = 0`, it sets the book to empty and loses track of existing offers. **Warning**: may make memory copy of `local.best` stale. Returns new `local`. */ function stitchOffers( address outbound_tkn, address inbound_tkn, uint betterId, uint worseId, P.Local.t local ) internal returns (P.Local.t) { unchecked { if (betterId != 0) { offers[outbound_tkn][inbound_tkn][betterId] = offers[outbound_tkn][ inbound_tkn ][betterId].next(worseId); } else { local = local.best(worseId); } if (worseId != 0) { offers[outbound_tkn][inbound_tkn][worseId] = offers[outbound_tkn][ inbound_tkn ][worseId].prev(betterId); } return local; } } /* ## Check offer is live */ /* Check whether an offer is 'live', that is: inserted in the order book. The Mangrove holds a `outbound_tkn => inbound_tkn => id => P.Offer.t` mapping in storage. Offer ids that are not yet assigned or that point to since-deleted offer will point to an offer with `gives` field at 0. */ function isLive(P.Offer.t offer) public pure returns (bool) { unchecked { return offer.gives() > 0; } } }
// SPDX-License-Identifier: Unlicense // MgvLib.sol // This is free and unencumbered software released into the public domain. // Anyone is free to copy, modify, publish, use, compile, sell, or distribute this software, either in source code form or as a compiled binary, for any purpose, commercial or non-commercial, and by any means. // In jurisdictions that recognize copyright laws, the author or authors of this software dedicate any and all copyright interest in the software to the public domain. We make this dedication for the benefit of the public at large and to the detriment of our heirs and successors. We intend this dedication to be an overt act of relinquishment in perpetuity of all present and future rights to this software under copyright law. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // For more information, please refer to <https://unlicense.org/> /* `MgvLib` contains data structures returned by external calls to Mangrove and the interfaces it uses for its own external calls. */ pragma solidity ^0.8.10; pragma abicoder v2; import "./IERC20.sol"; import "./MgvPack.sol" as P; /* # Structs The structs defined in `structs.js` have their counterpart as solidity structs that are easy to manipulate for outside contracts / callers of view functions. */ library MgvLib { /* Some miscellaneous data types useful to `Mangrove` and external contracts */ //+clear+ /* `SingleOrder` holds data about an order-offer match in a struct. Used by `marketOrder` and `internalSnipes` (and some of their nested functions) to avoid stack too deep errors. */ struct SingleOrder { address outbound_tkn; address inbound_tkn; uint offerId; P.Offer.t offer; /* `wants`/`gives` mutate over execution. Initially the `wants`/`gives` from the taker's pov, then actual `wants`/`gives` adjusted by offer's price and volume. */ uint wants; uint gives; /* `offerDetail` is only populated when necessary. */ P.OfferDetail.t offerDetail; P.Global.t global; P.Local.t local; } /* <a id="MgvLib/OrderResult"></a> `OrderResult` holds additional data for the maker and is given to them _after_ they fulfilled an offer. It gives them their own returned data from the previous call, and an `mgvData` specifying whether the Mangrove encountered an error. */ struct OrderResult { /* `makerdata` holds a message that was either returned by the maker or passed as revert message at the end of the trade execution*/ bytes32 makerData; /* `mgvData` is an [internal Mangrove status code](#MgvOfferTaking/statusCodes) code. */ bytes32 mgvData; } } /* # Events The events emitted for use by bots are listed here: */ contract HasMgvEvents { /* * Emitted at the creation of the new Mangrove contract on the pair (`inbound_tkn`, `outbound_tkn`)*/ event NewMgv(); /* Mangrove adds or removes wei from `maker`'s account */ /* * Credit event occurs when an offer is removed from the Mangrove or when the `fund` function is called*/ event Credit(address indexed maker, uint amount); /* * Debit event occurs when an offer is posted or when the `withdraw` function is called */ event Debit(address indexed maker, uint amount); /* * Mangrove reconfiguration */ event SetActive( address indexed outbound_tkn, address indexed inbound_tkn, bool value ); event SetFee( address indexed outbound_tkn, address indexed inbound_tkn, uint value ); event SetGasbase( address indexed outbound_tkn, address indexed inbound_tkn, uint offer_gasbase ); event SetGovernance(address value); event SetMonitor(address value); event SetVault(address value); event SetUseOracle(bool value); event SetNotify(bool value); event SetGasmax(uint value); event SetDensity( address indexed outbound_tkn, address indexed inbound_tkn, uint value ); event SetGasprice(uint value); /* Market order execution */ event OrderStart(); event OrderComplete( address indexed outbound_tkn, address indexed inbound_tkn, address indexed taker, uint takerGot, uint takerGave, uint penalty, uint feePaid ); /* * Offer execution */ event OfferSuccess( address indexed outbound_tkn, address indexed inbound_tkn, uint id, // `maker` is not logged because it can be retrieved from the state using `(outbound_tkn,inbound_tkn,id)`. address taker, uint takerWants, uint takerGives ); /* Log information when a trade execution reverts or returns a non empty bytes32 word */ event OfferFail( address indexed outbound_tkn, address indexed inbound_tkn, uint id, // `maker` is not logged because it can be retrieved from the state using `(outbound_tkn,inbound_tkn,id)`. address taker, uint takerWants, uint takerGives, // `mgvData` may only be `"mgv/makerRevert"`, `"mgv/makerTransferFail"` or `"mgv/makerReceiveFail"` bytes32 mgvData ); /* Log information when a posthook reverts */ event PosthookFail( address indexed outbound_tkn, address indexed inbound_tkn, uint offerId, bytes32 posthookData ); /* * After `permit` and `approve` */ event Approval( address indexed outbound_tkn, address indexed inbound_tkn, address owner, address spender, uint value ); /* * Mangrove closure */ event Kill(); /* * An offer was created or updated. A few words about why we include a `prev` field, and why we don't include a `next` field: in theory clients should need neither `prev` nor a `next` field. They could just 1. Read the order book state at a given block `b`. 2. On every event, update a local copy of the orderbook. But in practice, we do not want to force clients to keep a copy of the *entire* orderbook. There may be a long tail of spam. Now if they only start with the first $N$ offers and receive a new offer that goes to the end of the book, they cannot tell if there are missing offers between the new offer and the end of the local copy of the book. So we add a prev pointer so clients with only a prefix of the book can receive out-of-prefix offers and know what to do with them. The `next` pointer is an optimization useful in Solidity (we traverse fewer memory locations) but useless in client code. */ event OfferWrite( address indexed outbound_tkn, address indexed inbound_tkn, address maker, uint wants, uint gives, uint gasprice, uint gasreq, uint id, uint prev ); /* * `offerId` was present and is now removed from the book. */ event OfferRetract( address indexed outbound_tkn, address indexed inbound_tkn, uint id ); } /* # IMaker interface */ interface IMaker { /* Called upon offer execution. - If the call fails, Mangrove will not try to transfer funds. - If the call succeeds but returndata's first 32 bytes are not 0, Mangrove will not try to transfer funds either. - If the call succeeds and returndata's first 32 bytes are 0, Mangrove will try to transfer funds. In other words, you may declare failure by reverting or by returning nonzero data. In both cases, those 32 first bytes will be passed back to you during the call to `makerPosthook` in the `result.mgvData` field. ``` function tradeRevert(bytes32 data) internal pure { bytes memory revData = new bytes(32); assembly { mstore(add(revData, 32), data) revert(add(revData, 32), 32) } } ``` */ function makerExecute(MgvLib.SingleOrder calldata order) external returns (bytes32); /* Called after all offers of an order have been executed. Posthook of the last executed order is called first and full reentrancy into the Mangrove is enabled at this time. `order` recalls key arguments of the order that was processed and `result` recalls important information for updating the current offer. (see [above](#MgvLib/OrderResult))*/ function makerPosthook( MgvLib.SingleOrder calldata order, MgvLib.OrderResult calldata result ) external; } /* # ITaker interface */ interface ITaker { /* Inverted mangrove only: call to taker after loans went through */ function takerTrade( address outbound_tkn, address inbound_tkn, // total amount of outbound_tkn token that was flashloaned to the taker uint totalGot, // total amount of inbound_tkn token that should be made available uint totalGives ) external; } /* # Monitor interface If enabled, the monitor receives notification after each offer execution and is read for each pair's `gasprice` and `density`. */ interface IMgvMonitor { function notifySuccess(MgvLib.SingleOrder calldata sor, address taker) external; function notifyFail(MgvLib.SingleOrder calldata sor, address taker) external; function read(address outbound_tkn, address inbound_tkn) external view returns (uint gasprice, uint density); }
// SPDX-License-Identifier: AGPL-3.0 // MgvOfferMaking.sol // Copyright (C) 2021 Giry SAS. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU Affero General Public License as published // by the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU Affero General Public License for more details. // // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see <https://www.gnu.org/licenses/>. pragma solidity ^0.8.10; pragma abicoder v2; import {IMaker, HasMgvEvents, P} from "./MgvLib.sol"; import {MgvHasOffers} from "./MgvHasOffers.sol"; /* `MgvOfferMaking` contains market-making-related functions. */ contract MgvOfferMaking is MgvHasOffers { using P.Offer for P.Offer.t; using P.OfferDetail for P.OfferDetail.t; using P.Global for P.Global.t; using P.Local for P.Local.t; /* # Public Maker operations ## New Offer */ //+clear+ /* In the Mangrove, makers and takers call separate functions. Market makers call `newOffer` to fill the book, and takers call functions such as `marketOrder` to consume it. */ //+clear+ /* The following structs holds offer creation/update parameters in memory. This frees up stack space for local variables. */ struct OfferPack { address outbound_tkn; address inbound_tkn; uint wants; uint gives; uint id; uint gasreq; uint gasprice; uint pivotId; P.Global.t global; P.Local.t local; // used on update only P.Offer.t oldOffer; } /* The function `newOffer` is for market makers only; no match with the existing book is done. A maker specifies how much `inbound_tkn` it `wants` and how much `outbound_tkn` it `gives`. It also specify with `gasreq` how much gas should be given when executing their offer. `gasprice` indicates an upper bound on the gasprice at which the maker is ready to be penalised if their offer fails. Any value below the Mangrove's internal `gasprice` configuration value will be ignored. `gasreq`, together with `gasprice`, will contribute to determining the penalty provision set aside by the Mangrove from the market maker's `balanceOf` balance. Offers are always inserted at the correct place in the book. This requires walking through offers to find the correct insertion point. As in [Oasis](https://github.com/daifoundation/maker-otc/blob/f2060c5fe12fe3da71ac98e8f6acc06bca3698f5/src/matching_market.sol#L493), the maker should find the id of an offer close to its own and provide it as `pivotId`. An offer cannot be inserted in a closed market, nor when a reentrancy lock for `outbound_tkn`,`inbound_tkn` is on. No more than $2^{32}-1$ offers can ever be created for one `outbound_tkn`,`inbound_tkn` pair. The actual contents of the function is in `writeOffer`, which is called by both `newOffer` and `updateOffer`. */ function newOffer( address outbound_tkn, address inbound_tkn, uint wants, uint gives, uint gasreq, uint gasprice, uint pivotId ) external payable returns (uint) { unchecked { /* In preparation for calling `writeOffer`, we read the `outbound_tkn`,`inbound_tkn` pair configuration, check for reentrancy and market liveness, fill the `OfferPack` struct and increment the `outbound_tkn`,`inbound_tkn` pair's `last`. */ OfferPack memory ofp; (ofp.global, ofp.local) = config(outbound_tkn, inbound_tkn); unlockedMarketOnly(ofp.local); activeMarketOnly(ofp.global, ofp.local); if (msg.value > 0) { creditWei(msg.sender, msg.value); } ofp.id = 1 + ofp.local.last(); require(uint32(ofp.id) == ofp.id, "mgv/offerIdOverflow"); ofp.local = ofp.local.last(ofp.id); ofp.outbound_tkn = outbound_tkn; ofp.inbound_tkn = inbound_tkn; ofp.wants = wants; ofp.gives = gives; ofp.gasreq = gasreq; ofp.gasprice = gasprice; ofp.pivotId = pivotId; /* The second parameter to writeOffer indicates that we are creating a new offer, not updating an existing one. */ writeOffer(ofp, false); /* Since we locally modified a field of the local configuration (`last`), we save the change to storage. Note that `writeOffer` may have further modified the local configuration by updating the current `best` offer. */ locals[ofp.outbound_tkn][ofp.inbound_tkn] = ofp.local; return ofp.id; } } /* ## Update Offer */ //+clear+ /* Very similar to `newOffer`, `updateOffer` prepares an `OfferPack` for `writeOffer`. Makers should use it for updating live offers, but also to save on gas by reusing old, already consumed offers. A `pivotId` should still be given to minimise reads in the offer book. It is OK to give the offers' own id as a pivot. Gas use is minimal when: 1. The offer does not move in the book 2. The offer does not change its `gasreq` 3. The (`outbound_tkn`,`inbound_tkn`)'s `offer_gasbase` has not changed since the offer was last written 4. `gasprice` has not changed since the offer was last written 5. `gasprice` is greater than the Mangrove's gasprice estimation */ function updateOffer( address outbound_tkn, address inbound_tkn, uint wants, uint gives, uint gasreq, uint gasprice, uint pivotId, uint offerId ) external payable { unchecked { OfferPack memory ofp; (ofp.global, ofp.local) = config(outbound_tkn, inbound_tkn); unlockedMarketOnly(ofp.local); activeMarketOnly(ofp.global, ofp.local); if (msg.value > 0) { creditWei(msg.sender, msg.value); } ofp.outbound_tkn = outbound_tkn; ofp.inbound_tkn = inbound_tkn; ofp.wants = wants; ofp.gives = gives; ofp.id = offerId; ofp.gasreq = gasreq; ofp.gasprice = gasprice; ofp.pivotId = pivotId; ofp.oldOffer = offers[outbound_tkn][inbound_tkn][offerId]; // Save local config P.Local.t oldLocal = ofp.local; /* The second argument indicates that we are updating an existing offer, not creating a new one. */ writeOffer(ofp, true); /* We saved the current pair's configuration before calling `writeOffer`, since that function may update the current `best` offer. We now check for any change to the configuration and update it if needed. */ if (!oldLocal.eq(ofp.local)) { locals[ofp.outbound_tkn][ofp.inbound_tkn] = ofp.local; } } } /* ## Retract Offer */ //+clear+ /* `retractOffer` takes the offer `offerId` out of the book. However, `deprovision == true` also refunds the provision associated with the offer. */ function retractOffer( address outbound_tkn, address inbound_tkn, uint offerId, bool deprovision ) external returns (uint provision) { unchecked { (, P.Local.t local) = config(outbound_tkn, inbound_tkn); unlockedMarketOnly(local); P.Offer.t offer = offers[outbound_tkn][inbound_tkn][offerId]; P.OfferDetail.t offerDetail = offerDetails[outbound_tkn][inbound_tkn][ offerId ]; require( msg.sender == offerDetail.maker(), "mgv/retractOffer/unauthorized" ); /* Here, we are about to un-live an offer, so we start by taking it out of the book by stitching together its previous and next offers. Note that unconditionally calling `stitchOffers` would break the book since it would connect offers that may have since moved. */ if (isLive(offer)) { P.Local.t oldLocal = local; local = stitchOffers( outbound_tkn, inbound_tkn, offer.prev(), offer.next(), local ); /* If calling `stitchOffers` has changed the current `best` offer, we update the storage. */ if (!oldLocal.eq(local)) { locals[outbound_tkn][inbound_tkn] = local; } } /* Set `gives` to 0. Moreover, the last argument depends on whether the user wishes to get their provision back (if true, `gasprice` will be set to 0 as well). */ dirtyDeleteOffer( outbound_tkn, inbound_tkn, offerId, offer, offerDetail, deprovision ); /* If the user wants to get their provision back, we compute its provision from the offer's `gasprice`, `offer_gasbase` and `gasreq`. */ if (deprovision) { provision = 10**9 * offerDetail.gasprice() * //gasprice is 0 if offer was deprovisioned (offerDetail.gasreq() + offerDetail.offer_gasbase()); // credit `balanceOf` and log transfer creditWei(msg.sender, provision); } emit OfferRetract(outbound_tkn, inbound_tkn, offerId); } } /* ## Provisioning Market makers must have enough provisions for possible penalties. These provisions are in ETH. Every time a new offer is created or an offer is updated, `balanceOf` is adjusted to provision the offer's maximum possible penalty (`gasprice * (gasreq + offer_gasbase)`). For instance, if the current `balanceOf` of a maker is 1 ether and they create an offer that requires a provision of 0.01 ethers, their `balanceOf` will be reduced to 0.99 ethers. No ethers will move; this is just an internal accounting movement to make sure the maker cannot `withdraw` the provisioned amounts. */ //+clear+ /* Fund should be called with a nonzero value (hence the `payable` modifier). The provision will be given to `maker`, not `msg.sender`. */ function fund(address maker) public payable { unchecked { (P.Global.t _global, ) = config(address(0), address(0)); liveMgvOnly(_global); creditWei(maker, msg.value); } } function fund() external payable { unchecked { fund(msg.sender); } } /* A transfer with enough gas to the Mangrove will increase the caller's available `balanceOf` balance. _You should send enough gas to execute this function when sending money to the Mangrove._ */ receive() external payable { unchecked { fund(msg.sender); } } /* Any provision not currently held to secure an offer's possible penalty is available for withdrawal. */ function withdraw(uint amount) external returns (bool noRevert) { unchecked { /* Since we only ever send money to the caller, we do not need to provide any particular amount of gas, the caller should manage this herself. */ debitWei(msg.sender, amount); (noRevert, ) = msg.sender.call{value: amount}(""); } } /* # Low-level Maker functions */ /* ## Write Offer */ function writeOffer(OfferPack memory ofp, bool update) internal { unchecked { /* `gasprice`'s floor is Mangrove's own gasprice estimate, `ofp.global.gasprice`. We first check that gasprice fits in 16 bits. Otherwise it could be that `uint16(gasprice) < global_gasprice < gasprice`, and the actual value we store is `uint16(gasprice)`. */ require(checkGasprice(ofp.gasprice), "mgv/writeOffer/gasprice/16bits"); if (ofp.gasprice < ofp.global.gasprice()) { ofp.gasprice = ofp.global.gasprice(); } /* * Check `gasreq` below limit. Implies `gasreq` at most 24 bits wide, which ensures no overflow in computation of `provision` (see below). */ require( ofp.gasreq <= ofp.global.gasmax(), "mgv/writeOffer/gasreq/tooHigh" ); /* * Make sure `gives > 0` -- division by 0 would throw in several places otherwise, and `isLive` relies on it. */ require(ofp.gives > 0, "mgv/writeOffer/gives/tooLow"); /* * Make sure that the maker is posting a 'dense enough' offer: the ratio of `outbound_tkn` offered per gas consumed must be high enough. The actual gas cost paid by the taker is overapproximated by adding `offer_gasbase` to `gasreq`. */ require( ofp.gives >= (ofp.gasreq + ofp.local.offer_gasbase()) * ofp.local.density(), "mgv/writeOffer/density/tooLow" ); /* The following checks are for the maker's convenience only. */ require(uint96(ofp.gives) == ofp.gives, "mgv/writeOffer/gives/96bits"); require(uint96(ofp.wants) == ofp.wants, "mgv/writeOffer/wants/96bits"); /* The position of the new or updated offer is found using `findPosition`. If the offer is the best one, `prev == 0`, and if it's the last in the book, `next == 0`. `findPosition` is only ever called here, but exists as a separate function to make the code easier to read. **Warning**: `findPosition` will call `better`, which may read the offer's `offerDetails`. So it is important to find the offer position _before_ we update its `offerDetail` in storage. We waste 1 (hot) read in that case but we deem that the code would get too ugly if we passed the old `offerDetail` as argument to `findPosition` and to `better`, just to save 1 hot read in that specific case. */ (uint prev, uint next) = findPosition(ofp); /* Log the write offer event. */ emit OfferWrite( ofp.outbound_tkn, ofp.inbound_tkn, msg.sender, ofp.wants, ofp.gives, ofp.gasprice, ofp.gasreq, ofp.id, prev ); /* We now write the new `offerDetails` and remember the previous provision (0 by default, for new offers) to balance out maker's `balanceOf`. */ uint oldProvision; { P.OfferDetail.t offerDetail = offerDetails[ofp.outbound_tkn][ ofp.inbound_tkn ][ofp.id]; if (update) { require( msg.sender == offerDetail.maker(), "mgv/updateOffer/unauthorized" ); oldProvision = 10**9 * offerDetail.gasprice() * (offerDetail.gasreq() + offerDetail.offer_gasbase()); } /* If the offer is new, has a new `gasprice`, `gasreq`, or if the Mangrove's `offer_gasbase` configuration parameter has changed, we also update `offerDetails`. */ if ( !update || offerDetail.gasreq() != ofp.gasreq || offerDetail.gasprice() != ofp.gasprice || offerDetail.offer_gasbase() != ofp.local.offer_gasbase() ) { uint offer_gasbase = ofp.local.offer_gasbase(); offerDetails[ofp.outbound_tkn][ofp.inbound_tkn][ofp.id] = P .OfferDetail .pack({ __maker: msg.sender, __gasreq: ofp.gasreq, __offer_gasbase: offer_gasbase, __gasprice: ofp.gasprice }); } } /* With every change to an offer, a maker may deduct provisions from its `balanceOf` balance. It may also get provisions back if the updated offer requires fewer provisions than before. */ { uint provision = (ofp.gasreq + ofp.local.offer_gasbase()) * ofp.gasprice * 10**9; if (provision > oldProvision) { debitWei(msg.sender, provision - oldProvision); } else if (provision < oldProvision) { creditWei(msg.sender, oldProvision - provision); } } /* We now place the offer in the book at the position found by `findPosition`. */ /* First, we test if the offer has moved in the book or is not currently in the book. If `!isLive(ofp.oldOffer)`, we must update its prev/next. If it is live but its prev has changed, we must also update them. Note that checking both `prev = oldPrev` and `next == oldNext` would be redundant. If either is true, then the updated offer has not changed position and there is nothing to update. As a note for future changes, there is a tricky edge case where `prev == oldPrev` yet the prev/next should be changed: a previously-used offer being brought back in the book, and ending with the same prev it had when it was in the book. In that case, the neighbor is currently pointing to _another_ offer, and thus must be updated. With the current code structure, this is taken care of as a side-effect of checking `!isLive`, but should be kept in mind. The same goes in the `next == oldNext` case. */ if (!isLive(ofp.oldOffer) || prev != ofp.oldOffer.prev()) { /* * If the offer is not the best one, we update its predecessor; otherwise we update the `best` value. */ if (prev != 0) { offers[ofp.outbound_tkn][ofp.inbound_tkn][prev] = offers[ ofp.outbound_tkn ][ofp.inbound_tkn][prev].next(ofp.id); } else { ofp.local = ofp.local.best(ofp.id); } /* * If the offer is not the last one, we update its successor. */ if (next != 0) { offers[ofp.outbound_tkn][ofp.inbound_tkn][next] = offers[ ofp.outbound_tkn ][ofp.inbound_tkn][next].prev(ofp.id); } /* * Recall that in this branch, the offer has changed location, or is not currently in the book. If the offer is not new and already in the book, we must remove it from its previous location by stitching its previous prev/next. */ if (update && isLive(ofp.oldOffer)) { ofp.local = stitchOffers( ofp.outbound_tkn, ofp.inbound_tkn, ofp.oldOffer.prev(), ofp.oldOffer.next(), ofp.local ); } } /* With the `prev`/`next` in hand, we finally store the offer in the `offers` map. */ P.Offer.t ofr = P.Offer.pack({ __prev: prev, __next: next, __wants: ofp.wants, __gives: ofp.gives }); offers[ofp.outbound_tkn][ofp.inbound_tkn][ofp.id] = ofr; } } /* ## Find Position */ /* `findPosition` takes a price in the form of a (`ofp.wants`,`ofp.gives`) pair, an offer id (`ofp.pivotId`) and walks the book from that offer (backward or forward) until the right position for the price is found. The position is returned as a `(prev,next)` pair, with `prev` or `next` at 0 to mark the beginning/end of the book (no offer ever has id 0). If prices are equal, `findPosition` will put the newest offer last. */ function findPosition(OfferPack memory ofp) internal view returns (uint, uint) { unchecked { uint prevId; uint nextId; uint pivotId = ofp.pivotId; /* Get `pivot`, optimizing for the case where pivot info is already known */ P.Offer.t pivot = pivotId == ofp.id ? ofp.oldOffer : offers[ofp.outbound_tkn][ofp.inbound_tkn][pivotId]; /* In case pivotId is not an active offer, it is unusable (since it is out of the book). We default to the current best offer. If the book is empty pivot will be 0. That is handled through a test in the `better` comparison function. */ if (!isLive(pivot)) { pivotId = ofp.local.best(); pivot = offers[ofp.outbound_tkn][ofp.inbound_tkn][pivotId]; } /* * Pivot is better than `wants/gives`, we follow `next`. */ if (better(ofp, pivot, pivotId)) { P.Offer.t pivotNext; while (pivot.next() != 0) { uint pivotNextId = pivot.next(); pivotNext = offers[ofp.outbound_tkn][ofp.inbound_tkn][pivotNextId]; if (better(ofp, pivotNext, pivotNextId)) { pivotId = pivotNextId; pivot = pivotNext; } else { break; } } // gets here on empty book (prevId, nextId) = (pivotId, pivot.next()); /* * Pivot is strictly worse than `wants/gives`, we follow `prev`. */ } else { P.Offer.t pivotPrev; while (pivot.prev() != 0) { uint pivotPrevId = pivot.prev(); pivotPrev = offers[ofp.outbound_tkn][ofp.inbound_tkn][pivotPrevId]; if (better(ofp, pivotPrev, pivotPrevId)) { break; } else { pivotId = pivotPrevId; pivot = pivotPrev; } } (prevId, nextId) = (pivot.prev(), pivotId); } return ( prevId == ofp.id ? ofp.oldOffer.prev() : prevId, nextId == ofp.id ? ofp.oldOffer.next() : nextId ); } } /* ## Better */ /* The utility method `better` takes an offer represented by `ofp` and another represented by `offer1`. It returns true iff `offer1` is better or as good as `ofp`. "better" is defined on the lexicographic order $\textrm{price} \times_{\textrm{lex}} \textrm{density}^{-1}$. This means that for the same price, offers that deliver more volume per gas are taken first. In addition to `offer1`, we also provide its id, `offerId1` in order to save gas. If necessary (ie. if the prices `wants1/gives1` and `wants2/gives2` are the same), we read storage to get `gasreq1` at `offerDetails[...][offerId1]. */ function better( OfferPack memory ofp, P.Offer.t offer1, uint offerId1 ) internal view returns (bool) { unchecked { if (offerId1 == 0) { /* Happens on empty book. Returning `false` would work as well due to specifics of `findPosition` but true is more consistent. Here we just want to avoid reading `offerDetail[...][0]` for nothing. */ return true; } uint wants1 = offer1.wants(); uint gives1 = offer1.gives(); uint wants2 = ofp.wants; uint gives2 = ofp.gives; uint weight1 = wants1 * gives2; uint weight2 = wants2 * gives1; if (weight1 == weight2) { uint gasreq1 = offerDetails[ofp.outbound_tkn][ofp.inbound_tkn][offerId1] .gasreq(); uint gasreq2 = ofp.gasreq; return (gives1 * gasreq2 >= gives2 * gasreq1); } else { return weight1 < weight2; } } } }
// SPDX-License-Identifier: AGPL-3.0 // MgvOfferTaking.sol // Copyright (C) 2021 Giry SAS. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU Affero General Public License as published // by the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU Affero General Public License for more details. // // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see <https://www.gnu.org/licenses/>. pragma solidity ^0.8.10; pragma abicoder v2; import {IERC20, HasMgvEvents, IMaker, IMgvMonitor, MgvLib as ML, P} from "./MgvLib.sol"; import {MgvHasOffers} from "./MgvHasOffers.sol"; abstract contract MgvOfferTaking is MgvHasOffers { using P.Offer for P.Offer.t; using P.OfferDetail for P.OfferDetail.t; using P.Global for P.Global.t; using P.Local for P.Local.t; /* # MultiOrder struct */ /* The `MultiOrder` struct is used by market orders and snipes. Some of its fields are only used by market orders (`initialWants, initialGives`). We need a common data structure for both since low-level calls are shared between market orders and snipes. The struct is helpful in decreasing stack use. */ struct MultiOrder { uint initialWants; // used globally by market order, not used by snipes uint initialGives; // used globally by market order, not used by snipes uint totalGot; // used globally by market order, per-offer by snipes uint totalGave; // used globally by market order, per-offer by snipes uint totalPenalty; // used globally address taker; // used globally bool fillWants; // used globally uint feePaid; // used globally } /* # Market Orders */ /* ## Market Order */ //+clear+ /* A market order specifies a (`outbound_tkn`,`inbound_tkn`) pair, a desired total amount of `outbound_tkn` (`takerWants`), and an available total amount of `inbound_tkn` (`takerGives`). It returns two `uint`s: the total amount of `outbound_tkn` received and the total amount of `inbound_tkn` spent. The `takerGives/takerWants` ratio induces a maximum average price that the taker is ready to pay across all offers that will be executed during the market order. It is thus possible to execute an offer with a price worse than the initial (`takerGives`/`takerWants`) ratio given as argument to `marketOrder` if some cheaper offers were executed earlier in the market order. The market order stops when the price has become too high, or when the end of the book has been reached, or: * If `fillWants` is true, the market order stops when `takerWants` units of `outbound_tkn` have been obtained. With `fillWants` set to true, to buy a specific volume of `outbound_tkn` at any price, set `takerWants` to the amount desired and `takerGives` to $2^{160}-1$. * If `fillWants` is false, the taker is filling `gives` instead: the market order stops when `takerGives` units of `inbound_tkn` have been sold. With `fillWants` set to false, to sell a specific volume of `inbound_tkn` at any price, set `takerGives` to the amount desired and `takerWants` to $0$. */ function marketOrder( address outbound_tkn, address inbound_tkn, uint takerWants, uint takerGives, bool fillWants ) external returns ( uint, uint, uint, uint ) { unchecked { return generalMarketOrder( outbound_tkn, inbound_tkn, takerWants, takerGives, fillWants, msg.sender ); } } /* # General Market Order */ //+clear+ /* General market orders set up the market order with a given `taker` (`msg.sender` in the most common case). Returns `(totalGot, totalGave, penaltyReceived, feePaid)`. Note that the `taker` can be anyone. This is safe when `taker == msg.sender`, but `generalMarketOrder` must not be called with `taker != msg.sender` unless a security check is done after (see [`MgvOfferTakingWithPermit`](#mgvoffertakingwithpermit.sol)`. */ function generalMarketOrder( address outbound_tkn, address inbound_tkn, uint takerWants, uint takerGives, bool fillWants, address taker ) internal returns ( uint, uint, uint, uint ) { unchecked { /* Since amounts stored in offers are 96 bits wide, checking that `takerWants` and `takerGives` fit in 160 bits prevents overflow during the main market order loop. */ require( uint160(takerWants) == takerWants, "mgv/mOrder/takerWants/160bits" ); require( uint160(takerGives) == takerGives, "mgv/mOrder/takerGives/160bits" ); /* `SingleOrder` is defined in `MgvLib.sol` and holds information for ordering the execution of one offer. */ ML.SingleOrder memory sor; sor.outbound_tkn = outbound_tkn; sor.inbound_tkn = inbound_tkn; (sor.global, sor.local) = config(outbound_tkn, inbound_tkn); /* Throughout the execution of the market order, the `sor`'s offer id and other parameters will change. We start with the current best offer id (0 if the book is empty). */ sor.offerId = sor.local.best(); sor.offer = offers[outbound_tkn][inbound_tkn][sor.offerId]; /* `sor.wants` and `sor.gives` may evolve, but they are initially however much remains in the market order. */ sor.wants = takerWants; sor.gives = takerGives; /* `MultiOrder` (defined above) maintains information related to the entire market order. During the order, initial `wants`/`gives` values minus the accumulated amounts traded so far give the amounts that remain to be traded. */ MultiOrder memory mor; mor.initialWants = takerWants; mor.initialGives = takerGives; mor.taker = taker; mor.fillWants = fillWants; /* For the market order to even start, the market needs to be both active, and not currently protected from reentrancy. */ activeMarketOnly(sor.global, sor.local); unlockedMarketOnly(sor.local); /* ### Initialization */ /* The market order will operate as follows : it will go through offers from best to worse, starting from `offerId`, and: */ /* * will maintain remaining `takerWants` and `takerGives` values. The initial `takerGives/takerWants` ratio is the average price the taker will accept. Better prices may be found early in the book, and worse ones later. * will not set `prev`/`next` pointers to their correct locations at each offer taken (this is an optimization enabled by forbidding reentrancy). * after consuming a segment of offers, will update the current `best` offer to be the best remaining offer on the book. */ /* We start be enabling the reentrancy lock for this (`outbound_tkn`,`inbound_tkn`) pair. */ sor.local = sor.local.lock(true); locals[outbound_tkn][inbound_tkn] = sor.local; emit OrderStart(); /* Call recursive `internalMarketOrder` function.*/ internalMarketOrder(mor, sor, true); /* Over the course of the market order, a penalty reserved for `msg.sender` has accumulated in `mor.totalPenalty`. No actual transfers have occured yet -- all the ethers given by the makers as provision are owned by the Mangrove. `sendPenalty` finally gives the accumulated penalty to `msg.sender`. */ sendPenalty(mor.totalPenalty); emit OrderComplete( outbound_tkn, inbound_tkn, taker, mor.totalGot, mor.totalGave, mor.totalPenalty, mor.feePaid ); //+clear+ return (mor.totalGot, mor.totalGave, mor.totalPenalty, mor.feePaid); } } /* ## Internal market order */ //+clear+ /* `internalMarketOrder` works recursively. Going downward, each successive offer is executed until the market order stops (due to: volume exhausted, bad price, or empty book). Then the [reentrancy lock is lifted](#internalMarketOrder/liftReentrancy). Going upward, each offer's `maker` contract is called again with its remaining gas and given the chance to update its offers on the book. The last argument is a boolean named `proceed`. If an offer was not executed, it means the price has become too high. In that case, we notify the next recursive call that the market order should end. In this initial call, no offer has been executed yet so `proceed` is true. */ function internalMarketOrder( MultiOrder memory mor, ML.SingleOrder memory sor, bool proceed ) internal { unchecked { /* #### Case 1 : End of order */ /* We execute the offer currently stored in `sor`. */ if ( proceed && (mor.fillWants ? sor.wants > 0 : sor.gives > 0) && sor.offerId > 0 ) { uint gasused; // gas used by `makerExecute` bytes32 makerData; // data returned by maker /* <a id="MgvOfferTaking/statusCodes"></a> `mgvData` is an internal Mangrove status code. It may appear in an [`OrderResult`](#MgvLib/OrderResult). Its possible values are: * `"mgv/notExecuted"`: offer was not executed. * `"mgv/tradeSuccess"`: offer execution succeeded. Will appear in `OrderResult`. * `"mgv/notEnoughGasForMakerTrade"`: cannot give maker close enough to `gasreq`. Triggers a revert of the entire order. * `"mgv/makerRevert"`: execution of `makerExecute` reverted. Will appear in `OrderResult`. * `"mgv/makerTransferFail"`: maker could not send outbound_tkn tokens. Will appear in `OrderResult`. * `"mgv/makerReceiveFail"`: maker could not receive inbound_tkn tokens. Will appear in `OrderResult`. * `"mgv/takerTransferFail"`: taker could not send inbound_tkn tokens. Triggers a revert of the entire order. `mgvData` should not be exploitable by the maker! */ bytes32 mgvData; /* Load additional information about the offer. We don't do it earlier to save one storage read in case `proceed` was false. */ sor.offerDetail = offerDetails[sor.outbound_tkn][sor.inbound_tkn][ sor.offerId ]; /* `execute` will adjust `sor.wants`,`sor.gives`, and may attempt to execute the offer if its price is low enough. It is crucial that an error due to `taker` triggers a revert. That way, [`mgvData`](#MgvOfferTaking/statusCodes) not in `["mgv/notExecuted","mgv/tradeSuccess"]` means the failure is the maker's fault. */ /* Post-execution, `sor.wants`/`sor.gives` reflect how much was sent/taken by the offer. We will need it after the recursive call, so we save it in local variables. Same goes for `offerId`, `sor.offer` and `sor.offerDetail`. */ (gasused, makerData, mgvData) = execute(mor, sor); /* Keep cached copy of current `sor` values. */ uint takerWants = sor.wants; uint takerGives = sor.gives; uint offerId = sor.offerId; P.Offer.t offer = sor.offer; P.OfferDetail.t offerDetail = sor.offerDetail; /* If an execution was attempted, we move `sor` to the next offer. Note that the current state is inconsistent, since we have not yet updated `sor.offerDetails`. */ if (mgvData != "mgv/notExecuted") { sor.wants = mor.initialWants > mor.totalGot ? mor.initialWants - mor.totalGot : 0; /* It is known statically that `mor.initialGives - mor.totalGave` does not underflow since 1. `mor.totalGave` was increased by `sor.gives` during `execute`, 2. `sor.gives` was at most `mor.initialGives - mor.totalGave` from earlier step, 3. `sor.gives` may have been clamped _down_ during `execute` (to "`offer.wants`" if the offer is entirely consumed, or to `makerWouldWant`, cf. code of `execute`). */ sor.gives = mor.initialGives - mor.totalGave; sor.offerId = sor.offer.next(); sor.offer = offers[sor.outbound_tkn][sor.inbound_tkn][sor.offerId]; } /* note that internalMarketOrder may be called twice with same offerId, but in that case `proceed` will be false! */ internalMarketOrder( mor, sor, /* `proceed` value for next call. Currently, when an offer did not execute, it's because the offer's price was too high. In that case we interrupt the loop and let the taker leave with less than they asked for (but at a correct price). We could also revert instead of breaking; this could be a configurable flag for the taker to pick. */ mgvData != "mgv/notExecuted" ); /* Restore `sor` values from to before recursive call */ sor.offerId = offerId; sor.wants = takerWants; sor.gives = takerGives; sor.offer = offer; sor.offerDetail = offerDetail; /* After an offer execution, we may run callbacks and increase the total penalty. As that part is common to market orders and snipes, it lives in its own `postExecute` function. */ if (mgvData != "mgv/notExecuted") { postExecute(mor, sor, gasused, makerData, mgvData); } /* #### Case 2 : End of market order */ /* If `proceed` is false, the taker has gotten its requested volume, or we have reached the end of the book, we conclude the market order. */ } else { /* During the market order, all executed offers have been removed from the book. We end by stitching together the `best` offer pointer and the new best offer. */ sor.local = stitchOffers( sor.outbound_tkn, sor.inbound_tkn, 0, sor.offerId, sor.local ); /* <a id="internalMarketOrder/liftReentrancy"></a>Now that the market order is over, we can lift the lock on the book. In the same operation we * lift the reentrancy lock, and * update the storage so we are free from out of order storage writes. */ sor.local = sor.local.lock(false); locals[sor.outbound_tkn][sor.inbound_tkn] = sor.local; /* `payTakerMinusFees` sends the fee to the vault, proportional to the amount purchased, and gives the rest to the taker */ payTakerMinusFees(mor, sor); /* In an inverted Mangrove, amounts have been lent by each offer's maker to the taker. We now call the taker. This is a noop in a normal Mangrove. */ executeEnd(mor, sor); } } } /* # Sniping */ /* ## Snipes */ //+clear+ /* `snipes` executes multiple offers. It takes a `uint[4][]` as penultimate argument, with each array element of the form `[offerId,takerWants,takerGives,offerGasreq]`. The return parameters are of the form `(successes,snipesGot,snipesGave,bounty,feePaid)`. Note that we do not distinguish further between mismatched arguments/offer fields on the one hand, and an execution failure on the other. Still, a failed offer has to pay a penalty, and ultimately transaction logs explicitly mention execution failures (see `MgvLib.sol`). */ function snipes( address outbound_tkn, address inbound_tkn, uint[4][] calldata targets, bool fillWants ) external returns ( uint, uint, uint, uint, uint ) { unchecked { return generalSnipes( outbound_tkn, inbound_tkn, targets, fillWants, msg.sender ); } } /* From an array of _n_ `[offerId, takerWants,takerGives,gasreq]` elements, execute each snipe in sequence. Returns `(successes, takerGot, takerGave, bounty, feePaid)`. Note that if this function is not internal, anyone can make anyone use Mangrove. Note that unlike general market order, the returned total values are _not_ `mor.totalGot` and `mor.totalGave`, since those are reset at every iteration of the `targets` array. Instead, accumulators `snipesGot` and `snipesGave` are used. */ function generalSnipes( address outbound_tkn, address inbound_tkn, uint[4][] calldata targets, bool fillWants, address taker ) internal returns ( uint successCount, uint snipesGot, uint snipesGave, uint totalPenalty, uint feePaid ) { unchecked { ML.SingleOrder memory sor; sor.outbound_tkn = outbound_tkn; sor.inbound_tkn = inbound_tkn; (sor.global, sor.local) = config(outbound_tkn, inbound_tkn); MultiOrder memory mor; mor.taker = taker; mor.fillWants = fillWants; /* For the snipes to even start, the market needs to be both active and not currently protected from reentrancy. */ activeMarketOnly(sor.global, sor.local); unlockedMarketOnly(sor.local); emit OrderStart(); /* ### Main loop */ //+clear+ /* Call `internalSnipes` function. */ (successCount, snipesGot, snipesGave) = internalSnipes(mor, sor, targets); /* Over the course of the snipes order, a penalty reserved for `msg.sender` has accumulated in `mor.totalPenalty`. No actual transfers have occured yet -- all the ethers given by the makers as provision are owned by the Mangrove. `sendPenalty` finally gives the accumulated penalty to `msg.sender`. */ sendPenalty(mor.totalPenalty); //+clear+ emit OrderComplete( sor.outbound_tkn, sor.inbound_tkn, taker, snipesGot, snipesGave, mor.totalPenalty, mor.feePaid ); totalPenalty = mor.totalPenalty; feePaid = mor.feePaid; } } /* ## Internal snipes */ //+clear+ /* `internalSnipes` works by looping over targets. Each successive offer is executed under a [reentrancy lock](#internalSnipes/liftReentrancy), then its posthook is called. Going upward, each offer's `maker` contract is called again with its remaining gas and given the chance to update its offers on the book. */ function internalSnipes( MultiOrder memory mor, ML.SingleOrder memory sor, uint[4][] calldata targets ) internal returns ( uint successCount, uint snipesGot, uint snipesGave ) { unchecked { for (uint i = 0; i < targets.length; i++) { /* Reset these amounts since every snipe is treated individually. Only the total penalty is sent at the end of all snipes. */ mor.totalGot = 0; mor.totalGave = 0; /* Initialize single order struct. */ sor.offerId = targets[i][0]; sor.offer = offers[sor.outbound_tkn][sor.inbound_tkn][sor.offerId]; sor.offerDetail = offerDetails[sor.outbound_tkn][sor.inbound_tkn][ sor.offerId ]; /* If we removed the `isLive` conditional, a single expired or nonexistent offer in `targets` would revert the entire transaction (by the division by `offer.gives` below since `offer.gives` would be 0). We also check that `gasreq` is not worse than specified. A taker who does not care about `gasreq` can specify any amount larger than $2^{24}-1$. A mismatched price will be detected by `execute`. */ if (!isLive(sor.offer) || sor.offerDetail.gasreq() > targets[i][3]) { /* We move on to the next offer in the array. */ continue; } else { require( uint96(targets[i][1]) == targets[i][1], "mgv/snipes/takerWants/96bits" ); require( uint96(targets[i][2]) == targets[i][2], "mgv/snipes/takerGives/96bits" ); sor.wants = targets[i][1]; sor.gives = targets[i][2]; /* We start be enabling the reentrancy lock for this (`outbound_tkn`,`inbound_tkn`) pair. */ sor.local = sor.local.lock(true); locals[sor.outbound_tkn][sor.inbound_tkn] = sor.local; /* `execute` will adjust `sor.wants`,`sor.gives`, and may attempt to execute the offer if its price is low enough. It is crucial that an error due to `taker` triggers a revert. That way [`mgvData`](#MgvOfferTaking/statusCodes) not in `["mgv/tradeSuccess","mgv/notExecuted"]` means the failure is the maker's fault. */ /* Post-execution, `sor.wants`/`sor.gives` reflect how much was sent/taken by the offer. */ (uint gasused, bytes32 makerData, bytes32 mgvData) = execute( mor, sor ); if (mgvData == "mgv/tradeSuccess") { successCount += 1; } /* In the market order, we were able to avoid stitching back offers after every `execute` since we knew a continuous segment starting at best would be consumed. Here, we cannot do this optimisation since offers in the `targets` array may be anywhere in the book. So we stitch together offers immediately after each `execute`. */ if (mgvData != "mgv/notExecuted") { sor.local = stitchOffers( sor.outbound_tkn, sor.inbound_tkn, sor.offer.prev(), sor.offer.next(), sor.local ); } /* <a id="internalSnipes/liftReentrancy"></a> Now that the current snipe is over, we can lift the lock on the book. In the same operation we * lift the reentrancy lock, and * update the storage so we are free from out of order storage writes. */ sor.local = sor.local.lock(false); locals[sor.outbound_tkn][sor.inbound_tkn] = sor.local; /* `payTakerMinusFees` sends the fee to the vault, proportional to the amount purchased, and gives the rest to the taker */ payTakerMinusFees(mor, sor); /* In an inverted Mangrove, amounts have been lent by each offer's maker to the taker. We now call the taker. This is a noop in a normal Mangrove. */ executeEnd(mor, sor); /* After an offer execution, we may run callbacks and increase the total penalty. As that part is common to market orders and snipes, it lives in its own `postExecute` function. */ if (mgvData != "mgv/notExecuted") { postExecute(mor, sor, gasused, makerData, mgvData); } snipesGot += mor.totalGot; snipesGave += mor.totalGave; } } } } /* # General execution */ /* During a market order or a snipes, offers get executed. The following code takes care of executing a single offer with parameters given by a `SingleOrder` within a larger context given by a `MultiOrder`. */ /* ## Execute */ /* This function will compare `sor.wants` `sor.gives` with `sor.offer.wants` and `sor.offer.gives`. If the price of the offer is low enough, an execution will be attempted (with volume limited by the offer's advertised volume). Summary of the meaning of the return values: * `gasused` is the gas consumed by the execution * `makerData` is the data returned after executing the offer * `mgvData` is an [internal Mangrove status code](#MgvOfferTaking/statusCodes). */ function execute(MultiOrder memory mor, ML.SingleOrder memory sor) internal returns ( uint gasused, bytes32 makerData, bytes32 mgvData ) { unchecked { /* #### `Price comparison` */ //+clear+ /* The current offer has a price `p = offerWants ÷ offerGives` and the taker is ready to accept a price up to `p' = takerGives ÷ takerWants`. Comparing `offerWants * takerWants` and `offerGives * takerGives` tels us whether `p < p'`. */ { uint offerWants = sor.offer.wants(); uint offerGives = sor.offer.gives(); uint takerWants = sor.wants; uint takerGives = sor.gives; /* <a id="MgvOfferTaking/checkPrice"></a>If the price is too high, we return early. Otherwise we now know we'll execute the offer. */ if (offerWants * takerWants > offerGives * takerGives) { return (0, bytes32(0), "mgv/notExecuted"); } /* ### Specification of value transfers: Let $o_w$ be `offerWants`, $o_g$ be `offerGives`, $t_w$ be `takerWants`, $t_g$ be `takerGives`, and `f ∈ {w,g}` be $w$ if `fillWants` is true, $g$ otherwise. Let $\textrm{got}$ be the amount that the taker will receive, and $\textrm{gave}$ be the amount that the taker will pay. #### Case $f = w$ If $f = w$, let $\textrm{got} = \min(o_g,t_w)$, and let $\textrm{gave} = \left\lceil\dfrac{o_w \textrm{got}}{o_g}\right\rceil$. This is well-defined since, for live offers, $o_g > 0$. In plain english, we only give to the taker up to what they wanted (or what the offer has to give), and follow the offer price to determine what the taker will give. Since $\textrm{gave}$ is rounded up, the price might be overevaluated. Still, we cannot spend more than what the taker specified as `takerGives`. At this point [we know](#MgvOfferTaking/checkPrice) that $o_w t_w \leq o_g t_g$, so since $t_g$ is an integer we have $t_g \geq \left\lceil\dfrac{o_w t_w}{o_g}\right\rceil \geq \left\lceil\dfrac{o_w \textrm{got}}{o_g}\right\rceil = \textrm{gave}$. #### Case $f = g$ If $f = g$, let $\textrm{gave} = \min(o_w,t_g)$, and $\textrm{got} = o_g$ if $o_w = 0$, $\textrm{got} = \left\lfloor\dfrac{o_g \textrm{gave}}{o_w}\right\rfloor$ otherwise. In plain english, we spend up to what the taker agreed to pay (or what the offer wants), and follow the offer price to determine what the taker will get. This may exceed $t_w$. #### Price adjustment Prices are rounded up to ensure maker is not drained on small amounts. It's economically unlikely, but `density` protects the taker from being drained anyway so it is better to default towards protecting the maker here. */ /* ### Implementation First we check the cases $(f=w \wedge o_g < t_w)\vee(f_g \wedge o_w < t_g)$, in which case the above spec simplifies to $\textrm{got} = o_g, \textrm{gave} = o_w$. Otherwise the offer may be partially consumed. In the case $f=w$ we don't touch $\textrm{got}$ (which was initialized to $t_w$) and compute $\textrm{gave} = \left\lceil\dfrac{o_w t_w}{o_g}\right\rceil$. As shown above we have $\textrm{gave} \leq t_g$. In the case $f=g$ we don't touch $\textrm{gave}$ (which was initialized to $t_g$) and compute $\textrm{got} = o_g$ if $o_w = 0$, and $\textrm{got} = \left\lfloor\dfrac{o_g t_g}{o_w}\right\rfloor$ otherwise. */ if ( (mor.fillWants && offerGives < takerWants) || (!mor.fillWants && offerWants < takerGives) ) { sor.wants = offerGives; sor.gives = offerWants; } else { if (mor.fillWants) { uint product = offerWants * takerWants; sor.gives = product / offerGives + (product % offerGives == 0 ? 0 : 1); } else { if (offerWants == 0) { sor.wants = offerGives; } else { sor.wants = (offerGives * takerGives) / offerWants; } } } } /* The flashloan is executed by call to `flashloan`. If the call reverts, it means the maker failed to send back `sor.wants` `outbound_tkn` to the taker. Notes : * `msg.sender` is the Mangrove itself in those calls -- all operations related to the actual caller should be done outside of this call. * any spurious exception due to an error in Mangrove code will be falsely blamed on the Maker, and its provision for the offer will be unfairly taken away. */ (bool success, bytes memory retdata) = address(this).call( abi.encodeWithSelector(this.flashloan.selector, sor, mor.taker) ); /* `success` is true: trade is complete */ if (success) { /* In case of success, `retdata` encodes the gas used by the offer. */ gasused = abi.decode(retdata, (uint)); /* `mgvData` indicates trade success */ mgvData = bytes32("mgv/tradeSuccess"); emit OfferSuccess( sor.outbound_tkn, sor.inbound_tkn, sor.offerId, mor.taker, sor.wants, sor.gives ); /* If configured to do so, the Mangrove notifies an external contract that a successful trade has taken place. */ if (sor.global.notify()) { IMgvMonitor(sor.global.monitor()).notifySuccess(sor, mor.taker); } /* We update the totals in the multiorder based on the adjusted `sor.wants`/`sor.gives`. */ /* overflow: sor.{wants,gives} are on 96bits, sor.total{Got,Gave} are on 256 bits. */ mor.totalGot += sor.wants; mor.totalGave += sor.gives; } else { /* In case of failure, `retdata` encodes a short [status code](#MgvOfferTaking/statusCodes), the gas used by the offer, and an arbitrary 256 bits word sent by the maker. */ (mgvData, gasused, makerData) = innerDecode(retdata); /* Note that in the `if`s, the literals are bytes32 (stack values), while as revert arguments, they are strings (memory pointers). */ if ( mgvData == "mgv/makerRevert" || mgvData == "mgv/makerTransferFail" || mgvData == "mgv/makerReceiveFail" ) { emit OfferFail( sor.outbound_tkn, sor.inbound_tkn, sor.offerId, mor.taker, sor.wants, sor.gives, mgvData ); /* If configured to do so, the Mangrove notifies an external contract that a failed trade has taken place. */ if (sor.global.notify()) { IMgvMonitor(sor.global.monitor()).notifyFail(sor, mor.taker); } /* It is crucial that any error code which indicates an error caused by the taker triggers a revert, because functions that call `execute` consider that `mgvData` not in `["mgv/notExecuted","mgv/tradeSuccess"]` should be blamed on the maker. */ } else if (mgvData == "mgv/notEnoughGasForMakerTrade") { revert("mgv/notEnoughGasForMakerTrade"); } else if (mgvData == "mgv/takerTransferFail") { revert("mgv/takerTransferFail"); } else { /* This code must be unreachable. **Danger**: if a well-crafted offer/maker pair can force a revert of `flashloan`, the Mangrove will be stuck. */ revert("mgv/swapError"); } } /* Delete the offer. The last argument indicates whether the offer should be stripped of its provision (yes if execution failed, no otherwise). We delete offers whether the amount remaining on offer is > density or not for the sake of uniformity (code is much simpler). We also expect prices to move often enough that the maker will want to update their price anyway. To simulate leaving the remaining volume in the offer, the maker can program their `makerPosthook` to `updateOffer` and put the remaining volume back in. */ dirtyDeleteOffer( sor.outbound_tkn, sor.inbound_tkn, sor.offerId, sor.offer, sor.offerDetail, mgvData != "mgv/tradeSuccess" ); } } /* ## flashloan (abstract) */ /* Externally called by `execute`, flashloan lends money (from the taker to the maker, or from the maker to the taker, depending on the implementation) then calls `makerExecute` to run the maker liquidity fetching code. If `makerExecute` is unsuccessful, `flashloan` reverts (but the larger orderbook traversal will continue). All `flashloan` implementations must `require(msg.sender) == address(this))`. */ function flashloan(ML.SingleOrder calldata sor, address taker) external virtual returns (uint gasused); /* ## Maker Execute */ /* Called by `flashloan`, `makerExecute` runs the maker code and checks that it can safely send the desired assets to the taker. */ function makerExecute(ML.SingleOrder calldata sor) internal returns (uint gasused) { unchecked { bytes memory cd = abi.encodeWithSelector( IMaker.makerExecute.selector, sor ); uint gasreq = sor.offerDetail.gasreq(); address maker = sor.offerDetail.maker(); uint oldGas = gasleft(); /* We let the maker pay for the overhead of checking remaining gas and making the call, as well as handling the return data (constant gas since only the first 32 bytes of return data are read). So the `require` below is just an approximation: if the overhead of (`require` + cost of `CALL`) is $h$, the maker will receive at worst $\textrm{gasreq} - \frac{63h}{64}$ gas. */ /* Note : as a possible future feature, we could stop an order when there's not enough gas left to continue processing offers. This could be done safely by checking, as soon as we start processing an offer, whether `63/64(gasleft-offer_gasbase) > gasreq`. If no, we could stop and know by induction that there is enough gas left to apply fees, stitch offers, etc for the offers already executed. */ if (!(oldGas - oldGas / 64 >= gasreq)) { innerRevert([bytes32("mgv/notEnoughGasForMakerTrade"), "", ""]); } (bool callSuccess, bytes32 makerData) = controlledCall(maker, gasreq, cd); gasused = oldGas - gasleft(); if (!callSuccess) { innerRevert([bytes32("mgv/makerRevert"), bytes32(gasused), makerData]); } bool transferSuccess = transferTokenFrom( sor.outbound_tkn, maker, address(this), sor.wants ); if (!transferSuccess) { innerRevert( [bytes32("mgv/makerTransferFail"), bytes32(gasused), makerData] ); } } } /* ## executeEnd (abstract) */ /* Called by `internalSnipes` and `internalMarketOrder`, `executeEnd` may run implementation-specific code after all makers have been called once. In [`InvertedMangrove`](#InvertedMangrove), the function calls the taker once so they can act on their flashloan. In [`Mangrove`], it does nothing. */ function executeEnd(MultiOrder memory mor, ML.SingleOrder memory sor) internal virtual; /* ## Post execute */ /* At this point, we know `mgvData != "mgv/notExecuted"`. After executing an offer (whether in a market order or in snipes), we 1. Call the maker's posthook and sum the total gas used. 2. If offer failed: sum total penalty due to taker and give remainder to maker. */ function postExecute( MultiOrder memory mor, ML.SingleOrder memory sor, uint gasused, bytes32 makerData, bytes32 mgvData ) internal { unchecked { if (mgvData == "mgv/tradeSuccess") { beforePosthook(sor); } uint gasreq = sor.offerDetail.gasreq(); /* We are about to call back the maker, giving it its unused gas (`gasreq - gasused`). Since the gas used so far may exceed `gasreq`, we prevent underflow in the subtraction below by bounding `gasused` above with `gasreq`. We could have decided not to call back the maker at all when there is no gas left, but we do it for uniformity. */ if (gasused > gasreq) { gasused = gasreq; } gasused = gasused + makerPosthook(sor, gasreq - gasused, makerData, mgvData); if (mgvData != "mgv/tradeSuccess") { mor.totalPenalty += applyPenalty(sor, gasused); } } } /* ## beforePosthook (abstract) */ /* Called by `makerPosthook`, this function can run implementation-specific code before calling the maker has been called a second time. In [`InvertedMangrove`](#InvertedMangrove), all makers are called once so the taker gets all of its money in one shot. Then makers are traversed again and the money is sent back to each taker using `beforePosthook`. In [`Mangrove`](#Mangrove), `beforePosthook` does nothing. */ function beforePosthook(ML.SingleOrder memory sor) internal virtual; /* ## Maker Posthook */ function makerPosthook( ML.SingleOrder memory sor, uint gasLeft, bytes32 makerData, bytes32 mgvData ) internal returns (uint gasused) { unchecked { /* At this point, mgvData can only be `"mgv/tradeSuccess"`, `"mgv/makerRevert"`, `"mgv/makerTransferFail"` or `"mgv/makerReceiveFail"` */ bytes memory cd = abi.encodeWithSelector( IMaker.makerPosthook.selector, sor, ML.OrderResult({makerData: makerData, mgvData: mgvData}) ); address maker = sor.offerDetail.maker(); uint oldGas = gasleft(); /* We let the maker pay for the overhead of checking remaining gas and making the call. So the `require` below is just an approximation: if the overhead of (`require` + cost of `CALL`) is $h$, the maker will receive at worst $\textrm{gasreq} - \frac{63h}{64}$ gas. */ if (!(oldGas - oldGas / 64 >= gasLeft)) { revert("mgv/notEnoughGasForMakerPosthook"); } (bool callSuccess, bytes32 posthookData) = controlledCall( maker, gasLeft, cd ); gasused = oldGas - gasleft(); if (!callSuccess) { emit PosthookFail( sor.outbound_tkn, sor.inbound_tkn, sor.offerId, posthookData ); } } } /* ## `controlledCall` */ /* Calls an external function with controlled gas expense. A direct call of the form `(,bytes memory retdata) = maker.call{gas}(selector,...args)` enables a griefing attack: the maker uses half its gas to write in its memory, then reverts with that memory segment as argument. After a low-level call, solidity automaticaly copies `returndatasize` bytes of `returndata` into memory. So the total gas consumed to execute a failing offer could exceed `gasreq + offer_gasbase` where `n` is the number of failing offers. In case of success, we read the first 32 bytes of returndata (the signature of `makerExecute` is `bytes32`). Otherwise, for compatibility with most errors that bubble up from contract calls and Solidity's `require`, we read 32 bytes of returndata starting from the 69th (4 bytes of method sig + 32 bytes of offset + 32 bytes of string length). */ function controlledCall( address callee, uint gasreq, bytes memory cd ) internal returns (bool success, bytes32 data) { unchecked { bytes32[4] memory retdata; /* if success, read returned bytes 1..32, otherwise read returned bytes 69..100. */ assembly { success := call(gasreq, callee, 0, add(cd, 32), mload(cd), retdata, 100) data := mload(add(mul(iszero(success), 68), retdata)) } } } /* # Penalties */ /* Offers are just promises. They can fail. Penalty provisioning discourages from failing too much: we ask makers to provision more ETH than the expected gas cost of executing their offer and penalize them accoridng to wasted gas. Under normal circumstances, we should expect to see bots with a profit expectation dry-running offers locally and executing `snipe` on failing offers, collecting the penalty. The result should be a mostly clean book for actual takers (i.e. a book with only successful offers). **Incentive issue**: if the gas price increases enough after an offer has been created, there may not be an immediately profitable way to remove the fake offers. In that case, we count on 3 factors to keep the book clean: 1. Gas price eventually comes down. 2. Other market makers want to keep the Mangrove attractive and maintain their offer flow. 3. Mangrove governance (who may collect a fee) wants to keep the Mangrove attractive and maximize exchange volume. */ //+clear+ /* After an offer failed, part of its provision is given back to the maker and the rest is stored to be sent to the taker after the entire order completes. In `applyPenalty`, we _only_ credit the maker with its excess provision. So it looks like the maker is gaining something. In fact they're just getting back a fraction of what they provisioned earlier. */ /* Penalty application summary: * If the transaction was a success, we entirely refund the maker and send nothing to the taker. * Otherwise, the maker loses the cost of `gasused + offer_gasbase` gas. The gas price is estimated by `gasprice`. * To create the offer, the maker had to provision for `gasreq + offer_gasbase` gas at a price of `offerDetail.gasprice`. * We do not consider the tx.gasprice. * `offerDetail.gasbase` and `offerDetail.gasprice` are the values of the Mangrove parameters `config.offer_gasbase` and `config.gasprice` when the offer was created. Without caching those values, the provision set aside could end up insufficient to reimburse the maker (or to retribute the taker). */ function applyPenalty(ML.SingleOrder memory sor, uint gasused) internal returns (uint) { unchecked { uint gasreq = sor.offerDetail.gasreq(); uint provision = 10**9 * sor.offerDetail.gasprice() * (gasreq + sor.offerDetail.offer_gasbase()); /* We set `gasused = min(gasused,gasreq)` since `gasreq < gasused` is possible e.g. with `gasreq = 0` (all calls consume nonzero gas). */ if (gasused > gasreq) { gasused = gasreq; } /* As an invariant, `applyPenalty` is only called when `mgvData` is not in `["mgv/notExecuted","mgv/tradeSuccess"]` */ uint penalty = 10**9 * sor.global.gasprice() * (gasused + sor.local.offer_gasbase()); if (penalty > provision) { penalty = provision; } /* Here we write to storage the new maker balance. This occurs _after_ possible reentrant calls. How do we know we're not crediting twice the same amounts? Because the `offer`'s provision was set to 0 in storage (through `dirtyDeleteOffer`) before the reentrant calls. In this function, we are working with cached copies of the offer as it was before it was consumed. */ creditWei(sor.offerDetail.maker(), provision - penalty); return penalty; } } function sendPenalty(uint amount) internal { unchecked { if (amount > 0) { (bool noRevert, ) = msg.sender.call{value: amount}(""); require(noRevert, "mgv/sendPenaltyReverted"); } } } /* Post-trade, `payTakerMinusFees` sends what's due to the taker and the rest (the fees) to the vault. Routing through the Mangrove like that also deals with blacklisting issues (separates the maker-blacklisted and the taker-blacklisted cases). */ function payTakerMinusFees(MultiOrder memory mor, ML.SingleOrder memory sor) internal { unchecked { /* Should be statically provable that the 2 transfers below cannot return false under well-behaved ERC20s and a non-blacklisted, non-0 target. */ uint concreteFee = (mor.totalGot * sor.local.fee()) / 10_000; if (concreteFee > 0) { mor.totalGot -= concreteFee; mor.feePaid = concreteFee; require( transferToken(sor.outbound_tkn, vault, concreteFee), "mgv/feeTransferFail" ); } if (mor.totalGot > 0) { require( transferToken(sor.outbound_tkn, mor.taker, mor.totalGot), "mgv/MgvFailToPayTaker" ); } } } /* # Misc. functions */ /* Regular solidity reverts prepend the string argument with a [function signature](https://docs.soliditylang.org/en/v0.7.6/control-structures.html#revert). Since we wish to transfer data through a revert, the `innerRevert` function does a low-level revert with only the required data. `innerCode` decodes this data. */ function innerDecode(bytes memory data) internal pure returns ( bytes32 mgvData, uint gasused, bytes32 makerData ) { unchecked { /* The `data` pointer is of the form `[mgvData,gasused,makerData]` where each array element is contiguous and has size 256 bits. */ assembly { mgvData := mload(add(data, 32)) gasused := mload(add(data, 64)) makerData := mload(add(data, 96)) } } } /* <a id="MgvOfferTaking/innerRevert"></a>`innerRevert` reverts a raw triple of values to be interpreted by `innerDecode`. */ function innerRevert(bytes32[3] memory data) internal pure { unchecked { assembly { revert(data, 96) } } } /* `transferTokenFrom` is adapted from [existing code](https://soliditydeveloper.com/safe-erc20) and in particular avoids the "no return value" bug. It never throws and returns true iff the transfer was successful according to `tokenAddress`. Note that any spurious exception due to an error in Mangrove code will be falsely blamed on `from`. */ function transferTokenFrom( address tokenAddress, address from, address to, uint value ) internal returns (bool) { unchecked { bytes memory cd = abi.encodeWithSelector( IERC20.transferFrom.selector, from, to, value ); (bool noRevert, bytes memory data) = tokenAddress.call(cd); return (noRevert && (data.length == 0 || abi.decode(data, (bool)))); } } function transferToken( address tokenAddress, address to, uint value ) internal returns (bool) { unchecked { bytes memory cd = abi.encodeWithSelector( IERC20.transfer.selector, to, value ); (bool noRevert, bytes memory data) = tokenAddress.call(cd); return (noRevert && (data.length == 0 || abi.decode(data, (bool)))); } } }
// SPDX-License-Identifier: AGPL-3.0 // MgvOfferTakingWithPermit.sol // Copyright (C) 2021 Giry SAS. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU Affero General Public License as published // by the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU Affero General Public License for more details. // // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see <https://www.gnu.org/licenses/>. pragma solidity ^0.8.10; pragma abicoder v2; import {HasMgvEvents} from "./MgvLib.sol"; import {MgvOfferTaking} from "./MgvOfferTaking.sol"; abstract contract MgvOfferTakingWithPermit is MgvOfferTaking { /* Takers may provide allowances on specific pairs, so other addresses can execute orders in their name. Allowance may be set using the usual `approve` function, or through an [EIP712](https://eips.ethereum.org/EIPS/eip-712) `permit`. The mapping is `outbound_tkn => inbound_tkn => owner => spender => allowance` */ mapping(address => mapping(address => mapping(address => mapping(address => uint)))) public allowances; /* Storing nonces avoids replay attacks. */ mapping(address => uint) public nonces; /* Following [EIP712](https://eips.ethereum.org/EIPS/eip-712), structured data signing has `keccak256("Permit(address outbound_tkn,address inbound_tkn,address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)")` in its prefix. */ bytes32 public constant PERMIT_TYPEHASH = 0xb7bf278e51ab1478b10530c0300f911d9ed3562fc93ab5e6593368fe23c077a2; /* Initialized in the constructor, `DOMAIN_SEPARATOR` avoids cross-application permit reuse. */ bytes32 public immutable DOMAIN_SEPARATOR; constructor(string memory contractName) { /* Initialize [EIP712](https://eips.ethereum.org/EIPS/eip-712) `DOMAIN_SEPARATOR`. */ DOMAIN_SEPARATOR = keccak256( abi.encode( keccak256( "EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)" ), keccak256(bytes(contractName)), keccak256(bytes("1")), block.chainid, address(this) ) ); } /* # Delegation public functions */ /* Adapted from [Uniswap v2 contract](https://github.com/Uniswap/uniswap-v2-core/blob/55ae25109b7918565867e5c39f1e84b7edd19b2a/contracts/UniswapV2ERC20.sol#L81) */ function permit( address outbound_tkn, address inbound_tkn, address owner, address spender, uint value, uint deadline, uint8 v, bytes32 r, bytes32 s ) external { unchecked { require(deadline >= block.timestamp, "mgv/permit/expired"); uint nonce = nonces[owner]++; bytes32 digest = keccak256( abi.encodePacked( "\x19\x01", DOMAIN_SEPARATOR, keccak256( abi.encode( PERMIT_TYPEHASH, outbound_tkn, inbound_tkn, owner, spender, value, nonce, deadline ) ) ) ); address recoveredAddress = ecrecover(digest, v, r, s); require( recoveredAddress != address(0) && recoveredAddress == owner, "mgv/permit/invalidSignature" ); allowances[outbound_tkn][inbound_tkn][owner][spender] = value; emit Approval(outbound_tkn, inbound_tkn, owner, spender, value); } } function approve( address outbound_tkn, address inbound_tkn, address spender, uint value ) external returns (bool) { unchecked { allowances[outbound_tkn][inbound_tkn][msg.sender][spender] = value; emit Approval(outbound_tkn, inbound_tkn, msg.sender, spender, value); return true; } } /* The delegate version of `marketOrder` is `marketOrderFor`, which takes a `taker` address as additional argument. Penalties incurred by failed offers will still be sent to `msg.sender`, but exchanged amounts will be transferred from and to the `taker`. If the `msg.sender`'s allowance for the given `outbound_tkn`,`inbound_tkn` and `taker` are strictly less than the total amount eventually spent by `taker`, the call will fail. */ /* *Note:* `marketOrderFor` and `snipesFor` may emit ERC20 `Transfer` events of value 0 from `taker`, but that's already the case with common ERC20 implementations. */ function marketOrderFor( address outbound_tkn, address inbound_tkn, uint takerWants, uint takerGives, bool fillWants, address taker ) external returns ( uint takerGot, uint takerGave, uint bounty, uint feePaid ) { unchecked { (takerGot, takerGave, bounty, feePaid) = generalMarketOrder( outbound_tkn, inbound_tkn, takerWants, takerGives, fillWants, taker ); /* The sender's allowance is verified after the order complete so that `takerGave` rather than `takerGives` is checked against the allowance. The former may be lower. */ deductSenderAllowance(outbound_tkn, inbound_tkn, taker, takerGave); } } /* The delegate version of `snipes` is `snipesFor`, which takes a `taker` address as additional argument. */ function snipesFor( address outbound_tkn, address inbound_tkn, uint[4][] calldata targets, bool fillWants, address taker ) external returns ( uint successes, uint takerGot, uint takerGave, uint bounty, uint feePaid ) { unchecked { (successes, takerGot, takerGave, bounty, feePaid) = generalSnipes( outbound_tkn, inbound_tkn, targets, fillWants, taker ); /* The sender's allowance is verified after the order complete so that the actual amounts are checked against the allowance, instead of the declared `takerGives`. The former may be lower. An immediate consequence is that any funds availale to Mangrove through `approve` can be used to clean offers. After a `snipesFor` where all offers have failed, all token transfers have been reverted, so `takerGave=0` and the check will succeed -- but the sender will still have received the bounty of the failing offers. */ deductSenderAllowance(outbound_tkn, inbound_tkn, taker, takerGave); } } /* # Misc. low-level functions */ /* Used by `*For` functions, its both checks that `msg.sender` was allowed to use the taker's funds, and decreases the former's allowance. */ function deductSenderAllowance( address outbound_tkn, address inbound_tkn, address owner, uint amount ) internal { unchecked { uint allowed = allowances[outbound_tkn][inbound_tkn][owner][msg.sender]; require(allowed >= amount, "mgv/lowAllowance"); allowances[outbound_tkn][inbound_tkn][owner][msg.sender] = allowed - amount; emit Approval( outbound_tkn, inbound_tkn, owner, msg.sender, allowed - amount ); } } }
pragma solidity ^0.8.10; // SPDX-License-Identifier: Unlicense // MgvPack.sol // This is free and unencumbered software released into the public domain. // Anyone is free to copy, modify, publish, use, compile, sell, or distribute this software, either in source code form or as a compiled binary, for any purpose, commercial or non-commercial, and by any means. // In jurisdictions that recognize copyright laws, the author or authors of this software dedicate any and all copyright interest in the software to the public domain. We make this dedication for the benefit of the public at large and to the detriment of our heirs and successors. We intend this dedication to be an overt act of relinquishment in perpetuity of all present and future rights to this software under copyright law. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // For more information, please refer to <https://unlicense.org/> /* ************************************************** * GENERATED FILE. DO NOT EDIT. * ************************************************** */ /* since you can't convert bool to uint in an expression without conditionals, * we add a file-level function and rely on compiler optimization */ function uint_of_bool(bool b) pure returns (uint u) { assembly { u := b } } // fields are of the form [name,bits,type] // Can't put all structs under a 'Structs' library due to bad variable shadowing rules in Solidity // (would generate lots of spurious warnings about a nameclash between Structs.Offer and library Offer for instance) // struct_defs are of the form [name,obj] struct OfferStruct { uint prev; uint next; uint wants; uint gives; } struct OfferDetailStruct { address maker; uint gasreq; uint offer_gasbase; uint gasprice; } struct GlobalStruct { address monitor; bool useOracle; bool notify; uint gasprice; uint gasmax; bool dead; } struct LocalStruct { bool active; uint fee; uint density; uint offer_gasbase; bool lock; uint best; uint last; } library Offer { //some type safety for each struct type t is uint; uint constant prev_bits = 32; uint constant next_bits = 32; uint constant wants_bits = 96; uint constant gives_bits = 96; uint constant prev_before = 0; uint constant next_before = prev_before + prev_bits; uint constant wants_before = next_before + next_bits; uint constant gives_before = wants_before + wants_bits; uint constant prev_mask = 0x00000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffff; uint constant next_mask = 0xffffffff00000000ffffffffffffffffffffffffffffffffffffffffffffffff; uint constant wants_mask = 0xffffffffffffffff000000000000000000000000ffffffffffffffffffffffff; uint constant gives_mask = 0xffffffffffffffffffffffffffffffffffffffff000000000000000000000000; function to_struct(t __packed) internal pure returns (OfferStruct memory __s) { unchecked { __s.prev = (t.unwrap(__packed) << prev_before) >> (256 - prev_bits); __s.next = (t.unwrap(__packed) << next_before) >> (256 - next_bits); __s.wants = (t.unwrap(__packed) << wants_before) >> (256 - wants_bits); __s.gives = (t.unwrap(__packed) << gives_before) >> (256 - gives_bits); } } function t_of_struct(OfferStruct memory __s) internal pure returns (t) { unchecked { return pack(__s.prev, __s.next, __s.wants, __s.gives); } } function eq(t __packed1, t __packed2) internal pure returns (bool) { unchecked { return t.unwrap(__packed1) == t.unwrap(__packed2); } } function pack( uint __prev, uint __next, uint __wants, uint __gives ) internal pure returns (t) { unchecked { return t.wrap( ((((0 | ((__prev << (256 - prev_bits)) >> prev_before)) | ((__next << (256 - next_bits)) >> next_before)) | ((__wants << (256 - wants_bits)) >> wants_before)) | ((__gives << (256 - gives_bits)) >> gives_before)) ); } } function unpack(t __packed) internal pure returns ( uint __prev, uint __next, uint __wants, uint __gives ) { unchecked { __prev = (t.unwrap(__packed) << prev_before) >> (256 - prev_bits); __next = (t.unwrap(__packed) << next_before) >> (256 - next_bits); __wants = (t.unwrap(__packed) << wants_before) >> (256 - wants_bits); __gives = (t.unwrap(__packed) << gives_before) >> (256 - gives_bits); } } function prev(t __packed) internal pure returns (uint) { unchecked { return (t.unwrap(__packed) << prev_before) >> (256 - prev_bits); } } function prev(t __packed, uint val) internal pure returns (t) { unchecked { return t.wrap( (t.unwrap(__packed) & prev_mask) | (((val << (256 - prev_bits)) >> prev_before)) ); } } function next(t __packed) internal pure returns (uint) { unchecked { return (t.unwrap(__packed) << next_before) >> (256 - next_bits); } } function next(t __packed, uint val) internal pure returns (t) { unchecked { return t.wrap( (t.unwrap(__packed) & next_mask) | (((val << (256 - next_bits)) >> next_before)) ); } } function wants(t __packed) internal pure returns (uint) { unchecked { return (t.unwrap(__packed) << wants_before) >> (256 - wants_bits); } } function wants(t __packed, uint val) internal pure returns (t) { unchecked { return t.wrap( (t.unwrap(__packed) & wants_mask) | (((val << (256 - wants_bits)) >> wants_before)) ); } } function gives(t __packed) internal pure returns (uint) { unchecked { return (t.unwrap(__packed) << gives_before) >> (256 - gives_bits); } } function gives(t __packed, uint val) internal pure returns (t) { unchecked { return t.wrap( (t.unwrap(__packed) & gives_mask) | (((val << (256 - gives_bits)) >> gives_before)) ); } } } library OfferDetail { //some type safety for each struct type t is uint; uint constant maker_bits = 160; uint constant gasreq_bits = 24; uint constant offer_gasbase_bits = 24; uint constant gasprice_bits = 16; uint constant maker_before = 0; uint constant gasreq_before = maker_before + maker_bits; uint constant offer_gasbase_before = gasreq_before + gasreq_bits; uint constant gasprice_before = offer_gasbase_before + offer_gasbase_bits; uint constant maker_mask = 0x0000000000000000000000000000000000000000ffffffffffffffffffffffff; uint constant gasreq_mask = 0xffffffffffffffffffffffffffffffffffffffff000000ffffffffffffffffff; uint constant offer_gasbase_mask = 0xffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffff; uint constant gasprice_mask = 0xffffffffffffffffffffffffffffffffffffffffffffffffffff0000ffffffff; function to_struct(t __packed) internal pure returns (OfferDetailStruct memory __s) { unchecked { __s.maker = address( uint160((t.unwrap(__packed) << maker_before) >> (256 - maker_bits)) ); __s.gasreq = (t.unwrap(__packed) << gasreq_before) >> (256 - gasreq_bits); __s.offer_gasbase = (t.unwrap(__packed) << offer_gasbase_before) >> (256 - offer_gasbase_bits); __s.gasprice = (t.unwrap(__packed) << gasprice_before) >> (256 - gasprice_bits); } } function t_of_struct(OfferDetailStruct memory __s) internal pure returns (t) { unchecked { return pack(__s.maker, __s.gasreq, __s.offer_gasbase, __s.gasprice); } } function eq(t __packed1, t __packed2) internal pure returns (bool) { unchecked { return t.unwrap(__packed1) == t.unwrap(__packed2); } } function pack( address __maker, uint __gasreq, uint __offer_gasbase, uint __gasprice ) internal pure returns (t) { unchecked { return t.wrap( ((((0 | ((uint(uint160(__maker)) << (256 - maker_bits)) >> maker_before)) | ((__gasreq << (256 - gasreq_bits)) >> gasreq_before)) | ((__offer_gasbase << (256 - offer_gasbase_bits)) >> offer_gasbase_before)) | ((__gasprice << (256 - gasprice_bits)) >> gasprice_before)) ); } } function unpack(t __packed) internal pure returns ( address __maker, uint __gasreq, uint __offer_gasbase, uint __gasprice ) { unchecked { __maker = address( uint160((t.unwrap(__packed) << maker_before) >> (256 - maker_bits)) ); __gasreq = (t.unwrap(__packed) << gasreq_before) >> (256 - gasreq_bits); __offer_gasbase = (t.unwrap(__packed) << offer_gasbase_before) >> (256 - offer_gasbase_bits); __gasprice = (t.unwrap(__packed) << gasprice_before) >> (256 - gasprice_bits); } } function maker(t __packed) internal pure returns (address) { unchecked { return address( uint160((t.unwrap(__packed) << maker_before) >> (256 - maker_bits)) ); } } function maker(t __packed, address val) internal pure returns (t) { unchecked { return t.wrap( (t.unwrap(__packed) & maker_mask) | (((uint(uint160(val)) << (256 - maker_bits)) >> maker_before)) ); } } function gasreq(t __packed) internal pure returns (uint) { unchecked { return (t.unwrap(__packed) << gasreq_before) >> (256 - gasreq_bits); } } function gasreq(t __packed, uint val) internal pure returns (t) { unchecked { return t.wrap( (t.unwrap(__packed) & gasreq_mask) | (((val << (256 - gasreq_bits)) >> gasreq_before)) ); } } function offer_gasbase(t __packed) internal pure returns (uint) { unchecked { return (t.unwrap(__packed) << offer_gasbase_before) >> (256 - offer_gasbase_bits); } } function offer_gasbase(t __packed, uint val) internal pure returns (t) { unchecked { return t.wrap( (t.unwrap(__packed) & offer_gasbase_mask) | (((val << (256 - offer_gasbase_bits)) >> offer_gasbase_before)) ); } } function gasprice(t __packed) internal pure returns (uint) { unchecked { return (t.unwrap(__packed) << gasprice_before) >> (256 - gasprice_bits); } } function gasprice(t __packed, uint val) internal pure returns (t) { unchecked { return t.wrap( (t.unwrap(__packed) & gasprice_mask) | (((val << (256 - gasprice_bits)) >> gasprice_before)) ); } } } library Global { //some type safety for each struct type t is uint; uint constant monitor_bits = 160; uint constant useOracle_bits = 8; uint constant notify_bits = 8; uint constant gasprice_bits = 16; uint constant gasmax_bits = 24; uint constant dead_bits = 8; uint constant monitor_before = 0; uint constant useOracle_before = monitor_before + monitor_bits; uint constant notify_before = useOracle_before + useOracle_bits; uint constant gasprice_before = notify_before + notify_bits; uint constant gasmax_before = gasprice_before + gasprice_bits; uint constant dead_before = gasmax_before + gasmax_bits; uint constant monitor_mask = 0x0000000000000000000000000000000000000000ffffffffffffffffffffffff; uint constant useOracle_mask = 0xffffffffffffffffffffffffffffffffffffffff00ffffffffffffffffffffff; uint constant notify_mask = 0xffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffffffff; uint constant gasprice_mask = 0xffffffffffffffffffffffffffffffffffffffffffff0000ffffffffffffffff; uint constant gasmax_mask = 0xffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffff; uint constant dead_mask = 0xffffffffffffffffffffffffffffffffffffffffffffffffffffff00ffffffff; function to_struct(t __packed) internal pure returns (GlobalStruct memory __s) { unchecked { __s.monitor = address( uint160((t.unwrap(__packed) << monitor_before) >> (256 - monitor_bits)) ); __s.useOracle = (((t.unwrap(__packed) << useOracle_before) >> (256 - useOracle_bits)) > 0); __s.notify = (((t.unwrap(__packed) << notify_before) >> (256 - notify_bits)) > 0); __s.gasprice = (t.unwrap(__packed) << gasprice_before) >> (256 - gasprice_bits); __s.gasmax = (t.unwrap(__packed) << gasmax_before) >> (256 - gasmax_bits); __s.dead = (((t.unwrap(__packed) << dead_before) >> (256 - dead_bits)) > 0); } } function t_of_struct(GlobalStruct memory __s) internal pure returns (t) { unchecked { return pack( __s.monitor, __s.useOracle, __s.notify, __s.gasprice, __s.gasmax, __s.dead ); } } function eq(t __packed1, t __packed2) internal pure returns (bool) { unchecked { return t.unwrap(__packed1) == t.unwrap(__packed2); } } function pack( address __monitor, bool __useOracle, bool __notify, uint __gasprice, uint __gasmax, bool __dead ) internal pure returns (t) { unchecked { return t.wrap( ((((((0 | ((uint(uint160(__monitor)) << (256 - monitor_bits)) >> monitor_before)) | ((uint_of_bool(__useOracle) << (256 - useOracle_bits)) >> useOracle_before)) | ((uint_of_bool(__notify) << (256 - notify_bits)) >> notify_before)) | ((__gasprice << (256 - gasprice_bits)) >> gasprice_before)) | ((__gasmax << (256 - gasmax_bits)) >> gasmax_before)) | ((uint_of_bool(__dead) << (256 - dead_bits)) >> dead_before)) ); } } function unpack(t __packed) internal pure returns ( address __monitor, bool __useOracle, bool __notify, uint __gasprice, uint __gasmax, bool __dead ) { unchecked { __monitor = address( uint160((t.unwrap(__packed) << monitor_before) >> (256 - monitor_bits)) ); __useOracle = (((t.unwrap(__packed) << useOracle_before) >> (256 - useOracle_bits)) > 0); __notify = (((t.unwrap(__packed) << notify_before) >> (256 - notify_bits)) > 0); __gasprice = (t.unwrap(__packed) << gasprice_before) >> (256 - gasprice_bits); __gasmax = (t.unwrap(__packed) << gasmax_before) >> (256 - gasmax_bits); __dead = (((t.unwrap(__packed) << dead_before) >> (256 - dead_bits)) > 0); } } function monitor(t __packed) internal pure returns (address) { unchecked { return address( uint160( (t.unwrap(__packed) << monitor_before) >> (256 - monitor_bits) ) ); } } function monitor(t __packed, address val) internal pure returns (t) { unchecked { return t.wrap( (t.unwrap(__packed) & monitor_mask) | (((uint(uint160(val)) << (256 - monitor_bits)) >> monitor_before)) ); } } function useOracle(t __packed) internal pure returns (bool) { unchecked { return (((t.unwrap(__packed) << useOracle_before) >> (256 - useOracle_bits)) > 0); } } function useOracle(t __packed, bool val) internal pure returns (t) { unchecked { return t.wrap( (t.unwrap(__packed) & useOracle_mask) | ( ((uint_of_bool(val) << (256 - useOracle_bits)) >> useOracle_before) ) ); } } function notify(t __packed) internal pure returns (bool) { unchecked { return (((t.unwrap(__packed) << notify_before) >> (256 - notify_bits)) > 0); } } function notify(t __packed, bool val) internal pure returns (t) { unchecked { return t.wrap( (t.unwrap(__packed) & notify_mask) | (((uint_of_bool(val) << (256 - notify_bits)) >> notify_before)) ); } } function gasprice(t __packed) internal pure returns (uint) { unchecked { return (t.unwrap(__packed) << gasprice_before) >> (256 - gasprice_bits); } } function gasprice(t __packed, uint val) internal pure returns (t) { unchecked { return t.wrap( (t.unwrap(__packed) & gasprice_mask) | (((val << (256 - gasprice_bits)) >> gasprice_before)) ); } } function gasmax(t __packed) internal pure returns (uint) { unchecked { return (t.unwrap(__packed) << gasmax_before) >> (256 - gasmax_bits); } } function gasmax(t __packed, uint val) internal pure returns (t) { unchecked { return t.wrap( (t.unwrap(__packed) & gasmax_mask) | (((val << (256 - gasmax_bits)) >> gasmax_before)) ); } } function dead(t __packed) internal pure returns (bool) { unchecked { return (((t.unwrap(__packed) << dead_before) >> (256 - dead_bits)) > 0); } } function dead(t __packed, bool val) internal pure returns (t) { unchecked { return t.wrap( (t.unwrap(__packed) & dead_mask) | (((uint_of_bool(val) << (256 - dead_bits)) >> dead_before)) ); } } } library Local { //some type safety for each struct type t is uint; uint constant active_bits = 8; uint constant fee_bits = 16; uint constant density_bits = 112; uint constant offer_gasbase_bits = 24; uint constant lock_bits = 8; uint constant best_bits = 32; uint constant last_bits = 32; uint constant active_before = 0; uint constant fee_before = active_before + active_bits; uint constant density_before = fee_before + fee_bits; uint constant offer_gasbase_before = density_before + density_bits; uint constant lock_before = offer_gasbase_before + offer_gasbase_bits; uint constant best_before = lock_before + lock_bits; uint constant last_before = best_before + best_bits; uint constant active_mask = 0x00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff; uint constant fee_mask = 0xff0000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffff; uint constant density_mask = 0xffffff0000000000000000000000000000ffffffffffffffffffffffffffffff; uint constant offer_gasbase_mask = 0xffffffffffffffffffffffffffffffffff000000ffffffffffffffffffffffff; uint constant lock_mask = 0xffffffffffffffffffffffffffffffffffffffff00ffffffffffffffffffffff; uint constant best_mask = 0xffffffffffffffffffffffffffffffffffffffffff00000000ffffffffffffff; uint constant last_mask = 0xffffffffffffffffffffffffffffffffffffffffffffffffff00000000ffffff; function to_struct(t __packed) internal pure returns (LocalStruct memory __s) { unchecked { __s.active = (((t.unwrap(__packed) << active_before) >> (256 - active_bits)) > 0); __s.fee = (t.unwrap(__packed) << fee_before) >> (256 - fee_bits); __s.density = (t.unwrap(__packed) << density_before) >> (256 - density_bits); __s.offer_gasbase = (t.unwrap(__packed) << offer_gasbase_before) >> (256 - offer_gasbase_bits); __s.lock = (((t.unwrap(__packed) << lock_before) >> (256 - lock_bits)) > 0); __s.best = (t.unwrap(__packed) << best_before) >> (256 - best_bits); __s.last = (t.unwrap(__packed) << last_before) >> (256 - last_bits); } } function t_of_struct(LocalStruct memory __s) internal pure returns (t) { unchecked { return pack( __s.active, __s.fee, __s.density, __s.offer_gasbase, __s.lock, __s.best, __s.last ); } } function eq(t __packed1, t __packed2) internal pure returns (bool) { unchecked { return t.unwrap(__packed1) == t.unwrap(__packed2); } } function pack( bool __active, uint __fee, uint __density, uint __offer_gasbase, bool __lock, uint __best, uint __last ) internal pure returns (t) { unchecked { return t.wrap( (((((((0 | ((uint_of_bool(__active) << (256 - active_bits)) >> active_before)) | ((__fee << (256 - fee_bits)) >> fee_before)) | ((__density << (256 - density_bits)) >> density_before)) | ((__offer_gasbase << (256 - offer_gasbase_bits)) >> offer_gasbase_before)) | ((uint_of_bool(__lock) << (256 - lock_bits)) >> lock_before)) | ((__best << (256 - best_bits)) >> best_before)) | ((__last << (256 - last_bits)) >> last_before)) ); } } function unpack(t __packed) internal pure returns ( bool __active, uint __fee, uint __density, uint __offer_gasbase, bool __lock, uint __best, uint __last ) { unchecked { __active = (((t.unwrap(__packed) << active_before) >> (256 - active_bits)) > 0); __fee = (t.unwrap(__packed) << fee_before) >> (256 - fee_bits); __density = (t.unwrap(__packed) << density_before) >> (256 - density_bits); __offer_gasbase = (t.unwrap(__packed) << offer_gasbase_before) >> (256 - offer_gasbase_bits); __lock = (((t.unwrap(__packed) << lock_before) >> (256 - lock_bits)) > 0); __best = (t.unwrap(__packed) << best_before) >> (256 - best_bits); __last = (t.unwrap(__packed) << last_before) >> (256 - last_bits); } } function active(t __packed) internal pure returns (bool) { unchecked { return (((t.unwrap(__packed) << active_before) >> (256 - active_bits)) > 0); } } function active(t __packed, bool val) internal pure returns (t) { unchecked { return t.wrap( (t.unwrap(__packed) & active_mask) | (((uint_of_bool(val) << (256 - active_bits)) >> active_before)) ); } } function fee(t __packed) internal pure returns (uint) { unchecked { return (t.unwrap(__packed) << fee_before) >> (256 - fee_bits); } } function fee(t __packed, uint val) internal pure returns (t) { unchecked { return t.wrap( (t.unwrap(__packed) & fee_mask) | (((val << (256 - fee_bits)) >> fee_before)) ); } } function density(t __packed) internal pure returns (uint) { unchecked { return (t.unwrap(__packed) << density_before) >> (256 - density_bits); } } function density(t __packed, uint val) internal pure returns (t) { unchecked { return t.wrap( (t.unwrap(__packed) & density_mask) | (((val << (256 - density_bits)) >> density_before)) ); } } function offer_gasbase(t __packed) internal pure returns (uint) { unchecked { return (t.unwrap(__packed) << offer_gasbase_before) >> (256 - offer_gasbase_bits); } } function offer_gasbase(t __packed, uint val) internal pure returns (t) { unchecked { return t.wrap( (t.unwrap(__packed) & offer_gasbase_mask) | (((val << (256 - offer_gasbase_bits)) >> offer_gasbase_before)) ); } } function lock(t __packed) internal pure returns (bool) { unchecked { return (((t.unwrap(__packed) << lock_before) >> (256 - lock_bits)) > 0); } } function lock(t __packed, bool val) internal pure returns (t) { unchecked { return t.wrap( (t.unwrap(__packed) & lock_mask) | (((uint_of_bool(val) << (256 - lock_bits)) >> lock_before)) ); } } function best(t __packed) internal pure returns (uint) { unchecked { return (t.unwrap(__packed) << best_before) >> (256 - best_bits); } } function best(t __packed, uint val) internal pure returns (t) { unchecked { return t.wrap( (t.unwrap(__packed) & best_mask) | (((val << (256 - best_bits)) >> best_before)) ); } } function last(t __packed) internal pure returns (uint) { unchecked { return (t.unwrap(__packed) << last_before) >> (256 - last_bits); } } function last(t __packed, uint val) internal pure returns (t) { unchecked { return t.wrap( (t.unwrap(__packed) & last_mask) | (((val << (256 - last_bits)) >> last_before)) ); } } }
// SPDX-License-Identifier: AGPL-3.0 // MgvRoot.sol // Copyright (C) 2021 Giry SAS. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU Affero General Public License as published // by the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU Affero General Public License for more details. // // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see <https://www.gnu.org/licenses/>. /* `MgvRoot` and its descendants describe an orderbook-based exchange ("the Mangrove") where market makers *do not have to provision their offer*. See `structs.js` for a longer introduction. In a nutshell: each offer created by a maker specifies an address (`maker`) to call upon offer execution by a taker. In the normal mode of operation, the Mangrove transfers the amount to be paid by the taker to the maker, calls the maker, attempts to transfer the amount promised by the maker to the taker, and reverts if it cannot. There is one Mangrove contract that manages all tradeable pairs. This reduces deployment costs for new pairs and lets market makers have all their provision for all pairs in the same place. The interaction map between the different actors is as follows: <img src="./contactMap.png" width="190%"></img> The sequence diagram of a market order is as follows: <img src="./sequenceChart.png" width="190%"></img> There is a secondary mode of operation in which the _maker_ flashloans the sold amount to the taker. The Mangrove contract is `abstract` and accomodates both modes. Two contracts, `Mangrove` and `InvertedMangrove` inherit from it, one per mode of operation. The contract structure is as follows: <img src="./modular_mangrove.svg" width="180%"> </img> */ pragma solidity ^0.8.10; pragma abicoder v2; import {MgvLib as ML, HasMgvEvents, IMgvMonitor, P} from "./MgvLib.sol"; /* `MgvRoot` contains state variables used everywhere in the operation of the Mangrove and their related function. */ contract MgvRoot is HasMgvEvents { using P.Global for P.Global.t; using P.Local for P.Local.t; /* # State variables */ //+clear+ /* The `vault` address. If a pair has fees >0, those fees are sent to the vault. */ address public vault; /* Global mgv configuration, encoded in a 256 bits word. The information encoded is detailed in [`structs.js`](#structs.js). */ P.Global.t internal internal_global; /* Configuration mapping for each token pair of the form `outbound_tkn => inbound_tkn => P.Local.t`. The structure of each `P.Local.t` value is detailed in [`structs.js`](#structs.js). It fits in one word. */ mapping(address => mapping(address => P.Local.t)) internal locals; /* Checking the size of `density` is necessary to prevent overflow when `density` is used in calculations. */ function checkDensity(uint density) internal pure returns (bool) { unchecked { return uint112(density) == density; } } /* Checking the size of `gasprice` is necessary to prevent a) data loss when `gasprice` is copied to an `OfferDetail` struct, and b) overflow when `gasprice` is used in calculations. */ function checkGasprice(uint gasprice) internal pure returns (bool) { unchecked { return uint16(gasprice) == gasprice; } } /* # Configuration Reads */ /* Reading the configuration for a pair involves reading the config global to all pairs and the local one. In addition, a global parameter (`gasprice`) and a local one (`density`) may be read from the oracle. */ function config(address outbound_tkn, address inbound_tkn) public view returns (P.Global.t _global, P.Local.t _local) { unchecked { _global = internal_global; _local = locals[outbound_tkn][inbound_tkn]; if (_global.useOracle()) { (uint gasprice, uint density) = IMgvMonitor(_global.monitor()).read( outbound_tkn, inbound_tkn ); if (checkGasprice(gasprice)) { _global = _global.gasprice(gasprice); } if (checkDensity(density)) { _local = _local.density(density); } } } } /* Returns the configuration in an ABI-compatible struct. Should not be called internally, would be a huge memory copying waste. Use `config` instead. */ function configInfo(address outbound_tkn, address inbound_tkn) external view returns (P.GlobalStruct memory global, P.LocalStruct memory local) { unchecked { (P.Global.t _global, P.Local.t _local) = config( outbound_tkn, inbound_tkn ); global = _global.to_struct(); local = _local.to_struct(); } } /* Convenience function to check whether given pair is locked */ function locked(address outbound_tkn, address inbound_tkn) external view returns (bool) { P.Local.t local = locals[outbound_tkn][inbound_tkn]; return local.lock(); } /* # Gatekeeping Gatekeeping functions are safety checks called in various places. */ /* `unlockedMarketOnly` protects modifying the market while an order is in progress. Since external contracts are called during orders, allowing reentrancy would, for instance, let a market maker replace offers currently on the book with worse ones. Note that the external contracts _will_ be called again after the order is complete, this time without any lock on the market. */ function unlockedMarketOnly(P.Local.t local) internal pure { require(!local.lock(), "mgv/reentrancyLocked"); } /* <a id="Mangrove/definition/liveMgvOnly"></a> In case of emergency, the Mangrove can be `kill`ed. It cannot be resurrected. When a Mangrove is dead, the following operations are disabled : * Executing an offer * Sending ETH to the Mangrove the normal way. Usual [shenanigans](https://medium.com/@alexsherbuck/two-ways-to-force-ether-into-a-contract-1543c1311c56) are possible. * Creating a new offer */ function liveMgvOnly(P.Global.t _global) internal pure { require(!_global.dead(), "mgv/dead"); } /* When the Mangrove is deployed, all pairs are inactive by default (since `locals[outbound_tkn][inbound_tkn]` is 0 by default). Offers on inactive pairs cannot be taken or created. They can be updated and retracted. */ function activeMarketOnly(P.Global.t _global, P.Local.t _local) internal pure { liveMgvOnly(_global); require(_local.active(), "mgv/inactive"); } }
{ "evmVersion": "london", "libraries": {}, "metadata": { "bytecodeHash": "ipfs", "useLiteralContent": true }, "optimizer": { "enabled": true, "runs": 20000 }, "remappings": [], "outputSelection": { "*": { "*": [ "evm.bytecode", "evm.deployedBytecode", "devdoc", "userdoc", "metadata", "abi" ] } } }
[{"inputs":[{"internalType":"address","name":"governance","type":"address"},{"internalType":"uint256","name":"gasprice","type":"uint256"},{"internalType":"uint256","name":"gasmax","type":"uint256"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"outbound_tkn","type":"address"},{"indexed":true,"internalType":"address","name":"inbound_tkn","type":"address"},{"indexed":false,"internalType":"address","name":"owner","type":"address"},{"indexed":false,"internalType":"address","name":"spender","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"maker","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Credit","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"maker","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Debit","type":"event"},{"anonymous":false,"inputs":[],"name":"Kill","type":"event"},{"anonymous":false,"inputs":[],"name":"NewMgv","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"outbound_tkn","type":"address"},{"indexed":true,"internalType":"address","name":"inbound_tkn","type":"address"},{"indexed":false,"internalType":"uint256","name":"id","type":"uint256"},{"indexed":false,"internalType":"address","name":"taker","type":"address"},{"indexed":false,"internalType":"uint256","name":"takerWants","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"takerGives","type":"uint256"},{"indexed":false,"internalType":"bytes32","name":"mgvData","type":"bytes32"}],"name":"OfferFail","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"outbound_tkn","type":"address"},{"indexed":true,"internalType":"address","name":"inbound_tkn","type":"address"},{"indexed":false,"internalType":"uint256","name":"id","type":"uint256"}],"name":"OfferRetract","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"outbound_tkn","type":"address"},{"indexed":true,"internalType":"address","name":"inbound_tkn","type":"address"},{"indexed":false,"internalType":"uint256","name":"id","type":"uint256"},{"indexed":false,"internalType":"address","name":"taker","type":"address"},{"indexed":false,"internalType":"uint256","name":"takerWants","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"takerGives","type":"uint256"}],"name":"OfferSuccess","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"outbound_tkn","type":"address"},{"indexed":true,"internalType":"address","name":"inbound_tkn","type":"address"},{"indexed":false,"internalType":"address","name":"maker","type":"address"},{"indexed":false,"internalType":"uint256","name":"wants","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"gives","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"gasprice","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"gasreq","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"id","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"prev","type":"uint256"}],"name":"OfferWrite","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"outbound_tkn","type":"address"},{"indexed":true,"internalType":"address","name":"inbound_tkn","type":"address"},{"indexed":true,"internalType":"address","name":"taker","type":"address"},{"indexed":false,"internalType":"uint256","name":"takerGot","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"takerGave","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"penalty","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"feePaid","type":"uint256"}],"name":"OrderComplete","type":"event"},{"anonymous":false,"inputs":[],"name":"OrderStart","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"outbound_tkn","type":"address"},{"indexed":true,"internalType":"address","name":"inbound_tkn","type":"address"},{"indexed":false,"internalType":"uint256","name":"offerId","type":"uint256"},{"indexed":false,"internalType":"bytes32","name":"posthookData","type":"bytes32"}],"name":"PosthookFail","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"outbound_tkn","type":"address"},{"indexed":true,"internalType":"address","name":"inbound_tkn","type":"address"},{"indexed":false,"internalType":"bool","name":"value","type":"bool"}],"name":"SetActive","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"outbound_tkn","type":"address"},{"indexed":true,"internalType":"address","name":"inbound_tkn","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"SetDensity","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"outbound_tkn","type":"address"},{"indexed":true,"internalType":"address","name":"inbound_tkn","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"SetFee","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"outbound_tkn","type":"address"},{"indexed":true,"internalType":"address","name":"inbound_tkn","type":"address"},{"indexed":false,"internalType":"uint256","name":"offer_gasbase","type":"uint256"}],"name":"SetGasbase","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"SetGasmax","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"SetGasprice","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"value","type":"address"}],"name":"SetGovernance","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"value","type":"address"}],"name":"SetMonitor","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bool","name":"value","type":"bool"}],"name":"SetNotify","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bool","name":"value","type":"bool"}],"name":"SetUseOracle","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"value","type":"address"}],"name":"SetVault","type":"event"},{"inputs":[],"name":"DOMAIN_SEPARATOR","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"PERMIT_TYPEHASH","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"outbound_tkn","type":"address"},{"internalType":"address","name":"inbound_tkn","type":"address"},{"internalType":"uint256","name":"fee","type":"uint256"},{"internalType":"uint256","name":"density","type":"uint256"},{"internalType":"uint256","name":"offer_gasbase","type":"uint256"}],"name":"activate","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"allowances","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"outbound_tkn","type":"address"},{"internalType":"address","name":"inbound_tkn","type":"address"},{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"}],"name":"approve","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"outbound_tkn","type":"address"},{"internalType":"address","name":"inbound_tkn","type":"address"}],"name":"best","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"outbound_tkn","type":"address"},{"internalType":"address","name":"inbound_tkn","type":"address"}],"name":"config","outputs":[{"internalType":"Global.t","name":"_global","type":"uint256"},{"internalType":"Local.t","name":"_local","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"outbound_tkn","type":"address"},{"internalType":"address","name":"inbound_tkn","type":"address"}],"name":"configInfo","outputs":[{"components":[{"internalType":"address","name":"monitor","type":"address"},{"internalType":"bool","name":"useOracle","type":"bool"},{"internalType":"bool","name":"notify","type":"bool"},{"internalType":"uint256","name":"gasprice","type":"uint256"},{"internalType":"uint256","name":"gasmax","type":"uint256"},{"internalType":"bool","name":"dead","type":"bool"}],"internalType":"struct GlobalStruct","name":"global","type":"tuple"},{"components":[{"internalType":"bool","name":"active","type":"bool"},{"internalType":"uint256","name":"fee","type":"uint256"},{"internalType":"uint256","name":"density","type":"uint256"},{"internalType":"uint256","name":"offer_gasbase","type":"uint256"},{"internalType":"bool","name":"lock","type":"bool"},{"internalType":"uint256","name":"best","type":"uint256"},{"internalType":"uint256","name":"last","type":"uint256"}],"internalType":"struct LocalStruct","name":"local","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"outbound_tkn","type":"address"},{"internalType":"address","name":"inbound_tkn","type":"address"}],"name":"deactivate","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"outbound_tkn","type":"address"},{"internalType":"address","name":"inbound_tkn","type":"address"},{"internalType":"uint256","name":"offerId","type":"uint256"},{"internalType":"Offer.t","name":"offer","type":"uint256"},{"internalType":"uint256","name":"wants","type":"uint256"},{"internalType":"uint256","name":"gives","type":"uint256"},{"internalType":"OfferDetail.t","name":"offerDetail","type":"uint256"},{"internalType":"Global.t","name":"global","type":"uint256"},{"internalType":"Local.t","name":"local","type":"uint256"}],"internalType":"struct MgvLib.SingleOrder","name":"sor","type":"tuple"},{"internalType":"address","name":"taker","type":"address"}],"name":"flashloan","outputs":[{"internalType":"uint256","name":"gasused","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"maker","type":"address"}],"name":"fund","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"fund","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"governance","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"Offer.t","name":"offer","type":"uint256"}],"name":"isLive","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"kill","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"outbound_tkn","type":"address"},{"internalType":"address","name":"inbound_tkn","type":"address"}],"name":"locked","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"outbound_tkn","type":"address"},{"internalType":"address","name":"inbound_tkn","type":"address"},{"internalType":"uint256","name":"takerWants","type":"uint256"},{"internalType":"uint256","name":"takerGives","type":"uint256"},{"internalType":"bool","name":"fillWants","type":"bool"}],"name":"marketOrder","outputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"outbound_tkn","type":"address"},{"internalType":"address","name":"inbound_tkn","type":"address"},{"internalType":"uint256","name":"takerWants","type":"uint256"},{"internalType":"uint256","name":"takerGives","type":"uint256"},{"internalType":"bool","name":"fillWants","type":"bool"},{"internalType":"address","name":"taker","type":"address"}],"name":"marketOrderFor","outputs":[{"internalType":"uint256","name":"takerGot","type":"uint256"},{"internalType":"uint256","name":"takerGave","type":"uint256"},{"internalType":"uint256","name":"bounty","type":"uint256"},{"internalType":"uint256","name":"feePaid","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"outbound_tkn","type":"address"},{"internalType":"address","name":"inbound_tkn","type":"address"},{"internalType":"uint256","name":"wants","type":"uint256"},{"internalType":"uint256","name":"gives","type":"uint256"},{"internalType":"uint256","name":"gasreq","type":"uint256"},{"internalType":"uint256","name":"gasprice","type":"uint256"},{"internalType":"uint256","name":"pivotId","type":"uint256"}],"name":"newOffer","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"nonces","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"offerDetails","outputs":[{"internalType":"OfferDetail.t","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"outbound_tkn","type":"address"},{"internalType":"address","name":"inbound_tkn","type":"address"},{"internalType":"uint256","name":"offerId","type":"uint256"}],"name":"offerInfo","outputs":[{"components":[{"internalType":"uint256","name":"prev","type":"uint256"},{"internalType":"uint256","name":"next","type":"uint256"},{"internalType":"uint256","name":"wants","type":"uint256"},{"internalType":"uint256","name":"gives","type":"uint256"}],"internalType":"struct OfferStruct","name":"offer","type":"tuple"},{"components":[{"internalType":"address","name":"maker","type":"address"},{"internalType":"uint256","name":"gasreq","type":"uint256"},{"internalType":"uint256","name":"offer_gasbase","type":"uint256"},{"internalType":"uint256","name":"gasprice","type":"uint256"}],"internalType":"struct OfferDetailStruct","name":"offerDetail","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"offers","outputs":[{"internalType":"Offer.t","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"outbound_tkn","type":"address"},{"internalType":"address","name":"inbound_tkn","type":"address"},{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"},{"internalType":"uint256","name":"deadline","type":"uint256"},{"internalType":"uint8","name":"v","type":"uint8"},{"internalType":"bytes32","name":"r","type":"bytes32"},{"internalType":"bytes32","name":"s","type":"bytes32"}],"name":"permit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"outbound_tkn","type":"address"},{"internalType":"address","name":"inbound_tkn","type":"address"},{"internalType":"uint256","name":"offerId","type":"uint256"},{"internalType":"bool","name":"deprovision","type":"bool"}],"name":"retractOffer","outputs":[{"internalType":"uint256","name":"provision","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"outbound_tkn","type":"address"},{"internalType":"address","name":"inbound_tkn","type":"address"},{"internalType":"uint256","name":"density","type":"uint256"}],"name":"setDensity","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"outbound_tkn","type":"address"},{"internalType":"address","name":"inbound_tkn","type":"address"},{"internalType":"uint256","name":"fee","type":"uint256"}],"name":"setFee","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"outbound_tkn","type":"address"},{"internalType":"address","name":"inbound_tkn","type":"address"},{"internalType":"uint256","name":"offer_gasbase","type":"uint256"}],"name":"setGasbase","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"gasmax","type":"uint256"}],"name":"setGasmax","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"gasprice","type":"uint256"}],"name":"setGasprice","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"governanceAddress","type":"address"}],"name":"setGovernance","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"monitor","type":"address"}],"name":"setMonitor","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bool","name":"notify","type":"bool"}],"name":"setNotify","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bool","name":"useOracle","type":"bool"}],"name":"setUseOracle","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"vaultAddress","type":"address"}],"name":"setVault","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"outbound_tkn","type":"address"},{"internalType":"address","name":"inbound_tkn","type":"address"},{"internalType":"uint256[4][]","name":"targets","type":"uint256[4][]"},{"internalType":"bool","name":"fillWants","type":"bool"}],"name":"snipes","outputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"outbound_tkn","type":"address"},{"internalType":"address","name":"inbound_tkn","type":"address"},{"internalType":"uint256[4][]","name":"targets","type":"uint256[4][]"},{"internalType":"bool","name":"fillWants","type":"bool"},{"internalType":"address","name":"taker","type":"address"}],"name":"snipesFor","outputs":[{"internalType":"uint256","name":"successes","type":"uint256"},{"internalType":"uint256","name":"takerGot","type":"uint256"},{"internalType":"uint256","name":"takerGave","type":"uint256"},{"internalType":"uint256","name":"bounty","type":"uint256"},{"internalType":"uint256","name":"feePaid","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"outbound_tkn","type":"address"},{"internalType":"address","name":"inbound_tkn","type":"address"},{"internalType":"uint256","name":"wants","type":"uint256"},{"internalType":"uint256","name":"gives","type":"uint256"},{"internalType":"uint256","name":"gasreq","type":"uint256"},{"internalType":"uint256","name":"gasprice","type":"uint256"},{"internalType":"uint256","name":"pivotId","type":"uint256"},{"internalType":"uint256","name":"offerId","type":"uint256"}],"name":"updateOffer","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"vault","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"withdraw","outputs":[{"internalType":"bool","name":"noRevert","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"stateMutability":"payable","type":"receive"}]
Contract Creation Code
60a06040523480156200001157600080fd5b5060405162005f1538038062005f15833981016040819052620000349162000484565b828282604051806040016040528060088152602001674d616e67726f766560c01b815250808484847f72201d073f249840099a96530e189c4291c1852040b1e96ff1f606db656be4ff60405160405180910390a1620000938362000165565b6200009e82620001c4565b620000a98162000277565b620000b48362000327565b5050815160208084019190912060408051808201825260018152603160f81b9084015280517f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f938101939093528201527fc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc660608201524660808201523060a082015260c001905060408051601f19818403018152919052805160209091012060805250620004c99650505050505050565b6200016f620003d8565b600080546001600160a01b0319166001600160a01b0383169081179091556040519081527fd459c7242e23d490831b5676a611c4342d899d28f342d89ae80793e56a930f30906020015b60405180910390a150565b620001ce620003d8565b61ffff81168114620002275760405162461bcd60e51b815260206004820152601a60248201527f6d67762f636f6e6669672f67617370726963652f31366269747300000000000060448201526064015b60405180910390fd5b62000243816001546200044960201b620022991790919060201c565b6001556040518181527fdbebd814ae648f654dcc50c734aa76e55a32e96b7d85303a08e2ddf11874a0dd90602001620001b9565b62000281620003d8565b808162ffffff1614620002d75760405162461bcd60e51b815260206004820152601860248201527f6d67762f636f6e6669672f6761736d61782f323462697473000000000000000060448201526064016200021e565b620002f3816001546200046760201b620022d11790919060201c565b6001556040518181527f8cdff0cd5ed2543bcdf7eb93d8ec1c23626e579925f4531beeef15d84cf78d1890602001620001b9565b62000331620003d8565b6001600160a01b038116620003895760405162461bcd60e51b815260206004820152601360248201527f6d67762f636f6e6669672f676f762f6e6f74300000000000000000000000000060448201526064016200021e565b600380546001600160a01b0319166001600160a01b0383169081179091556040519081527f24a8c4807b324a269a51827c3446b8ac1cc13810d7d0c0ca1efafabddd7b621990602001620001b9565b6003546001600160a01b0316331480620003f157503330145b806200040657506003546001600160a01b0316155b620004475760405162461bcd60e51b815260206004820152601060248201526f1b59dd8bdd5b985d5d1a1bdc9a5e995960821b60448201526064016200021e565b565b60401b69ffff00000000000000001661ffff60401b19919091161790565b60281b67ffffff00000000001662ffffff60281b19919091161790565b6000806000606084860312156200049a57600080fd5b83516001600160a01b0381168114620004b257600080fd5b602085015160409095015190969495509392505050565b608051615a29620004ec600039600081816103e90152611a710152615a296000f3fe6080604052600436106102ca5760003560e01c80636d27de6711610179578063b60d4288116100d6578063dc7e98df1161008a578063fbfa77cf11610064578063fbfa77cf1461098d578063fbffe5fd146109ad578063fcaa7e2e146109cd57600080fd5b8063dc7e98df1461092d578063def5ddaa1461094d578063fbb669101461096d57600080fd5b8063cbf75c9a116100bb578063cbf75c9a146108b8578063d0ee5099146108ed578063db20266f1461090d57600080fd5b8063b60d428814610869578063c7bb25d01461087157600080fd5b8063aa4dd2821161012d578063ad97db1b11610112578063ad97db1b14610809578063b09d2a1614610829578063b2b120571461084957600080fd5b8063aa4dd282146107c9578063ab033ea9146107e957600080fd5b80637ecebe001161015e5780637ecebe00146107065780638c66715c1461073357806395999ee3146107a957600080fd5b80636d27de671461069957806370a08231146106d957600080fd5b80635722647b11610227578063644e6703116101db5780636817031b116101c05780636817031b1461062857806368c13d6b146106485780636a4f76911461068657600080fd5b8063644e6703146105515780636804f6131461060857600080fd5b80635a611f4e1161020c5780635a611f4e146104d95780635aa6e675146104f95780635cd823901461053157600080fd5b80635722647b1461047b57806359eba454146104b957600080fd5b806330adf81f1161027e57806341c0e1b51161026357806341c0e1b51461040b57806346e142fa1461042057806349f6d2dc1461046857600080fd5b806330adf81f146103955780633644e515146103d757600080fd5b806327507458116102af578063275074581461031257806327d097c6146103555780632e1a7d4d1461037557600080fd5b80630b73a18f146102df57806323024408146102ff57600080fd5b366102da576102d8336109ed565b005b600080fd5b3480156102eb57600080fd5b506102d86102fa3660046151ed565b610a14565b6102d861030d36600461523a565b6109ed565b34801561031e57600080fd5b5061034061032d366004615255565b6bffffffffffffffffffffffff16151590565b60405190151581526020015b60405180910390f35b34801561036157600080fd5b506102d861037036600461526e565b610ae6565b34801561038157600080fd5b50610340610390366004615255565b610be3565b3480156103a157600080fd5b506103c97fb7bf278e51ab1478b10530c0300f911d9ed3562fc93ab5e6593368fe23c077a281565b60405190815260200161034c565b3480156103e357600080fd5b506103c97f000000000000000000000000000000000000000000000000000000000000000081565b34801561041757600080fd5b506102d8610c3d565b34801561042c57600080fd5b5061044061043b3660046152fd565b610c9f565b604080519586526020860194909452928401919091526060830152608082015260a00161034c565b6103c9610476366004615376565b610ccb565b34801561048757600080fd5b506103c961049636600461526e565b600560209081526000938452604080852082529284528284209052825290205481565b3480156104c557600080fd5b506103406104d43660046153d4565b610ebf565b3480156104e557600080fd5b506102d86104f436600461541f565b610f4d565b34801561050557600080fd5b50600354610519906001600160a01b031681565b6040516001600160a01b03909116815260200161034c565b34801561053d57600080fd5b506102d861054c36600461523a565b610fc9565b34801561055d57600080fd5b5061057161056c36600461543c565b611046565b6040805183516001600160a01b0316815260208085015115158183015284830151151582840152606080860151818401526080808701518185015260a0968701511515878501528551151560c0808601919091529286015160e0850152938501516101008401528401516101208301529183015115156101408201529282015161016084015201516101808201526101a00161034c565b34801561061457600080fd5b506102d861062336600461541f565b611149565b34801561063457600080fd5b506102d861064336600461523a565b6111bf565b34801561065457600080fd5b506103c961066336600461526e565b600460209081526000938452604080852082529284528284209052825290205481565b6102d861069436600461546f565b61122d565b3480156106a557600080fd5b506106b96106b43660046154d6565b6113a7565b60408051948552602085019390935291830152606082015260800161034c565b3480156106e557600080fd5b506103c96106f436600461523a565b60066020526000908152604090205481565b34801561071257600080fd5b506103c961072136600461523a565b60086020526000908152604090205481565b34801561073f57600080fd5b5061075361074e36600461526e565b6113de565b60408051835181526020808501518183015284830151828401526060948501518583015283516001600160a01b0316608083015283015160a08201529082015160c082015291015160e08201526101000161034c565b3480156107b557600080fd5b506102d86107c4366004615255565b6114ba565b3480156107d557600080fd5b506103c96107e436600461553e565b61157e565b3480156107f557600080fd5b506102d861080436600461523a565b6116c4565b34801561081557600080fd5b506103c9610824366004615571565b611788565b34801561083557600080fd5b506102d86108443660046155c0565b61195f565b34801561085557600080fd5b50610440610864366004615656565b611cd9565b6102d8611d15565b34801561087d57600080fd5b506103c961088c3660046156d0565b600760209081526000948552604080862082529385528385208152918452828420909152825290205481565b3480156108c457600080fd5b506108d86108d336600461543c565b611d20565b6040805192835260208301919091520161034c565b3480156108f957600080fd5b506102d861090836600461543c565b611eac565b34801561091957600080fd5b5061034061092836600461543c565b611f35565b34801561093957600080fd5b506102d861094836600461526e565b611f70565b34801561095957600080fd5b506103c961096836600461543c565b61206d565b34801561097957600080fd5b506102d861098836600461526e565b6120a5565b34801561099957600080fd5b50600054610519906001600160a01b031681565b3480156109b957600080fd5b506106b96109c8366004615724565b6121ad565b3480156109d957600080fd5b506102d86109e8366004615255565b6121d4565b60006109fa600080611d20565b509050610a0681612307565b610a10823461235f565b5050565b610a1c6123bb565b6001600160a01b0385811660008181526002602090815260408083209489168084529482529182902080547effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f010000000000000000000000000000000000000000000000000000000000000017905581516001815291517fc4ad6e84469caa93e473acfa6c029fcf9d3757d8e268e6233c150771e9f88da69281900390910190a3610ac9858585611f70565b610ad48585846120a5565b610adf858583610ae6565b5050505050565b610aee6123bb565b808162ffffff1614610b475760405162461bcd60e51b815260206004820152601f60248201527f6d67762f636f6e6669672f6f666665725f676173626173652f3234626974730060448201526064015b60405180910390fd5b6001600160a01b0383811660008181526002602090815260408083209487168084529482529182902080547fffffffffffffffffffffffffffffffffff000000ffffffffffffffffffffffff166effffff000000000000000000000000606088901b1617905590518481527fc5859916e5f3fc1ed4d2cfb8c7a6cf709580f228fa0c9d9e0fa468744c7e4a9191015b60405180910390a3505050565b6000610bef3383612433565b60405133908390600081818185875af1925050503d8060008114610c2f576040519150601f19603f3d011682016040523d82523d6000602084013e610c34565b606091505b50909392505050565b610c456123bb565b6001547fffffffffffffffffffffffffffffffffffffffffffffffffffffff00ffffffff16640100000000176001556040517fbe26733c2bf6ff3ea5ba8cfe744422bd49052ff9ed5685c9e81e6f9321dbaddd90600090a1565b6000806000806000610cb58a8a8a8a8a336124f8565b939e929d50909b50995090975095505050505050565b6000610d3c60405180610160016040528060006001600160a01b0316815260200160006001600160a01b031681526020016000815260200160008152602001600081526020016000815260200160008152602001600081526020016000815260200160008152602001600081525090565b610d468989611d20565b6101208301819052610100830191909152610d60906126f1565b610d74816101000151826101200151612746565b3415610d8457610d84333461235f565b61012081015160181c63ffffffff166001016080820181905263ffffffff811614610df15760405162461bcd60e51b815260206004820152601360248201527f6d67762f6f6666657249644f766572666c6f77000000000000000000000000006044820152606401610b3e565b6080810151610120820151610e359160181b66ffffffff000000167fffffffffffffffffffffffffffffffffffffffffffffffffff00000000ffffff919091161790565b6101208201526001600160a01b03808a16825288166020820152604081018790526060810186905260a0810185905260c0810184905260e08101839052610e7d8160006127a7565b61012081015181516001600160a01b03908116600090815260026020908152604080832082870151909416835292905220556080015198975050505050505050565b6001600160a01b038481166000818152600760209081526040808320888616808552908352818420338086529084528285209689168086529684528285208890558251908152928301959095528101859052909291907f6e791f456e6f5034d4928b936c32bf7e650d8228543ce7985d632ab03934837d9060600160405180910390a3506001949350505050565b610f556123bb565b6001547fffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffffffff166aff00000000000000000000605083901b161760015560405181151581527f9b84fe4d9ffc14a6f7ec50c18884ee0d4bb8165aa718a28558df12ab4bd32b14906020015b60405180910390a150565b610fd16123bb565b6001546bffffffffffffffffffffffff167fffffffffffffffffffffffffffffffffffffffff000000000000000000000000606083901b16176001556040516001600160a01b03821681527ff592e42c32883b35bebfbf9d9b801570a25d5ccbba452310746ad2cfd20d548f90602001610fbe565b6040805160c081018252600080825260208201819052918101829052606081018290526080810182905260a08101919091526110bc6040518060e0016040528060001515815260200160008152602001600081526020016000815260200160001515815260200160008152602001600081525090565b6000806110c98686611d20565b91509150611133826040805160c081018252606083811c8252605884901c60ff9081161515602080850191909152605086901c82161515848601529385901c61ffff1691830191909152602884901c62ffffff1660808301529290911c909116151560a082015290565b935061113e81612ff0565b925050509250929050565b6111516123bb565b6001547fffffffffffffffffffffffffffffffffffffffff00ffffffffffffffffffffff166bff0000000000000000000000605883901b161760015560405181151581527fbf39fbfab835c119637f4dbc4fb0f428c559c210200405d1ec25ddb50a93f9d490602001610fbe565b6111c76123bb565b600080547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b0383169081179091556040519081527fd459c7242e23d490831b5676a611c4342d899d28f342d89ae80793e56a930f3090602001610fbe565b61129c60405180610160016040528060006001600160a01b0316815260200160006001600160a01b031681526020016000815260200160008152602001600081526020016000815260200160008152602001600081526020016000815260200160008152602001600081525090565b6112a68989611d20565b61012083018190526101008301919091526112c0906126f1565b6112d4816101000151826101200151612746565b34156112e4576112e4333461235f565b6001600160a01b03808a16808352908916602080840182905260408085018b9052606085018a90526080850186905260a0850189905260c0850188905260e08501879052600093845260048252808420928452918152818320858452905290205461014082015261012081015161135c8260016127a7565b610120820151811461139b5761012082015182516001600160a01b03908116600090815260026020908152604080832082880151909416835292905220555b50505050505050505050565b6000806000806113bb8a8a8a8a8a8a6130a5565b929650909450925090506113d18a8a8786613425565b9650965096509692505050565b6114096040518060800160405280600081526020016000815260200160008152602001600081525090565b61143d604051806080016040528060006001600160a01b031681526020016000815260200160008152602001600081525090565b6001600160a01b03808616600090815260046020908152604080832093881683529281528282208683529052205461147481613537565b6001600160a01b038088166000908152600560209081526040808320938a168352928152828220888352905220549093506114ae8161359e565b92505050935093915050565b6114c26123bb565b808162ffffff16146115165760405162461bcd60e51b815260206004820152601860248201527f6d67762f636f6e6669672f6761736d61782f32346269747300000000000000006044820152606401610b3e565b6001547fffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffff1667ffffff0000000000602883901b16176001556040518181527f8cdff0cd5ed2543bcdf7eb93d8ec1c23626e579925f4531beeef15d84cf78d1890602001610fbe565b60003330146115cf5760405162461bcd60e51b815260206004820152601760248201527f6d67762f666c6173686c6f616e2f70726f7465637465640000000000000000006044820152606401610b3e565b6115ee6115e2604085016020860161523a565b83308660a0013561360d565b1561167857611618611606604085016020860161523a565b60c085013560601c8560a00135613719565b1561162d576116268361381e565b90506116be565b604080516060810182527f6d67762f6d616b6572526563656976654661696c00000000000000000000000081526000602082018190529181019190915261167390606081fd5b6116be565b604080516060810182527f6d67762f74616b65725472616e736665724661696c00000000000000000000008152600060208201819052918101919091526116be90606081fd5b92915050565b6116cc6123bb565b6001600160a01b0381166117225760405162461bcd60e51b815260206004820152601360248201527f6d67762f636f6e6669672f676f762f6e6f7430000000000000000000000000006044820152606401610b3e565b600380547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b0383169081179091556040519081527f24a8c4807b324a269a51827c3446b8ac1cc13810d7d0c0ca1efafabddd7b621990602001610fbe565b6000806117958686611d20565b9150506117a1816126f1565b6001600160a01b038087166000818152600460209081526040808320948a1680845294825280832089845282528083205493835260058252808320948352938152838220888352905291909120546117f98160601c90565b6001600160a01b0316336001600160a01b0316146118595760405162461bcd60e51b815260206004820152601d60248201527f6d67762f726574726163744f666665722f756e617574686f72697a65640000006044820152606401610b3e565b6bffffffffffffffffffffffff8216156118c2578261188e898961187d8660e01c90565b60c087901c63ffffffff1688613a00565b93508084146118c0576001600160a01b03808a166000908152600260209081526040808320938c168352929052208490555b505b6118d088888885858a613b22565b841561190757603081901c62ffffff16604882901c62ffffff1601602082901c61ffff16633b9aca0002029350611907338561235f565b866001600160a01b0316886001600160a01b03167fe2ce51fba856682424155a2d89b2ad1d2adde6ea0a9e3115b4b474cd6cb1798b8860405161194c91815260200190565b60405180910390a3505050949350505050565b428410156119af5760405162461bcd60e51b815260206004820152601260248201527f6d67762f7065726d69742f6578706972656400000000000000000000000000006044820152606401610b3e565b6001600160a01b03878116600081815260086020908152604080832080546001810190915581517fb7bf278e51ab1478b10530c0300f911d9ed3562fc93ab5e6593368fe23c077a2818501528f8716818401528e871660608201526080810195909552948b1660a085015260c084018a905260e084018590526101008085018a905281518086039091018152610120850190915280519101207f19010000000000000000000000000000000000000000000000000000000000006101408401527f00000000000000000000000000000000000000000000000000000000000000006101428401526101628301529061018201604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181528282528051602091820120600080855291840180845281905260ff89169284019290925260608301879052608083018690529092509060019060a0016020604051602081039080840390855afa158015611b2a573d6000803e3d6000fd5b50506040517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe001519150506001600160a01b03811615801590611b7e5750896001600160a01b0316816001600160a01b0316145b611bca5760405162461bcd60e51b815260206004820152601b60248201527f6d67762f7065726d69742f696e76616c69645369676e617475726500000000006044820152606401610b3e565b87600760008e6001600160a01b03166001600160a01b0316815260200190815260200160002060008d6001600160a01b03166001600160a01b0316815260200190815260200160002060008c6001600160a01b03166001600160a01b0316815260200190815260200160002060008b6001600160a01b03166001600160a01b03168152602001908152602001600020819055508a6001600160a01b03168c6001600160a01b03167f6e791f456e6f5034d4928b936c32bf7e650d8228543ce7985d632ab03934837d8c8c8c604051611cc3939291906001600160a01b039384168152919092166020820152604081019190915260600190565b60405180910390a3505050505050505050505050565b6000806000806000611cef8b8b8b8b8b8b6124f8565b939850919650945092509050611d078b8b8886613425565b965096509650965096915050565b611d1e336109ed565b565b6001546001600160a01b03838116600090815260026020908152604080832093861683529290522054605882901c60ff1615611ea557600080611d638460601c90565b6040517f6968abd30000000000000000000000000000000000000000000000000000000081526001600160a01b03888116600483015287811660248301529190911690636968abd3906044016040805180830381865afa158015611dcb573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611def9190615771565b91509150611e008261ffff81161490565b15611e3d577fffffffffffffffffffffffffffffffffffffffffffff0000ffffffffffffffff841669ffff0000000000000000604084901b161793505b6dffffffffffffffffffffffffffff8116811415611ea2577fffffff0000000000000000000000000000ffffffffffffffffffffffffffffff83167cffffffffffffffffffffffffffff000000000000000000000000000000607883901b161761113e565b50505b9250929050565b611eb46123bb565b6001600160a01b03828116600081815260026020908152604080832094861680845294825280832080547effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1690558051928352517fc4ad6e84469caa93e473acfa6c029fcf9d3757d8e268e6233c150771e9f88da69281900390910190a35050565b6001600160a01b038281166000908152600260209081526040808320938516835292905290812054605881901c60ff1615155b949350505050565b611f786123bb565b6101f4811115611fca5760405162461bcd60e51b815260206004820152601460248201527f6d67762f636f6e6669672f6665652f3c3d3530300000000000000000000000006044820152606401610b3e565b6001600160a01b0383811660008181526002602090815260408083209487168084529482529182902080547fff0000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffff167effff000000000000000000000000000000000000000000000000000000000060e888901b1617905590518481527f44a6d70a601a6f8a85c075467e9d7245897140cbf6dd505c9d9d764459f5fb649101610bd6565b6001600160a01b038281166000908152600260209081526040808320938516835292905290812054603881901c63ffffffff16611f68565b6120ad6123bb565b6dffffffffffffffffffffffffffff8116811461210c5760405162461bcd60e51b815260206004820152601a60248201527f6d67762f636f6e6669672f64656e736974792f313132626974730000000000006044820152606401610b3e565b6001600160a01b0383811660008181526002602090815260408083209487168084529482529182902080547fffffff0000000000000000000000000000ffffffffffffffffffffffffffffff167cffffffffffffffffffffffffffff000000000000000000000000000000607888901b1617905590518481527f95f1a495c6d9d1a215995408f6ad898c2b33798d6eb3cf3220b4c6cc8b61b0649101610bd6565b6000806000806121c18989898989336130a5565b929c919b50995090975095505050505050565b6121dc6123bb565b61ffff8116811461222f5760405162461bcd60e51b815260206004820152601a60248201527f6d67762f636f6e6669672f67617370726963652f3136626974730000000000006044820152606401610b3e565b6001547fffffffffffffffffffffffffffffffffffffffffffff0000ffffffffffffffff1669ffff0000000000000000604083901b16176001556040518181527fdbebd814ae648f654dcc50c734aa76e55a32e96b7d85303a08e2ddf11874a0dd90602001610fbe565b60401b69ffff0000000000000000167fffffffffffffffffffffffffffffffffffffffffffff0000ffffffffffffffff919091161790565b60281b67ffffff0000000000167fffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffff919091161790565b602081901c60ff161561235c5760405162461bcd60e51b815260206004820152600860248201527f6d67762f646561640000000000000000000000000000000000000000000000006044820152606401610b3e565b50565b6001600160a01b03821660008181526006602052604090819020805484019055517f1bbf55d483639f8103dc4e035af71a4fbdb16c80be740fa3eef81198acefa094906123af9084815260200190565b60405180910390a25050565b6003546001600160a01b03163314806123d357503330145b806123e757506003546001600160a01b0316155b611d1e5760405162461bcd60e51b815260206004820152601060248201527f6d67762f756e617574686f72697a6564000000000000000000000000000000006044820152606401610b3e565b6001600160a01b0382166000908152600660205260409020548181101561249c5760405162461bcd60e51b815260206004820152601960248201527f6d67762f696e73756666696369656e7450726f766973696f6e000000000000006044820152606401610b3e565b6001600160a01b038316600081815260066020526040908190208484039055517f59c79d79be0fadf59fe689b6952b7ebe90201a3a1f00d4a31982377890bc6046906124eb9085815260200190565b60405180910390a2505050565b600080600080600061256160405180610120016040528060006001600160a01b0316815260200160006001600160a01b03168152602001600081526020016000815260200160008152602001600081526020016000815260200160008152602001600081525090565b6001600160a01b03808d1682528b16602082015261257f8c8c611d20565b8260e0018361010001828152508281525050506125e5604051806101000160405280600081526020016000815260200160008152602001600081526020016000815260200160006001600160a01b03168152602001600015158152602001600081525090565b6001600160a01b03881660a082015288151560c082015260e08201516101008301516126119190612746565b61261f8261010001516126f1565b6040517fbc01c241b6c13c2ee496b5103c73ba7c02e7d4bbc77ee637862cc2b152f068b990600090a161265481838d8d613bc8565b6080840151929950909750955061266a906140c8565b602080830151835160808085015160e0860151604080518d81529687018c905286019190915260608501526001600160a01b03808d1694938116939216917ff906db58b0529ffcfdb9bf545bfdbace7ca86906a716bfd1b65f62ff07cdec47910160405180910390a4806080015193508060e0015192505050965096509650965096915050565b605881901c60ff161561235c5760405162461bcd60e51b815260206004820152601460248201527f6d67762f7265656e7472616e63794c6f636b65640000000000000000000000006044820152606401610b3e565b61274f82612307565b61275b8160f81c151590565b610a105760405162461bcd60e51b815260206004820152600c60248201527f6d67762f696e61637469766500000000000000000000000000000000000000006044820152606401610b3e565b60c082015161ffff8116146127fe5760405162461bcd60e51b815260206004820152601e60248201527f6d67762f77726974654f666665722f67617370726963652f31366269747300006044820152606401610b3e565b61010082015160401c61ffff168260c0015110156128295761010082015160401c61ffff1660c08301525b61010082015160281c62ffffff168260a00151111561288a5760405162461bcd60e51b815260206004820152601d60248201527f6d67762f77726974654f666665722f6761737265712f746f6f486967680000006044820152606401610b3e565b60008260600151116128de5760405162461bcd60e51b815260206004820152601b60248201527f6d67762f77726974654f666665722f67697665732f746f6f4c6f7700000000006044820152606401610b3e565b61012082015160781c6dffffffffffffffffffffffffffff1661012083015160601c62ffffff168360a0015101028260600151101561295f5760405162461bcd60e51b815260206004820152601d60248201527f6d67762f77726974654f666665722f64656e736974792f746f6f4c6f770000006044820152606401610b3e565b60608201516bffffffffffffffffffffffff8116146129c05760405162461bcd60e51b815260206004820152601b60248201527f6d67762f77726974654f666665722f67697665732f39366269747300000000006044820152606401610b3e565b60408201516bffffffffffffffffffffffff811614612a215760405162461bcd60e51b815260206004820152601b60248201527f6d67762f77726974654f666665722f77616e74732f39366269747300000000006044820152606401610b3e565b600080612a2d84614166565b9150915083602001516001600160a01b031684600001516001600160a01b03167f2b101f9ef8ca2761d91bea55ab50a4d051e9248088ff7314ddb8826839544c2a33876040015188606001518960c001518a60a001518b608001518a604051612ad297969594939291906001600160a01b03979097168752602087019590955260408601939093526060850191909152608084015260a083015260c082015260e00190565b60405180910390a383516001600160a01b039081166000908152600560209081526040808320828901519094168352928152828220608088015183529052908120548415612bac57612b248160601c90565b6001600160a01b0316336001600160a01b031614612b845760405162461bcd60e51b815260206004820152601c60248201527f6d67762f7570646174654f666665722f756e617574686f72697a6564000000006044820152606401610b3e565b603081901c62ffffff16604882901c62ffffff1601602082901c61ffff16633b9aca00020291505b841580612bc6575060a0860151604882901c62ffffff1614155b80612bdd575060c0860151602082901c61ffff1614155b80612bfe575061012086015160601c62ffffff16603082901c62ffffff1614155b15612cc05761012086015160009060601c62ffffff169050612c86338860a00151838a60c0015160201b65ffff000000001660309190911b68ffffff0000000000001660489290921b6bffffff0000000000000000001660609390931b7fffffffffffffffffffffffffffffffffffffffff0000000000000000000000001692909217171790565b87516001600160a01b039081166000908152600560209081526040808320828d0151909416835292815282822060808c0151835290522055505b5060c08501516101208601516000919060601c62ffffff168760a001510102633b9aca0002905081811115612d0057612cfb33838303612433565b612d14565b81811015612d1457612d143382840361235f565b506101408501516bffffffffffffffffffffffff161580612d3d575061014085015160e01c8314155b15612f44578215612e0857608085015185516001600160a01b039081166000908152600460209081526040808320828b0151909416835292815282822087835290522054612dcf9160c01b7bffffffff000000000000000000000000000000000000000000000000167fffffffff00000000ffffffffffffffffffffffffffffffffffffffffffffffff919091161790565b85516001600160a01b039081166000908152600460209081526040808320828b0151909416835292815282822087835290522055612e57565b6080850151610120860151612e509160381b6affffffff00000000000000167fffffffffffffffffffffffffffffffffffffffffff00000000ffffffffffffff919091161790565b6101208601525b8115612ee357608085015185516001600160a01b039081166000908152600460208181526040808420828c018051871686529083528185208986528352818520548c518716865293835281852090519095168452938152838320878452905291902060e09290921b7bffffffffffffffffffffffffffffffffffffffffffffffffffffffff9091161790555b838015612f0257506101408501516bffffffffffffffffffffffff1615155b15612f4457612f3d85600001518660200151612f2388610140015160e01c90565b61014089015160c01c63ffffffff16896101200151613a00565b6101208601525b60408501516060808701516000926bffffffffffffffffffffffff909116911b77ffffffffffffffffffffffff0000000000000000000000001660c085901b7bffffffff0000000000000000000000000000000000000000000000001660e087901b17171786516001600160a01b039081166000908152600460209081526040808320828c015190941683529281528282206080909a0151825298909852909620959095555050505050565b6130346040518060e0016040528060001515815260200160008152602001600081526020016000815260200160001515815260200160008152602001600081525090565b60f882901c1515815260e882901c61ffff166020820152607882901c6dffffffffffffffffffffffffffff166040820152606082811c62ffffff1690820152605882901c60ff1615156080820152603882901c63ffffffff90811660a083015260189290921c90911660c082015290565b60008060008087886001600160a01b0316146131035760405162461bcd60e51b815260206004820152601d60248201527f6d67762f6d4f726465722f74616b657257616e74732f313630626974730000006044820152606401610b3e565b86876001600160a01b03161461315b5760405162461bcd60e51b815260206004820152601d60248201527f6d67762f6d4f726465722f74616b657247697665732f313630626974730000006044820152606401610b3e565b6131bc60405180610120016040528060006001600160a01b0316815260200160006001600160a01b03168152602001600081526020016000815260200160008152602001600081526020016000815260200160008152602001600081525090565b6001600160a01b03808c1682528a1660208201526131da8b8b611d20565b610100830181905260e083019190915260381c63ffffffff1660408281019182526001600160a01b038d81166000908152600460209081528382208f841683528152838220945182529384528281205460608087019190915260808087018f905260a08088018f90528551610100808201885296810185905292830184905290820183905260e0808301939093528e82529481018d9052918a169382019390935289151560c082015291830151908301516132959190612746565b6132a38261010001516126f1565b6101008201517fffffffffffffffffffffffffffffffffffffffff00ffffffffffffffffffffff166b0100000000000000000000001782610100018181525050816101000151600260008e6001600160a01b03166001600160a01b0316815260200190815260200160002060008d6001600160a01b03166001600160a01b03168152602001908152602001600020819055507fbc01c241b6c13c2ee496b5103c73ba7c02e7d4bbc77ee637862cc2b152f068b960405160405180910390a161336d81836001614397565b61337a81608001516140c8565b866001600160a01b03168b6001600160a01b03168d6001600160a01b03167ff906db58b0529ffcfdb9bf545bfdbace7ca86906a716bfd1b65f62ff07cdec478460400151856060015186608001518760e001516040516133f3949392919093845260208401929092526040830152606082015260800190565b60405180910390a460408101516060820151608083015160e090930151919e909d50919b509950975050505050505050565b6001600160a01b03808516600090815260076020908152604080832087851684528252808320938616835292815282822033835290522054818110156134ad5760405162461bcd60e51b815260206004820152601060248201527f6d67762f6c6f77416c6c6f77616e6365000000000000000000000000000000006044820152606401610b3e565b6001600160a01b038581166000818152600760209081526040808320898616808552908352818420958916808552958352818420338086529084529382902088880390819055825196875292860193909352840152917f6e791f456e6f5034d4928b936c32bf7e650d8228543ce7985d632ab03934837d9060600160405180910390a35050505050565b6135626040518060800160405280600081526020016000815260200160008152602001600081525090565b60e082901c815263ffffffff60c083901c1660208201526bffffffffffffffffffffffff606083811c8216604084015292169181019190915290565b6135d2604051806080016040528060006001600160a01b031681526020016000815260200160008152602001600081525090565b606082811c8252604883901c62ffffff908116602080850191909152603085901c90911660408401529290921c61ffff169181019190915290565b604080516001600160a01b0385811660248301528481166044830152606480830185905283518084039091018152608490920183526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f23b872dd000000000000000000000000000000000000000000000000000000001790529151600092839182918916906136a1908590615795565b6000604051808303816000865af19150503d80600081146136de576040519150601f19603f3d011682016040523d82523d6000602084013e6136e3565b606091505b509150915081801561370d57508051158061370d57508080602001905181019061370d91906157d0565b98975050505050505050565b604080516001600160a01b038481166024830152604480830185905283518084039091018152606490920183526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fa9059cbb000000000000000000000000000000000000000000000000000000001790529151600092839182918816906137a5908590615795565b6000604051808303816000865af19150503d80600081146137e2576040519150601f19603f3d011682016040523d82523d6000602084013e6137e7565b606091505b509150915081801561381157508051158061381157508080602001905181019061381191906157d0565b93505050505b9392505050565b600080636c49c32c60e01b8360405160240161383a91906157ed565b604080518083037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe00181529190526020810180517fffffffff00000000000000000000000000000000000000000000000000000000939093167bffffffffffffffffffffffffffffffffffffffffffffffffffffffff90931692909217909152905060c0830135604881901c62ffffff169060601c60005a905082604082048203101561392757604080516060810182527f6d67762f6e6f74456e6f756768476173466f724d616b6572547261646500000081526000602082018190529181019190915261392790606081fd5b600080613935848688614638565b915091505a83039650816139895761398960405180606001604052807f6d67762f6d616b6572526576657274000000000000000000000000000000000081526020018960001b815260200183815250606081fd5b60006139a761399b60208b018b61523a565b86308c6080013561360d565b9050806139f4576139f460405180606001604052807f6d67762f6d616b65725472616e736665724661696c000000000000000000000081526020018a60001b815260200184815250606081fd5b50505050505050919050565b60008315613a84576001600160a01b038681166000908152600460209081526040808320938916835292815282822087835290522080547fffffffff00000000ffffffffffffffffffffffffffffffffffffffffffffffff167bffffffff00000000000000000000000000000000000000000000000060c086901b16179055613abd565b7fffffffffffffffffffffffffffffffffffffffffff00000000ffffffffffffff82166affffffff00000000000000603885901b161791505b8215613b19576001600160a01b038681166000908152600460209081526040808320938916835292815282822086835290522080547bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1660e086901b1790555b50949350505050565b7fffffffffffffffffffffffffffffffffffffffff000000000000000000000000831692508015613b73577fffffffffffffffffffffffffffffffffffffffffffffffffffff0000ffffffff821691505b506001600160a01b039485166000818152600460209081526040808320979098168083529681528782208683528152878220949094559081526005835285812094815293825284842092845291905291902055565b60008080805b848110156140bd576000604089018190526060890152858582818110613bf657613bf6615898565b905060800201600060048110613c0e57613c0e615898565b60209081029190910135604089810191825289516001600160a01b039081166000908152600485528281208c86018051841683529086528382208551835286528382205460608e019081528d518416835260058752848320915190931682528552828120935181529290935290205460c0890152516bffffffffffffffffffffffff161580613cdf5750858582818110613caa57613caa615898565b905060800201600360048110613cc257613cc2615898565b6020020135613cdd8860c0015162ffffff60489190911c1690565b115b15613ce9576140b5565b858582818110613cfb57613cfb615898565b905060800201600160048110613d1357613d13615898565b6020020135868683818110613d2a57613d2a615898565b905060800201600160048110613d4257613d42615898565b60200201356bffffffffffffffffffffffff1614613da25760405162461bcd60e51b815260206004820152601c60248201527f6d67762f736e697065732f74616b657257616e74732f393662697473000000006044820152606401610b3e565b858582818110613db457613db4615898565b905060800201600260048110613dcc57613dcc615898565b6020020135868683818110613de357613de3615898565b905060800201600260048110613dfb57613dfb615898565b60200201356bffffffffffffffffffffffff1614613e5b5760405162461bcd60e51b815260206004820152601c60248201527f6d67762f736e697065732f74616b657247697665732f393662697473000000006044820152606401610b3e565b858582818110613e6d57613e6d615898565b905060800201600160048110613e8557613e85615898565b60200201356080880152858582818110613ea157613ea1615898565b905060800201600260048110613eb957613eb9615898565b602002013560a08801526101008701517fffffffffffffffffffffffffffffffffffffffff00ffffffffffffffffffffff166b01000000000000000000000017610100880181905287516001600160a01b039081166000908152600260209081526040808320828d01519094168352929052908120919091558080613f3e8b8b614666565b925092509250807f6d67762f747261646553756363657373000000000000000000000000000000001415613f73576001870196505b807f6d67762f6e6f744578656375746564000000000000000000000000000000000014613fd557613fce8a600001518b60200151613fb58d6060015160e01c90565b60608e015160c01c63ffffffff168e6101000151613a00565b6101008b01525b6101008a01517fffffffffffffffffffffffffffffffffffffffff00ffffffffffffffffffffff168a610100018181525050896101000151600260008c600001516001600160a01b03166001600160a01b0316815260200190815260200160002060008c602001516001600160a01b03166001600160a01b031681526020019081526020016000208190555061406b8b8b614d53565b807f6d67762f6e6f74457865637574656400000000000000000000000000000000001461409f5761409f8b8b858585614e67565b8a60400151860195508a60600151850194505050505b600101613bce565b509450945094915050565b801561235c57604051600090339083908381818185875af1925050503d8060008114614110576040519150601f19603f3d011682016040523d82523d6000602084013e614115565b606091505b5050905080610a105760405162461bcd60e51b815260206004820152601760248201527f6d67762f73656e6450656e616c747952657665727465640000000000000000006044820152606401610b3e565b60008060008060008560e0015190506000866080015182146141bb5786516001600160a01b039081166000908152600460209081526040808320828c01519094168352928152828220858352905220546141c2565b8661014001515b90506bffffffffffffffffffffffff81166142205761012087015160381c63ffffffff1687516001600160a01b039081166000908152600460209081526040808320828d015190941683529281528282208483529052205490925090505b61422b878284614ed7565b156142c55760005b60c082901c63ffffffff16156142ad575086516001600160a01b039081166000908152600460209081526040808320828c0151909416835292815282822063ffffffff60c086901c1680845291529190205490614291898383614ed7565b156142a1578093508192506142a7565b506142ad565b50614233565b8260c083901c63ffffffff16909550935061434e9050565b60005b6142d28260e01c90565b1561433b5760006142e38360e01c90565b89516001600160a01b039081166000908152600460209081526040808320828f015190941683529281528282208483529052205492509050614326898383614ed7565b15614331575061433b565b92509050806142c8565b6143458260e01c90565b94509192508291505b8660800151841461435f5783614369565b61014087015160e01c5b8760800151841461437a578361438a565b61014088015160c01c63ffffffff165b9550955050505050915091565b8080156143be57508260c001516143b55760008260a00151116143be565b60008260800151115b80156143ce575060008260400151115b156145b45781516001600160a01b03908116600090815260056020908152604080832082870151909416835292815282822083860151835290529081205460c0840152808061441d8686614666565b608088015160a089015160408a015160608b015160c08c015196995094975092955090939092907f6d67762f6e6f744578656375746564000000000000000000000000000000000086146145225760408b01518b511161447e576000614487565b60408b01518b51035b60808b015260608b81015160208d01510360a08c01528a015160c01c63ffffffff168a6040018181525050600460008b600001516001600160a01b03166001600160a01b0316815260200190815260200160002060008b602001516001600160a01b03166001600160a01b0316815260200190815260200160002060008b604001518152602001908152602001600020548a60600181815250505b6145508b8b887f6d67762f6e6f74457865637574656400000000000000000000000000000000001415614397565b60408a0183905260808a0185905260a08a0184905260608a0182905260c08a018190527f6d67762f6e6f744578656375746564000000000000000000000000000000000086146145a7576145a78b8b8a8a8a614e67565b5050505050505050505050565b6145d38260000151836020015160008560400151866101000151613a00565b7fffffffffffffffffffffffffffffffffffffffff00ffffffffffffffffffffff16610100830181905282516001600160a01b03908116600090815260026020908152604080832082880151909416835292905220556146338383614d53565b505050565b6000806146436151b3565b60648185516020870160008a8af192508060448415020151915050935093915050565b60008060008061468b85606001516bffffffffffffffffffffffff60609190911c1690565b905060006146a886606001516bffffffffffffffffffffffff1690565b608087015160a08801519192509080830284830211156146f65750600095508594507f6d67762f6e6f74457865637574656400000000000000000000000000000000009350614d4c92505050565b8860c00151801561470657508183105b8061471d57508860c0015115801561471d57508084105b15614735576080880183905260a088018490526147a9565b8860c00151156147805783820283818161475157614751615869565b061561475e576001614761565b60005b60ff1684828161477357614773615869565b040160a08a0152506147a9565b8361479157608088018390526147a9565b83818402816147a2576147a2615869565b0460808901525b50505050600080306001600160a01b031663aa4dd28260e01b878960a001516040516024016147d99291906158c7565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff000000000000000000000000000000000000000000000000000000009094169390931790925290516148629190615795565b6000604051808303816000865af19150503d806000811461489f576040519150601f19603f3d011682016040523d82523d6000602084013e6148a4565b606091505b50915091508115614a2457808060200190518101906148c3919061594f565b94507f6d67762f74726164655375636365737300000000000000000000000000000000925085602001516001600160a01b031686600001516001600160a01b03167f1d81940ff2c3ac549c8acefd7c1b2ffb24aacc8b3cfa309c22abc3de79727f9d88604001518a60a001518a608001518b60a0015160405161496894939291909384526001600160a01b039290921660208401526040830152606082015260800190565b60405180910390a360e086015160501c60ff16156149ff5760e086015160a08801516040517fcac1827100000000000000000000000000000000000000000000000000000000815260609290921c9163cac18271916149cc918a91906004016158c7565b600060405180830381600087803b1580156149e657600080fd5b505af11580156149fa573d6000803e3d6000fd5b505050505b6080860151604088018051909101905260a08601516060880180519091019052614d04565b602081015160408201516060830151909650945092507f6d67762f6d616b65725265766572740000000000000000000000000000000000831480614a875750827f6d67762f6d616b65725472616e736665724661696c0000000000000000000000145b80614ab15750827f6d67762f6d616b6572526563656976654661696c000000000000000000000000145b15614bdc5785602001516001600160a01b031686600001516001600160a01b03167f1d59c048edb5397821854bd12cbe02242b82101c677f949d076d14eb7bd8343688604001518a60a001518a608001518b60a0015189604051614b409594939291909485526001600160a01b0393909316602085015260408401919091526060830152608082015260a00190565b60405180910390a360e086015160501c60ff1615614bd75760e086015160a08801516040517f1159507300000000000000000000000000000000000000000000000000000000815260609290921c91631159507391614ba4918a91906004016158c7565b600060405180830381600087803b158015614bbe57600080fd5b505af1158015614bd2573d6000803e3d6000fd5b505050505b614d04565b827f6d67762f6e6f74456e6f756768476173466f724d616b657254726164650000001415614c4c5760405162461bcd60e51b815260206004820152601d60248201527f6d67762f6e6f74456e6f756768476173466f724d616b657254726164650000006044820152606401610b3e565b827f6d67762f74616b65725472616e736665724661696c00000000000000000000001415614cbc5760405162461bcd60e51b815260206004820152601560248201527f6d67762f74616b65725472616e736665724661696c00000000000000000000006044820152606401610b3e565b60405162461bcd60e51b815260206004820152600d60248201527f6d67762f737761704572726f72000000000000000000000000000000000000006044820152606401610b3e565b614d4986600001518760200151886040015189606001518a60c00151887f6d67762f747261646553756363657373000000000000000000000000000000001415613b22565b50505b9250925092565b6101008101516000906127109060e81c61ffff1684604001510281614d7a57614d7a615869565b0490508015614dfa57604083018051829003905260e083018190528151600054614dae91906001600160a01b031683613719565b614dfa5760405162461bcd60e51b815260206004820152601360248201527f6d67762f6665655472616e736665724661696c000000000000000000000000006044820152606401610b3e565b60408301511561463357614e1b82600001518460a001518560400151613719565b6146335760405162461bcd60e51b815260206004820152601560248201527f6d67762f4d67764661696c546f50617954616b657200000000000000000000006044820152606401610b3e565b60c084015160481c62ffffff1680841115614e80578093505b614e8e858583038585614f80565b84019350817f6d67762f7472616465537563636573730000000000000000000000000000000014614ecf57614ec38585615121565b60808701805190910190525b505050505050565b600081614ee657506001613817565b60408401516060858101516bffffffffffffffffffffffff9186901c821692918616919080840282840280821415614f735789516001600160a01b039081166000908152600560209081526040808320828f015190941683529281528282208b835290529081205460481c62ffffff1660a08c015194029590930294909410159550613817945050505050565b1194506138179350505050565b600080633d3d130d60e01b86604051806040016040528087815260200186815250604051602401614fb2929190615968565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff00000000000000000000000000000000000000000000000000000000939093169290921790915260c087015190915060601c60005a90508660408204820310156150955760405162461bcd60e51b815260206004820181905260248201527f6d67762f6e6f74456e6f756768476173466f724d616b6572506f7374686f6f6b6044820152606401610b3e565b6000806150a3848a87614638565b915091505a83039550816151145789602001516001600160a01b03168a600001516001600160a01b03167f51d5a3ecf332626d83c8207fe9bd22f38801f578c7eb222e2d811cd1b4c629a58c604001518460405161510b929190918252602082015260400190565b60405180910390a35b5050505050949350505050565b60c082015160009062ffffff604882901c81169161ffff602082901c1660309190911c909116820102633b9aca00028184111561515c578193505b61010085015160009060601c62ffffff1660e08701519086019060401c61ffff16633b9aca0002029050818111156151915750805b6151aa6151a28760c0015160601c90565b82840361235f565b95945050505050565b60405180608001604052806004906020820280368337509192915050565b80356001600160a01b03811681146151e857600080fd5b919050565b600080600080600060a0868803121561520557600080fd5b61520e866151d1565b945061521c602087016151d1565b94979496505050506040830135926060810135926080909101359150565b60006020828403121561524c57600080fd5b613817826151d1565b60006020828403121561526757600080fd5b5035919050565b60008060006060848603121561528357600080fd5b61528c846151d1565b925061529a602085016151d1565b9150604084013590509250925092565b60008083601f8401126152bc57600080fd5b50813567ffffffffffffffff8111156152d457600080fd5b6020830191508360208260071b8501011115611ea557600080fd5b801515811461235c57600080fd5b60008060008060006080868803121561531557600080fd5b61531e866151d1565b945061532c602087016151d1565b9350604086013567ffffffffffffffff81111561534857600080fd5b615354888289016152aa565b9094509250506060860135615368816152ef565b809150509295509295909350565b600080600080600080600060e0888a03121561539157600080fd5b61539a886151d1565b96506153a8602089016151d1565b96999698505050506040850135946060810135946080820135945060a0820135935060c0909101359150565b600080600080608085870312156153ea57600080fd5b6153f3856151d1565b9350615401602086016151d1565b925061540f604086016151d1565b9396929550929360600135925050565b60006020828403121561543157600080fd5b8135613817816152ef565b6000806040838503121561544f57600080fd5b615458836151d1565b9150615466602084016151d1565b90509250929050565b600080600080600080600080610100898b03121561548c57600080fd5b615495896151d1565b97506154a360208a016151d1565b979a9799505050506040860135956060810135956080820135955060a0820135945060c0820135935060e0909101359150565b60008060008060008060c087890312156154ef57600080fd5b6154f8876151d1565b9550615506602088016151d1565b945060408701359350606087013592506080870135615524816152ef565b915061553260a088016151d1565b90509295509295509295565b60008082840361014081121561555357600080fd5b6101208082121561556357600080fd5b84935061113e8186016151d1565b6000806000806080858703121561558757600080fd5b615590856151d1565b935061559e602086016151d1565b92506040850135915060608501356155b5816152ef565b939692955090935050565b60008060008060008060008060006101208a8c0312156155df57600080fd5b6155e88a6151d1565b98506155f660208b016151d1565b975061560460408b016151d1565b965061561260608b016151d1565b955060808a0135945060a08a0135935060c08a013560ff8116811461563657600080fd5b8093505060e08a013591506101008a013590509295985092959850929598565b60008060008060008060a0878903121561566f57600080fd5b615678876151d1565b9550615686602088016151d1565b9450604087013567ffffffffffffffff8111156156a257600080fd5b6156ae89828a016152aa565b90955093505060608701356156c2816152ef565b9150615532608088016151d1565b600080600080608085870312156156e657600080fd5b6156ef856151d1565b93506156fd602086016151d1565b925061570b604086016151d1565b9150615719606086016151d1565b905092959194509250565b600080600080600060a0868803121561573c57600080fd5b615745866151d1565b9450615753602087016151d1565b935060408601359250606086013591506080860135615368816152ef565b6000806040838503121561578457600080fd5b505080516020909101519092909150565b6000825160005b818110156157b6576020818601810151858301520161579c565b818111156157c5576000828501525b509190910192915050565b6000602082840312156157e257600080fd5b8151613817816152ef565b61012081016001600160a01b0380615804856151d1565b16835280615814602086016151d1565b1660208401525060408301356040830152606083013560608301526080830135608083015260a083013560a083015260c083013560c083015260e083013560e083015261010080840135818401525092915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b610140810161593882856001600160a01b038082511683528060208301511660208401525060408101516040830152606081015160608301526080810151608083015260a081015160a083015260c081015160c083015260e081015160e08301526101008082015181840152505050565b6001600160a01b0383166101208301529392505050565b60006020828403121561596157600080fd5b5051919050565b61016081016159d982856001600160a01b038082511683528060208301511660208401525060408101516040830152606081015160608301526080810151608083015260a081015160a083015260c081015160c083015260e081015160e08301526101008082015181840152505050565b82516101208301526020830151610140830152939250505056fea2646970667358221220456bed48860bc84bb92cd2249b83edea27d1fd756bce6bf3b05e81a7e3a50e3464736f6c634300080a003300000000000000000000000047897ee61498d02b18794601ed3a71896a1ff894000000000000000000000000000000000000000000000000000000000000002800000000000000000000000000000000000000000000000000000000000aae60
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
00000000000000000000000047897ee61498d02b18794601ed3a71896a1ff894000000000000000000000000000000000000000000000000000000000000002800000000000000000000000000000000000000000000000000000000000aae60
-----Decoded View---------------
Arg [0] : governance (address): 0x47897ee61498d02b18794601ed3a71896a1ff894
Arg [1] : gasprice (uint256): 40
Arg [2] : gasmax (uint256): 700000
-----Encoded View---------------
3 Constructor Arguments found :
Arg [0] : 00000000000000000000000047897ee61498d02b18794601ed3a71896a1ff894
Arg [1] : 0000000000000000000000000000000000000000000000000000000000000028
Arg [2] : 00000000000000000000000000000000000000000000000000000000000aae60
Age | Block | Fee Address | BC Fee Address | Voting Power | Jailed | Incoming |
---|