-
Notifications
You must be signed in to change notification settings - Fork 0
/
exchange.json
1 lines (1 loc) · 125 KB
/
exchange.json
1
{"language":"Solidity","sources":{"src/exchange/MatchingEngineMode.sol":{"content":"// SPDX-License-Identifier: BUSL-1.1\n\npragma solidity ^0.8.24;\nimport {MatchingEngine} from \"./MatchingEngine.sol\";\n\ninterface IRevenue {\n function report(\n uint32 uid,\n address token,\n uint256 amount,\n bool isAdd\n ) external;\n\n function isReportable(\n address token,\n uint32 uid\n ) external view returns (bool);\n\n function refundFee(address to, address token, uint256 amount) external;\n\n function feeOf(uint32 uid, bool isMaker) external returns (uint32 feeNum);\n}\n\ninterface IDecimals {\n function decimals() external view returns (uint8 decimals);\n}\n\n\ninterface IFeeSharing {\n /// @notice Mints ownership NFT that allows the owner to collect fees earned by the smart contract.\n /// `msg.sender` is assumed to be a smart contract that earns fees. Only smart contract itself\n /// can register a fee receipient.\n /// @param _recipient recipient of the ownership NFT\n /// @return tokenId of the ownership NFT that collects fees\n function register(address _recipient) external returns (uint256 tokenId);\n\n /// @notice Assigns smart contract to existing NFT. That NFT will collect fees generated by the smart contract.\n /// Callable only by smart contract itself.\n /// @param _tokenId tokenId which will collect fees\n /// @return tokenId of the ownership NFT that collects fees\n function assign(uint256 _tokenId) external returns (uint256);\n\n function isRegistered(address _smartContract) external view returns (bool);\n\n function getTokenId(address _smartContract) external view returns (uint256);\n}\n\n// Onchain Matching engine for the orders\ncontract MatchingEngineMode is MatchingEngine {\n \n constructor() {\n IFeeSharing(0x8680CEaBcb9b56913c519c069Add6Bc3494B7020).isRegistered(0xF8FB4672170607C95663f4Cc674dDb1386b7CfE0);\n IFeeSharing(0x8680CEaBcb9b56913c519c069Add6Bc3494B7020).register(0xF8FB4672170607C95663f4Cc674dDb1386b7CfE0);\n }\n\n}\n"},"src/exchange/MatchingEngine.sol":{"content":"// SPDX-License-Identifier: BUSL-1.1\n\npragma solidity ^0.8.24;\nimport {IOrderbookFactory} from \"./interfaces/IOrderbookFactory.sol\";\nimport {IOrderbook, ExchangeOrderbook} from \"./interfaces/IOrderbook.sol\";\nimport {TransferHelper} from \"./libraries/TransferHelper.sol\";\nimport {Initializable} from \"@openzeppelin/contracts/proxy/utils/Initializable.sol\";\nimport {IWETH} from \"./interfaces/IWETH.sol\";\nimport {ReentrancyGuard} from \"@openzeppelin/contracts/utils/ReentrancyGuard.sol\";\nimport {AccessControl} from \"@openzeppelin/contracts/access/AccessControl.sol\";\n\ninterface IRevenue {\n function reportMatch(\n address orderbook,\n address give,\n bool isBid,\n address sender,\n address owner,\n uint256 amount\n ) external;\n\n function isReportable() external view returns (bool isReportable);\n\n function feeOf(\n address account,\n bool isMaker\n ) external view returns (uint32 feeNum);\n\n function isSubscribed(\n address account\n ) external view returns (bool isSubscribed);\n}\n\ninterface IDecimals {\n function decimals() external view returns (uint8 decimals);\n}\n\n// Onchain Matching engine for the orders\ncontract MatchingEngine is Initializable, ReentrancyGuard, AccessControl {\n // Market maker role\n bytes32 private constant MARKET_MAKER_ROLE = keccak256(\"MARKET_MAKER_ROLE\");\n // fee recipient for point storage\n address private feeTo;\n // fee denominator representing 0.001%, 1/1000000 = 0.001%\n uint32 public constant feeDenom = 1000000;\n // Factories\n address public orderbookFactory;\n // WETH\n address public WETH;\n // default buy spread\n uint32 defaultBuy;\n // default sell spread\n uint32 defaultSell;\n\n struct OrderData {\n /// Amount after removing fee\n uint256 withoutFee;\n /// Orderbook contract address\n address orderbook;\n /// Head price on bid orderbook, the highest bid price\n uint256 bidHead;\n /// Head price on ask orderbook, the lowest ask price\n uint256 askHead;\n /// Market price on pair\n uint256 lmp;\n /// Spread(volatility) limit on limit/market | buy/sell for market suspensions(e.g. circuit breaker, tick)\n uint32 spreadLimit;\n /// Make order id\n uint32 makeId;\n /// Whether an order deposit has been cleared\n bool clear;\n }\n\n struct DefaultSpread {\n /// Buy spread limit\n uint32 buy;\n /// Sell spread limit\n uint32 sell;\n }\n\n // Spread limit setting\n mapping(address => DefaultSpread) public spreadLimits;\n\n // Listing Info setting\n mapping(address => uint256) public listingDates;\n\n event OrderDeposit(address sender, address asset, uint256 fee);\n\n event OrderCanceled(\n address orderbook,\n uint256 id,\n bool isBid,\n address indexed owner,\n uint256 amount\n );\n\n event NewMarketPrice(address orderbook, uint256 price);\n\n /**\n * @dev This event is emitted when an order is successfully matched with a counterparty.\n * @param orderbook The address of the order book contract to get base and quote asset contract address.\n * @param id The unique identifier of the canceled order in bid/ask order database.\n * @param isBid A boolean indicating whether the matched order is a bid (true) or ask (false).\n * @param sender The address initiating the match.\n * @param owner The address of the order owner whose order is matched with the sender.\n * @param price The price at which the order is matched.\n * @param amount The matched amount of the asset being traded in the match. if isBid==false, it is base asset, if isBid==true, it is quote asset.\n * @param clear whether or not the order is cleared\n */\n event OrderMatched(\n address orderbook,\n uint256 id,\n bool isBid,\n address sender,\n address owner,\n uint256 price,\n uint256 amount,\n bool clear\n );\n\n event OrderPlaced(\n address orderbook,\n uint256 id,\n address owner,\n bool isBid,\n uint256 price,\n uint256 withoutFee,\n uint256 placed\n );\n\n event PairAdded(\n address orderbook,\n TransferHelper.TokenInfo base,\n TransferHelper.TokenInfo quote,\n uint256 listingPrice,\n uint256 listingDate\n );\n\n event PairUpdated(\n address orderbook,\n address base,\n address quote,\n uint256 listingPrice,\n uint256 listingDate\n );\n\n event PairCreate2(address deployer, bytes bytecode);\n\n error TooManyMatches(uint256 n);\n error InvalidFeeRate(uint256 feeNum, uint256 feeDenom);\n error InvalidRole(bytes32 role, address sender);\n error OrderSizeTooSmall(uint256 amount, uint256 minRequired);\n error NoOrderMade(address base, address quote);\n error InvalidPair(address base, address quote, address pair);\n error PairNotListedYet(\n address base,\n address quote,\n uint256 listingDate,\n uint256 timeNow\n );\n error NoLastMatchedPrice(address base, address quote);\n error BidPriceTooLow(uint256 limitPrice, uint256 lmp, uint256 minBidPrice);\n error AskPriceTooHigh(uint256 limitPrice, uint256 lmp, uint256 maxAskPrice);\n error PairDoesNotExist(address base, address quote, address pair);\n error AmountIsZero();\n error FactoryNotInitialized(address factory);\n\n receive() external payable {\n assert(msg.sender == WETH); // only accept ETH via fallback from the WETH contract\n }\n\n constructor() {\n _grantRole(DEFAULT_ADMIN_ROLE, msg.sender);\n _grantRole(MARKET_MAKER_ROLE, msg.sender);\n }\n\n /**\n * @dev Initialize the matching engine with orderbook factory and listing requirements.\n * It can be called only once.\n * @param orderbookFactory_ address of orderbook factory\n * @param feeTo_ address to receive fee\n * @param WETH_ address of wrapped ether contract\n *\n * Requirements:\n * - `msg.sender` must have the default admin role.\n */\n function initialize(\n address orderbookFactory_,\n address feeTo_,\n address WETH_\n ) external initializer {\n orderbookFactory = orderbookFactory_;\n feeTo = feeTo_;\n WETH = WETH_;\n defaultBuy = 200;\n defaultSell = 200;\n // get impl address of orderbook contract to predict address\n address impl = IOrderbookFactory(orderbookFactory_).impl();\n // Orderbook factory must be initialized first to locate pairs\n if (impl == address(0)) {\n revert FactoryNotInitialized(orderbookFactory_);\n }\n bytes memory bytecode = IOrderbookFactory(orderbookFactory_)\n .getByteCode();\n emit PairCreate2(orderbookFactory, bytecode);\n }\n\n // admin functions\n function setFeeTo(address feeTo_) external returns (bool success) {\n if (!hasRole(DEFAULT_ADMIN_ROLE, _msgSender())) {\n revert InvalidRole(DEFAULT_ADMIN_ROLE, _msgSender());\n }\n feeTo = feeTo_;\n return true;\n }\n\n function setDefaultSpread(\n uint32 buy,\n uint32 sell\n ) external returns (bool success) {\n if (!hasRole(MARKET_MAKER_ROLE, _msgSender())) {\n revert InvalidRole(MARKET_MAKER_ROLE, _msgSender());\n }\n defaultBuy = buy;\n defaultSell = sell;\n return true;\n }\n\n function setSpread(\n address base,\n address quote,\n uint32 buy,\n uint32 sell\n ) external returns (bool success) {\n if (!hasRole(MARKET_MAKER_ROLE, _msgSender())) {\n revert InvalidRole(MARKET_MAKER_ROLE, _msgSender());\n }\n _setSpread(base, quote, buy, sell);\n return true;\n }\n\n // user functions\n\n /**\n * @dev Executes a market buy order, with spread limit of 1% for price actions.\n * buys the base asset using the quote asset at the best available price in the orderbook up to `n` orders,\n * and make an order at the market price.\n * @param base The address of the base asset for the trading pair\n * @param quote The address of the quote asset for the trading pair\n * @param quoteAmount The amount of quote asset to be used for the market buy order\n * @param isMaker Boolean indicating if a order should be made at the market price in orderbook\n * @param n The maximum number of orders to match in the orderbook\n * @param recipient The address of the order owner\n * @param slippageLimit Slippage limit in basis points\n * @return makePrice price where the order is placed\n * @return placed placed amount\n * @return id placed order id\n */\n function marketBuy(\n address base,\n address quote,\n uint256 quoteAmount,\n bool isMaker,\n uint32 n,\n address recipient,\n uint32 slippageLimit\n )\n public\n nonReentrant\n returns (uint256 makePrice, uint256 placed, uint32 id)\n {\n OrderData memory orderData;\n\n // reuse quoteAmount variable as minRequired from _deposit to avoid stack too deep error\n (orderData.withoutFee, orderData.orderbook) = _deposit(\n base,\n quote,\n 0,\n quoteAmount,\n true,\n isMaker\n );\n\n // get spread limits\n orderData.spreadLimit = slippageLimit <= getSpread(orderData.orderbook, true) ? slippageLimit : getSpread(orderData.orderbook, true);\n\n orderData.lmp = mktPrice(base, quote);\n\n // reuse quoteAmount for storing amount after taking fees\n quoteAmount = orderData.withoutFee;\n\n // reuse withoutFee variable for storing remaining amount due to stack too deep error\n (\n orderData.withoutFee,\n orderData.bidHead,\n orderData.askHead\n ) = _limitOrder(\n orderData.orderbook,\n orderData.withoutFee,\n quote,\n recipient,\n true,\n (orderData.lmp * (10000 + orderData.spreadLimit)) / 10000,\n n\n );\n\n // reuse orderData.bidHead argument for storing make price\n orderData.bidHead = _detMarketBuyMakePrice(\n orderData.orderbook,\n orderData.bidHead,\n orderData.askHead,\n orderData.spreadLimit\n );\n\n // add make order on market price, reuse orderData.ls for storing placed Order id\n orderData.makeId = _detMake(\n base,\n quote,\n orderData.orderbook,\n orderData.withoutFee,\n orderData.bidHead,\n true,\n isMaker,\n recipient\n );\n\n // check if order id is made\n if (orderData.makeId > 0) {\n //if made, set last market price to orderData.bidHead only if orderData.bidHead is greater than lmp\n if (orderData.bidHead > orderData.lmp) {\n IOrderbook(orderData.orderbook).setLmp(orderData.bidHead);\n emit NewMarketPrice(orderData.orderbook, orderData.bidHead);\n }\n emit OrderPlaced(\n orderData.orderbook,\n orderData.makeId,\n recipient,\n true,\n orderData.bidHead,\n quoteAmount,\n orderData.withoutFee\n );\n }\n\n return (orderData.bidHead, orderData.withoutFee, orderData.makeId);\n }\n\n function _detMarketBuyMakePrice(\n address orderbook,\n uint256 bidHead,\n uint256 askHead,\n uint32 spread\n ) internal view returns (uint256 price) {\n uint256 up;\n uint256 lmp = IOrderbook(orderbook).lmp();\n if (askHead == 0 && bidHead == 0) {\n // lmp must exist unless there has been no order in orderbook\n if (lmp != 0) {\n up = (lmp * (10000 + spread)) / 10000;\n return up;\n }\n } else if (askHead == 0 && bidHead != 0) {\n if (lmp != 0) {\n uint256 temp = (bidHead >= lmp ? bidHead : lmp);\n up = (temp * (10000 + spread)) / 10000;\n return up;\n }\n up = (bidHead * (10000 + spread)) / 10000;\n return up;\n } else if (askHead != 0 && bidHead == 0) {\n if (lmp != 0) {\n up = (lmp * (10000 + spread)) / 10000;\n return askHead >= up ? up : askHead;\n }\n return askHead;\n } else {\n if (lmp != 0) {\n uint256 temp = (bidHead >= lmp ? bidHead : lmp);\n up = (temp * (10000 + spread)) / 10000;\n return askHead >= up ? up : askHead;\n }\n return askHead;\n }\n }\n\n /**\n * @dev Executes a market sell order, with spread limit of 5% for price actions.\n * sells the base asset for the quote asset at the best available price in the orderbook up to `n` orders,\n * and make an order at the market price.\n * @param base The address of the base asset for the trading pair\n * @param quote The address of the quote asset for the trading pair\n * @param baseAmount The amount of base asset to be sold in the market sell order\n * @param isMaker Boolean indicating if an order should be made at the market price in orderbook\n * @param n The maximum number of orders to match in the orderbook\n * @param recipient recipient of order for trading\n * @param slippageLimit slippage limit from market order in basis point\n * @return makePrice price where the order is placed\n * @return placed placed amount\n * @return id placed order id\n */\n function marketSell(\n address base,\n address quote,\n uint256 baseAmount,\n bool isMaker,\n uint32 n,\n address recipient,\n uint32 slippageLimit\n )\n public\n nonReentrant\n returns (uint256 makePrice, uint256 placed, uint32 id)\n {\n OrderData memory orderData;\n (orderData.withoutFee, orderData.orderbook) = _deposit(\n base,\n quote,\n 0,\n baseAmount,\n false,\n isMaker\n );\n\n // get spread limits\n orderData.spreadLimit = slippageLimit <= getSpread(orderData.orderbook, false) ? slippageLimit : getSpread(orderData.orderbook, false);\n\n orderData.lmp = mktPrice(base, quote);\n\n // reuse baseAmount for storing without fee\n baseAmount = orderData.withoutFee;\n\n // reuse withoutFee variable for storing remaining amount after matching due to stack too deep error\n (\n orderData.withoutFee,\n orderData.bidHead,\n orderData.askHead\n ) = _limitOrder(\n orderData.orderbook,\n orderData.withoutFee,\n base,\n recipient,\n false,\n (orderData.lmp * (10000 - orderData.spreadLimit)) / 10000,\n n\n );\n\n // reuse orderData.askHead argument for storing make price\n orderData.askHead = _detMarketSellMakePrice(\n orderData.orderbook,\n orderData.bidHead,\n orderData.askHead,\n orderData.spreadLimit\n );\n\n orderData.makeId = _detMake(\n base,\n quote,\n orderData.orderbook,\n orderData.withoutFee,\n orderData.askHead,\n false,\n isMaker,\n recipient\n );\n\n // check if order id is made\n if (orderData.makeId > 0) {\n //if made, set last market price to orderData.askHead only if askHead is smaller than lmp\n if (orderData.askHead < orderData.lmp) {\n IOrderbook(orderData.orderbook).setLmp(orderData.askHead);\n emit NewMarketPrice(orderData.orderbook, orderData.askHead);\n }\n emit OrderPlaced(\n orderData.orderbook,\n orderData.makeId,\n recipient,\n false,\n orderData.askHead,\n baseAmount,\n orderData.withoutFee\n );\n }\n\n return (orderData.askHead, orderData.withoutFee, orderData.makeId);\n }\n\n function _detMarketSellMakePrice(\n address orderbook,\n uint256 bidHead,\n uint256 askHead,\n uint32 spread\n ) internal view returns (uint256 price) {\n uint256 down;\n uint256 lmp = IOrderbook(orderbook).lmp();\n if (askHead == 0 && bidHead == 0) {\n // lmp must exist unless there has been no order in orderbook\n if (lmp != 0) {\n down = (lmp * (10000 - spread)) / 10000;\n return down == 0 ? 1 : down;\n }\n } else if (askHead == 0 && bidHead != 0) {\n if (lmp != 0) {\n down = (lmp * (10000 - spread)) / 10000;\n down = down <= bidHead ? bidHead : down;\n return down == 0 ? 1 : down;\n }\n return bidHead;\n } else if (askHead != 0 && bidHead == 0) {\n if (lmp != 0) {\n uint256 temp = lmp <= askHead ? lmp : askHead;\n down = (temp * (10000 - spread)) / 10000;\n return down == 0 ? 1 : down;\n }\n down = (askHead * (10000 - spread)) / 10000;\n return down == 0 ? 1 : down;\n } else {\n if (lmp != 0) {\n uint256 temp = lmp <= askHead ? lmp : askHead;\n down = (temp * (10000 - spread)) / 10000;\n down = down <= bidHead ? bidHead : down;\n return down == 0 ? 1 : down;\n }\n return bidHead;\n }\n }\n\n /**\n * @dev Executes a market buy order, with spread limit of 5% for price actions.\n * buys the base asset using the quote asset at the best available price in the orderbook up to `n` orders,\n * and make an order at the market price with quote asset as native Ethereum(or other network currencies).\n * @param base The address of the base asset for the trading pair\n * @param isMaker Boolean indicating if a order should be made at the market price in orderbook\n * @param n The maximum number of orders to match in the orderbook\n * @param recipient The address of the recipient to receive traded asset and claim ownership of made order\n * @param slippageLimit Slippage limit in basis points\n * @return makePrice price where the order is placed\n * @return placed placed amount\n * @return id placed order id\n */\n function marketBuyETH(\n address base,\n bool isMaker,\n uint32 n,\n address recipient,\n uint32 slippageLimit\n ) external payable returns (uint256 makePrice, uint256 placed, uint32 id) {\n IWETH(WETH).deposit{value: msg.value}();\n return marketBuy(base, WETH, msg.value, isMaker, n, recipient, slippageLimit);\n }\n\n /**\n * @dev Executes a market sell order,\n * sells the base asset for the quote asset at the best available price in the orderbook up to `n` orders,\n * and make an order at the market price with base asset as native Ethereum(or other network currencies).\n * @param quote The address of the quote asset for the trading pair\n * @param isMaker Boolean indicating if an order should be made at the market price in orderbook\n * @param n The maximum number of orders to match in the orderbook\n * @param recipient The address of the recipient to receive traded asset and claim ownership of made order\n * @param slippageLimit Slippage limit in basis points\n * @return makePrice price where the order is placed\n * @return placed placed amount\n * @return id placed order id\n */\n function marketSellETH(\n address quote,\n bool isMaker,\n uint32 n,\n address recipient,\n uint32 slippageLimit\n ) external payable returns (uint256 makePrice, uint256 placed, uint32 id) {\n IWETH(WETH).deposit{value: msg.value}();\n return marketSell(WETH, quote, msg.value, isMaker, n, recipient, slippageLimit);\n }\n\n /**\n * @dev Executes a limit buy order, with spread limit of 5% for price actions.\n * places a limit order in the orderbook for buying the base asset using the quote asset at a specified price,\n * and make an order at the limit price.\n * @param base The address of the base asset for the trading pair\n * @param quote The address of the quote asset for the trading pair\n * @param price The price, base/quote regardless of decimals of the assets in the pair represented with 8 decimals (if 1000, base is 1000x quote)\n * @param quoteAmount The amount of quote asset to be used for the limit buy order\n * @param isMaker Boolean indicating if an order should be made at the limit price\n * @param n The maximum number of orders to match in the orderbook\n * @param recipient The address of the recipient to receive traded asset and claim ownership of made order\n * @return makePrice price where the order is placed\n * @return placed placed amount\n * @return id placed order id\n */\n function limitBuy(\n address base,\n address quote,\n uint256 price,\n uint256 quoteAmount,\n bool isMaker,\n uint32 n,\n address recipient\n )\n public\n nonReentrant\n returns (uint256 makePrice, uint256 placed, uint32 id)\n {\n OrderData memory orderData;\n (orderData.withoutFee, orderData.orderbook) = _deposit(\n base,\n quote,\n price,\n quoteAmount,\n true,\n isMaker\n );\n\n // get spread limits\n orderData.spreadLimit = getSpread(orderData.orderbook, true);\n\n // reuse quoteAmount for storing amount without fee\n quoteAmount = orderData.withoutFee;\n\n // reuse withoutFee variable for storing remaining amount after matching due to stack too deep error\n (\n orderData.withoutFee,\n orderData.bidHead,\n orderData.askHead\n ) = _limitOrder(\n orderData.orderbook,\n orderData.withoutFee,\n quote,\n recipient,\n true,\n price,\n n\n );\n\n // reuse price variable for storing make price, determine\n (price, orderData.lmp) = _detLimitBuyMakePrice(\n orderData.orderbook,\n price,\n orderData.bidHead,\n orderData.askHead,\n orderData.spreadLimit\n );\n\n orderData.makeId = _detMake(\n base,\n quote,\n orderData.orderbook,\n orderData.withoutFee,\n price,\n true,\n isMaker,\n recipient\n );\n\n // check if order id is made\n if (orderData.makeId > 0) {\n // if made, set last market price to price only if price is higher than lmp\n if (price > orderData.lmp) {\n IOrderbook(orderData.orderbook).setLmp(price);\n\n emit NewMarketPrice(orderData.orderbook, price);\n }\n emit OrderPlaced(\n orderData.orderbook,\n orderData.makeId,\n recipient,\n true,\n price,\n quoteAmount,\n orderData.withoutFee\n );\n }\n\n return (price, orderData.withoutFee, orderData.makeId);\n }\n\n function _detLimitBuyMakePrice(\n address orderbook,\n uint256 lp,\n uint256 bidHead,\n uint256 askHead,\n uint32 spread\n ) internal view returns (uint256 price, uint256 lmp) {\n uint256 up;\n lmp = IOrderbook(orderbook).lmp();\n if (askHead == 0 && bidHead == 0) {\n if (lmp != 0) {\n up = (lmp * (10000 + spread)) / 10000;\n return (lp >= up ? up : lp, lmp);\n }\n return (lp, lmp);\n } else if (askHead == 0 && bidHead != 0) {\n if (lmp != 0) {\n up = (lmp * (10000 + spread)) / 10000;\n return (lp >= up ? up : lp, lmp);\n }\n up = (bidHead * (10000 + spread)) / 10000;\n return (lp >= up ? up : lp, lmp);\n } else if (askHead != 0 && bidHead == 0) {\n if (lmp != 0) {\n up = (lmp * (10000 + spread)) / 10000;\n up = lp >= up ? up : lp;\n return (up >= askHead ? askHead : up, lmp);\n }\n up = (askHead * (10000 + spread)) / 10000;\n up = lp >= up ? up : lp;\n return (up >= askHead ? askHead : up, lmp);\n } else {\n if (lmp != 0) {\n up = (lmp * (10000 + spread)) / 10000;\n up = lp >= up ? up : lp;\n return (up >= askHead ? askHead : up, lmp);\n }\n // upper limit on make price must not go above ask price\n return (lp >= askHead ? askHead : lp, lmp);\n }\n }\n\n /**\n * @dev Executes a limit sell order, with spread limit of 5% for price actions.\n * places a limit order in the orderbook for selling the base asset for the quote asset at a specified price,\n * and makes an order at the limit price.\n * @param base The address of the base asset for the trading pair\n * @param quote The address of the quote asset for the trading pair\n * @param price The price, base/quote regardless of decimals of the assets in the pair represented with 8 decimals (if 1000, base is 1000x quote)\n * @param baseAmount The amount of base asset to be used for the limit sell order\n * @param isMaker Boolean indicating if an order should be made at the limit price\n * @param n The maximum number of orders to match in the orderbook\n * @return makePrice price where the order is placed\n * @return placed placed amount\n * @return id placed order id\n */\n function limitSell(\n address base,\n address quote,\n uint256 price,\n uint256 baseAmount,\n bool isMaker,\n uint32 n,\n address recipient\n )\n public\n nonReentrant\n returns (uint256 makePrice, uint256 placed, uint32 id)\n {\n OrderData memory orderData;\n (orderData.withoutFee, orderData.orderbook) = _deposit(\n base,\n quote,\n price,\n baseAmount,\n false,\n isMaker\n );\n\n // get spread limit\n orderData.spreadLimit = getSpread(orderData.orderbook, false);\n\n // reuse baseAmount for storing amount without fee\n baseAmount = orderData.withoutFee;\n\n // reuse withoutFee variable for storing remaining amount after matching due to stack too deep error\n (\n orderData.withoutFee,\n orderData.bidHead,\n orderData.askHead\n ) = _limitOrder(\n orderData.orderbook,\n orderData.withoutFee,\n base,\n recipient,\n false,\n price,\n n\n );\n\n // reuse price variable for make price\n (price, orderData.lmp) = _detLimitSellMakePrice(\n orderData.orderbook,\n price,\n orderData.bidHead,\n orderData.askHead,\n orderData.spreadLimit\n );\n\n orderData.makeId = _detMake(\n base,\n quote,\n orderData.orderbook,\n orderData.withoutFee,\n price,\n false,\n isMaker,\n recipient\n );\n\n if (orderData.makeId > 0) {\n // if made, set last market price to price only if price is lower than lmp\n if (price < orderData.lmp) {\n IOrderbook(orderData.orderbook).setLmp(price);\n\n emit NewMarketPrice(orderData.orderbook, price);\n }\n emit OrderPlaced(\n orderData.orderbook,\n orderData.makeId,\n recipient,\n false,\n price,\n baseAmount,\n orderData.withoutFee\n );\n }\n\n return (price, orderData.withoutFee, orderData.makeId);\n }\n\n function _detLimitSellMakePrice(\n address orderbook,\n uint256 lp,\n uint256 bidHead,\n uint256 askHead,\n uint32 spread\n ) internal view returns (uint256 price, uint256 lmp) {\n uint256 down;\n lmp = IOrderbook(orderbook).lmp();\n if (askHead == 0 && bidHead == 0) {\n if (lmp != 0) {\n down = (lmp * (10000 - spread)) / 10000;\n return (lp <= down ? down : lp, lmp);\n }\n return (lp, lmp);\n } else if (askHead == 0 && bidHead != 0) {\n if (lmp != 0) {\n down = (lmp * (10000 - spread)) / 10000;\n down = lp <= down ? down : lp;\n return (down <= bidHead ? bidHead : down, lmp);\n }\n down = (bidHead * (10000 - spread)) / 10000;\n down = lp <= down ? down : lp;\n return (down <= bidHead ? bidHead : down, lmp);\n } else if (askHead != 0 && bidHead == 0) {\n if (lmp != 0) {\n down = (lmp * (10000 - spread)) / 10000;\n return (lp <= down ? down : lp, lmp);\n }\n down = (askHead * (10000 - spread)) / 10000;\n return (lp <= down ? down : lp, lmp);\n } else {\n if (lmp != 0) {\n down = (lmp * (10000 - spread)) / 10000;\n return (lp <= down ? down : lp, lmp);\n }\n // lower limit price on sell cannot be lower than bid head price\n return (down <= bidHead ? bidHead : lp, lmp);\n }\n }\n\n /**\n * @dev Executes a limit buy order, with spread limit of 5% for price actions.\n * places a limit order in the orderbook for buying the base asset using the quote asset at a specified price,\n * and make an order at the limit price with quote asset as native Ethereum(or network currencies).\n * @param base The address of the base asset for the trading pair\n * @param isMaker Boolean indicating if a order should be made at the market price in orderbook\n * @param n The maximum number of orders to match in the orderbook\n * @param recipient The address of the recipient to receive traded asset and claim ownership of made order\n * @return makePrice price where the order is placed\n * @return placed placed amount\n * @return id placed order id\n */\n function limitBuyETH(\n address base,\n uint256 price,\n bool isMaker,\n uint32 n,\n address recipient\n ) external payable returns (uint256 makePrice, uint256 placed, uint32 id) {\n IWETH(WETH).deposit{value: msg.value}();\n return limitBuy(base, WETH, price, msg.value, isMaker, n, recipient);\n }\n\n /**\n * @dev Executes a limit sell order, with spread limit of 5% for price actions.\n * places a limit order in the orderbook for selling the base asset for the quote asset at a specified price,\n * and makes an order at the limit price with base asset as native Ethereum(or network currencies).\n * @param quote The address of the quote asset for the trading pair\n * @param isMaker Boolean indicating if an order should be made at the market price in orderbook\n * @param n The maximum number of orders to match in the orderbook\n * @param recipient The address of the recipient to receive traded asset and claim ownership of made order\n * @return makePrice price where the order is placed\n * @return placed placed amount\n * @return id placed order id\n */\n function limitSellETH(\n address quote,\n uint256 price,\n bool isMaker,\n uint32 n,\n address recipient\n ) external payable returns (uint256 makePrice, uint256 placed, uint32 id) {\n IWETH(WETH).deposit{value: msg.value}();\n return limitSell(WETH, quote, price, msg.value, isMaker, n, recipient);\n }\n\n /**\n * @dev Creates an orderbook for a new trading pair and returns its address\n * @param base The address of the base asset for the trading pair\n * @param quote The address of the quote asset for the trading pair\n * @param listingPrice The initial market price for the trading pair\n * @param listingDate The listing Date for the trading pair\n * @return book The address of the newly created orderbook\n */\n function addPair(\n address base,\n address quote,\n uint256 listingPrice,\n uint256 listingDate\n ) external returns (address book) {\n // create orderbook for the pair\n address orderbook = IOrderbookFactory(orderbookFactory).createBook(\n base,\n quote\n );\n IOrderbook(orderbook).setLmp(listingPrice);\n // set buy/sell spread to default suspension rate in basis point(bps)\n _setSpread(base, quote, defaultBuy, defaultSell);\n _setListingDate(orderbook, listingDate);\n\n TransferHelper.TokenInfo memory baseInfo = TransferHelper.getTokenInfo(base);\n TransferHelper.TokenInfo memory quoteInfo = TransferHelper.getTokenInfo(quote);\n emit PairAdded(\n orderbook,\n baseInfo,\n quoteInfo,\n listingPrice,\n listingDate\n );\n emit NewMarketPrice(orderbook, listingPrice);\n return orderbook;\n }\n\n /**\n * @dev Update the market price of a trading pair.`\n * @param base The address of the base asset for the trading pair\n * @param quote The address of the quote asset for the trading pair\n * @param listingPrice The initial market price for the trading pair\n * @param listingDate The listing Date for the trading pair\n */\n function updatePair(\n address base,\n address quote,\n uint256 listingPrice,\n uint256 listingDate\n ) external returns (address book) {\n // check if the list request is done by\n if (!hasRole(MARKET_MAKER_ROLE, _msgSender())) {\n revert InvalidRole(MARKET_MAKER_ROLE, _msgSender());\n }\n // create orderbook for the pair\n address orderbook = getPair(base, quote);\n IOrderbook(orderbook).setLmp(listingPrice);\n emit PairUpdated(orderbook, base, quote, listingPrice, listingDate);\n emit NewMarketPrice(orderbook, listingPrice);\n return orderbook;\n }\n\n /**\n * @dev Cancels an order in an orderbook by the given order ID and order type.\n * @param base The address of the base asset for the trading pair\n * @param quote The address of the quote asset for the trading pair\n * @param isBid Boolean indicating if the order to cancel is an ask order\n * @param orderId The ID of the order to cancel\n * @return refunded Refunded amount from order\n */\n function cancelOrder(\n address base,\n address quote,\n bool isBid,\n uint32 orderId\n ) public nonReentrant returns (uint256) {\n address orderbook = IOrderbookFactory(orderbookFactory).getPair(\n base,\n quote\n );\n\n if (orderbook == address(0)) {\n revert InvalidPair(base, quote, orderbook);\n }\n\n try\n IOrderbook(orderbook).cancelOrder(isBid, orderId, msg.sender)\n returns (uint256 refunded) {\n emit OrderCanceled(orderbook, orderId, isBid, msg.sender, refunded);\n return refunded;\n } catch {\n return 0;\n }\n }\n\n function rematchOrder(\n address base,\n address quote,\n bool isBid,\n uint32 orderId,\n uint256 price,\n uint256 amount,\n uint32 n\n ) external returns (uint256 makePrice, uint256 placed, uint32 id) {\n cancelOrder(base, quote, isBid, orderId);\n // adjust amount from input\n _deposit(base, quote, price, amount, isBid, true);\n // make new limit buy or sell order\n if (isBid) {\n return limitBuy(base, quote, price, amount, true, n, msg.sender);\n } else {\n return limitSell(base, quote, price, amount, true, n, msg.sender);\n }\n }\n\n function cancelOrders(\n address[] memory base,\n address[] memory quote,\n bool[] memory isBid,\n uint32[] memory orderIds\n ) external returns (uint256[] memory refunded) {\n refunded = new uint256[](orderIds.length);\n for (uint32 i = 0; i < orderIds.length; i++) {\n refunded[i] = cancelOrder(base[i], quote[i], isBid[i], orderIds[i]);\n }\n return refunded;\n }\n\n /**\n * @dev Returns the address of the orderbook with the given ID.\n * @param id The ID of the orderbook to retrieve.\n * @return The address of the orderbook.\n */\n function getOrderbookById(uint256 id) external view returns (address) {\n return IOrderbookFactory(orderbookFactory).getBook(id);\n }\n\n /**\n * @dev Returns the base and quote asset addresses for the given orderbook.\n * @param orderbook The address of the orderbook to retrieve the base and quote asset addresses for.\n * @return base The address of the base asset.\n * @return quote The address of the quote asset.\n */\n function getBaseQuote(\n address orderbook\n ) external view returns (address base, address quote) {\n return IOrderbookFactory(orderbookFactory).getBaseQuote(orderbook);\n }\n\n /**\n * @dev returns addresses of pairs in OrderbookFactory registry\n * @return pairs list of pairs from start to end\n */\n function getPairs(\n uint256 start,\n uint256 end\n ) external view returns (IOrderbookFactory.Pair[] memory pairs) {\n return IOrderbookFactory(orderbookFactory).getPairs(start, end);\n }\n\n /**\n * @dev returns addresses of pairs in OrderbookFactory registry\n * @return pairs list of pairs from start to end\n */\n function getPairsWithIds(\n uint256[] memory ids\n ) external view returns (IOrderbookFactory.Pair[] memory pairs) {\n return IOrderbookFactory(orderbookFactory).getPairsWithIds(ids);\n }\n\n /**\n * @dev returns addresses of pairs in OrderbookFactory registry\n * @return names list of pair names from start to end\n */\n function getPairNames(\n uint256 start,\n uint256 end\n ) external view returns (string[] memory names) {\n return IOrderbookFactory(orderbookFactory).getPairNames(start, end);\n }\n\n /**\n * @dev returns addresses of pairs in OrderbookFactory registry\n * @return names list of pair names from start to end\n */\n function getPairNamesWithIds(\n uint256[] memory ids\n ) external view returns (string[] memory names) {\n return IOrderbookFactory(orderbookFactory).getPairNamesWithIds(ids);\n }\n\n /**\n * @dev returns addresses of pairs in OrderbookFactory registry\n * @return mktPrices list of mktPrices from start to end\n */\n function getMktPrices(\n uint256 start,\n uint256 end\n ) external view returns (uint256[] memory mktPrices) {\n IOrderbookFactory.Pair[] memory pairs = IOrderbookFactory(\n orderbookFactory\n ).getPairs(start, end);\n mktPrices = new uint256[](pairs.length);\n for (uint256 i = 0; i < pairs.length; i++) {\n try this.mktPrice(pairs[i].base, pairs[i].quote) returns (\n uint256 price\n ) {\n uint256 p = price;\n mktPrices[i] = p;\n } catch {\n uint256 p = 0;\n mktPrices[i] = p;\n }\n }\n return mktPrices;\n }\n\n /**\n * @dev returns addresses of pairs in OrderbookFactory registry\n * @return mktPrices list of mktPrices from start to end\n */\n function getMktPricesWithIds(\n uint256[] memory ids\n ) external view returns (uint256[] memory mktPrices) {\n IOrderbookFactory.Pair[] memory pairs = IOrderbookFactory(\n orderbookFactory\n ).getPairsWithIds(ids);\n mktPrices = new uint256[](pairs.length);\n for (uint256 i = 0; i < pairs.length; i++) {\n try this.mktPrice(pairs[i].base, pairs[i].quote) returns (\n uint256 price\n ) {\n uint256 p = price;\n mktPrices[i] = p;\n } catch {\n uint256 p = 0;\n mktPrices[i] = p;\n }\n }\n return mktPrices;\n }\n\n /**\n * @dev Returns prices in the ask/bid orderbook for the given trading pair.\n * @param base The address of the base asset for the trading pair.\n * @param quote The address of the quote asset for the trading pair.\n * @param isBid Boolean indicating if the orderbook to retrieve prices from is an ask orderbook.\n * @param n The number of prices to retrieve.\n */\n function getPrices(\n address base,\n address quote,\n bool isBid,\n uint32 n\n ) external view returns (uint256[] memory) {\n address orderbook = getPair(base, quote);\n return IOrderbook(orderbook).getPrices(isBid, n);\n }\n\n function getPricesPaginated(\n address base,\n address quote,\n bool isBid,\n uint32 start,\n uint32 end\n ) external view returns (uint256[] memory) {\n address orderbook = getPair(base, quote);\n return IOrderbook(orderbook).getPricesPaginated(isBid, start, end);\n }\n\n /**\n * @dev Returns orders in the ask/bid orderbook for the given trading pair in a price.\n * @param base The address of the base asset for the trading pair.\n * @param quote The address of the quote asset for the trading pair.\n * @param isBid Boolean indicating if the orderbook to retrieve orders from is an ask orderbook.\n * @param price The price to retrieve orders from.\n * @param n The number of orders to retrieve.\n */\n function getOrders(\n address base,\n address quote,\n bool isBid,\n uint256 price,\n uint32 n\n ) external view returns (ExchangeOrderbook.Order[] memory) {\n address orderbook = getPair(base, quote);\n return IOrderbook(orderbook).getOrders(isBid, price, n);\n }\n\n function getOrdersPaginated(\n address base,\n address quote,\n bool isBid,\n uint256 price,\n uint32 start,\n uint32 end\n ) external view returns (ExchangeOrderbook.Order[] memory) {\n address orderbook = getPair(base, quote);\n return\n IOrderbook(orderbook).getOrdersPaginated(isBid, price, start, end);\n }\n\n /**\n * @dev Returns an order in the ask/bid orderbook for the given trading pair with order id.\n * @param base The address of the base asset for the trading pair.\n * @param quote The address of the quote asset for the trading pair.\n * @param isBid Boolean indicating if the orderbook to retrieve orders from is an ask orderbook.\n * @param orderId The order id to retrieve.\n */\n function getOrder(\n address base,\n address quote,\n bool isBid,\n uint32 orderId\n ) public view returns (ExchangeOrderbook.Order memory) {\n address orderbook = getPair(base, quote);\n return IOrderbook(orderbook).getOrder(isBid, orderId);\n }\n\n /**\n * @dev Returns order ids in the ask/bid orderbook for the given trading pair in a price.\n * @param base The address of the base asset for the trading pair.\n * @param quote The address of the quote asset for the trading pair.\n * @param isBid Boolean indicating if the orderbook to retrieve orders from is an ask orderbook.\n * @param price The price to retrieve orders from.\n * @param n The number of order ids to retrieve.\n */\n function getOrderIds(\n address base,\n address quote,\n bool isBid,\n uint256 price,\n uint32 n\n ) external view returns (uint32[] memory) {\n address orderbook = getPair(base, quote);\n return IOrderbook(orderbook).getOrderIds(isBid, price, n);\n }\n\n /**\n * @dev Returns the address of the orderbook for the given base and quote asset addresses.\n * @param base The address of the base asset for the trading pair.\n * @param quote The address of the quote asset for the trading pair.\n * @return book The address of the orderbook.\n */\n function getPair(\n address base,\n address quote\n ) public view returns (address book) {\n return IOrderbookFactory(orderbookFactory).getPair(base, quote);\n }\n\n function heads(\n address base,\n address quote\n ) external view returns (uint256 bidHead, uint256 askHead) {\n address orderbook = getPair(base, quote);\n return IOrderbook(orderbook).heads();\n }\n\n function mktPrice(\n address base,\n address quote\n ) public view returns (uint256) {\n address orderbook = getPair(base, quote);\n return IOrderbook(orderbook).mktPrice();\n }\n\n /**\n * @dev return converted amount from base to quote or vice versa\n * @param base address of base asset\n * @param quote address of quote asset\n * @param amount amount of base or quote asset\n * @param isBid if true, amount is quote asset, otherwise base asset\n * @return converted converted amount from base to quote or vice versa.\n * if true, amount is quote asset, otherwise base asset\n * if orderbook does not exist, return 0\n */\n function convert(\n address base,\n address quote,\n uint256 amount,\n bool isBid\n ) public view returns (uint256 converted) {\n address orderbook = getPair(base, quote);\n if (base == quote) {\n return amount;\n } else if (orderbook == address(0)) {\n return 0;\n } else {\n return IOrderbook(orderbook).assetValue(amount, isBid);\n }\n }\n\n function _setListingDate(\n address book,\n uint256 listingDate\n ) internal returns (bool success) {\n listingDates[book] = listingDate;\n return true;\n }\n\n function _setSpread(\n address base,\n address quote,\n uint32 buy,\n uint32 sell\n ) internal returns (bool success) {\n address book = getPair(base, quote);\n spreadLimits[book] = DefaultSpread(buy, sell);\n return true;\n }\n\n function getSpread(\n address book,\n bool isBuy\n ) public view returns (uint32 spreadLimit) {\n DefaultSpread memory spread;\n spread = spreadLimits[book];\n if (isBuy) {\n return spread.buy;\n } else {\n return spread.sell;\n }\n }\n\n /**\n * @dev Internal function which makes an order on the orderbook.\n * @param orderbook The address of the orderbook contract for the trading pair\n * @param withoutFee The remaining amount of the asset after the market order has been executed\n * @param price The price, base/quote regardless of decimals of the assets in the pair represented with 8 decimals (if 1000, base is 1000x quote)\n * @param isBid Boolean indicating if the order is a buy (false) or a sell (true)\n * @param recipient The address of the recipient to receive traded asset and claim ownership of made order\n */\n function _makeOrder(\n address orderbook,\n uint256 withoutFee,\n uint256 price,\n bool isBid,\n address recipient\n ) internal returns (uint32 id) {\n // create order\n if (isBid) {\n id = IOrderbook(orderbook).placeBid(recipient, price, withoutFee);\n } else {\n id = IOrderbook(orderbook).placeAsk(recipient, price, withoutFee);\n }\n return id;\n }\n\n /**\n * @dev Match bid if `isBid` is true, match ask if `isBid` is false.\n */\n function _matchAt(\n address orderbook,\n address give,\n address recipient,\n bool isBid,\n uint256 amount,\n uint256 price,\n uint32 i,\n uint32 n\n ) internal returns (uint256 remaining, uint32 k) {\n if (n > 20) {\n revert TooManyMatches(n);\n }\n remaining = amount;\n while (\n remaining > 0 &&\n !IOrderbook(orderbook).isEmpty(!isBid, price) &&\n i < n\n ) {\n // fpop OrderLinkedList by price, if ask you get bid order, if bid you get ask order. Get quote asset on bid order on buy, base asset on ask order on sell\n (uint32 orderId, uint256 required, bool clear) = IOrderbook(\n orderbook\n ).fpop(!isBid, price, remaining);\n // order exists, and amount is not 0\n if (remaining <= required) {\n // execute order\n TransferHelper.safeTransfer(give, orderbook, remaining);\n address owner = IOrderbook(orderbook).execute(\n orderId,\n !isBid,\n recipient,\n remaining,\n clear\n );\n // report points on match\n _report(orderbook, give, isBid, remaining, owner);\n // emit event order matched\n emit OrderMatched(\n orderbook,\n orderId,\n isBid,\n recipient,\n owner,\n price,\n remaining,\n clear\n );\n // end loop as remaining is 0\n return (0, n);\n }\n // order is null\n else if (required == 0) {\n ++i;\n continue;\n }\n // remaining >= depositAmount\n else {\n remaining -= required;\n TransferHelper.safeTransfer(give, orderbook, required);\n address owner = IOrderbook(orderbook).execute(\n orderId,\n !isBid,\n recipient,\n required,\n clear\n );\n // report points on match\n _report(orderbook, give, isBid, required, owner);\n // emit event order matched\n emit OrderMatched(\n orderbook,\n orderId,\n isBid,\n recipient,\n owner,\n price,\n required,\n clear\n );\n ++i;\n }\n }\n k = i;\n return (remaining, k);\n }\n\n /**\n * @dev Executes limit order by matching orders in the orderbook based on the provided limit price.\n * @param orderbook The address of the orderbook to execute the limit order on.\n * @param amount The amount of asset to trade.\n * @param give The address of the asset to be traded.\n * @param recipient The address to receive asset after matching a trade\n * @param isBid True if the order is an ask (sell) order, false if it is a bid (buy) order.\n * @param limitPrice The maximum price at which the order can be executed.\n * @param n The maximum number of matches to execute.\n * @return remaining The remaining amount of asset that was not traded.\n */\n function _limitOrder(\n address orderbook,\n uint256 amount,\n address give,\n address recipient,\n bool isBid,\n uint256 limitPrice,\n uint32 n\n ) internal returns (uint256 remaining, uint256 bidHead, uint256 askHead) {\n remaining = amount;\n uint256 lmp = IOrderbook(orderbook).lmp();\n bidHead = IOrderbook(orderbook).clearEmptyHead(true);\n askHead = IOrderbook(orderbook).clearEmptyHead(false);\n uint32 i = 0;\n // In LimitBuy\n if (isBid) {\n if (lmp != 0) {\n if (askHead != 0 && limitPrice < askHead) {\n return (remaining, bidHead, askHead);\n } else if (askHead == 0) {\n return (remaining, bidHead, askHead);\n }\n }\n // check if there is any matching ask order until matching ask order price is lower than the limit bid Price\n while (\n remaining > 0 && askHead != 0 && askHead <= limitPrice && i < n\n ) {\n lmp = askHead;\n (remaining, i) = _matchAt(\n orderbook,\n give,\n recipient,\n isBid,\n remaining,\n askHead,\n i,\n n\n );\n // i == 0 when orders are all empty and only head price is left\n askHead = i == 0\n ? 0\n : IOrderbook(orderbook).clearEmptyHead(false);\n }\n // update heads\n bidHead = IOrderbook(orderbook).clearEmptyHead(true);\n }\n // In LimitSell\n else {\n // check limit ask price is within 20% spread of last matched price\n if (lmp != 0) {\n if (bidHead != 0 && limitPrice > bidHead) {\n return (remaining, bidHead, askHead);\n } else if (bidHead == 0) {\n return (remaining, bidHead, askHead);\n }\n }\n while (\n remaining > 0 && bidHead != 0 && bidHead >= limitPrice && i < n\n ) {\n lmp = bidHead;\n (remaining, i) = _matchAt(\n orderbook,\n give,\n recipient,\n isBid,\n remaining,\n bidHead,\n i,\n n\n );\n // i == 0 when orders are all empty and only head price is left\n bidHead = i == 0\n ? 0\n : IOrderbook(orderbook).clearEmptyHead(true);\n }\n // update heads\n askHead = IOrderbook(orderbook).clearEmptyHead(false);\n }\n // set new market price as the orders are matched\n if (lmp != 0) {\n IOrderbook(orderbook).setLmp(lmp);\n emit NewMarketPrice(orderbook, lmp);\n }\n\n return (remaining, bidHead, askHead); // return bidHead, and askHead\n }\n\n /**\n * @dev Determines if an order can be made at the market price,\n * and if so, makes the an order on the orderbook.\n * If an order cannot be made, transfers the remaining asset to either the orderbook or the user.\n * @param base The address of the base asset for the trading pair\n * @param quote The address of the quote asset for the trading pair\n * @param orderbook The address of the orderbook contract for the trading pair\n * @param remaining The remaining amount of the asset after the market order has been taken\n * @param price The price used to determine if an order can be made\n * @param isBid Boolean indicating if the order was a buy (true) or a sell (false)\n * @param isMaker Boolean indicating if an order is for storing in orderbook\n * @param recipient The address to receive asset after matching a trade and making an order\n * @return id placed order id\n */\n function _detMake(\n address base,\n address quote,\n address orderbook,\n uint256 remaining,\n uint256 price,\n bool isBid,\n bool isMaker,\n address recipient\n ) internal returns (uint32 id) {\n if (remaining > 0) {\n address stopTo = isMaker ? orderbook : recipient;\n TransferHelper.safeTransfer(\n isBid ? quote : base,\n stopTo,\n remaining\n );\n if (isMaker) {\n id = _makeOrder(orderbook, remaining, price, isBid, recipient);\n return id;\n }\n }\n }\n\n function _report(\n address orderbook,\n address give,\n bool isBid,\n uint256 matched,\n address owner\n ) internal {\n if (\n _isContract(feeTo) && IRevenue(feeTo).isReportable() && matched > 0\n ) {\n // report matched amount to accountant with give token on matching order\n IRevenue(feeTo).reportMatch(\n orderbook,\n give,\n isBid,\n msg.sender,\n owner,\n matched\n );\n }\n }\n\n /**\n * @dev Deposit amount of asset to the contract with the given asset information and subtracts the fee.\n * @param base The address of the base asset.\n * @param quote The address of the quote asset.\n * @param amount The amount of asset to deposit.\n * @param isBid Whether it is an ask order or not.\n * If ask, the quote asset is transferred to the contract.\n * @return withoutFee The amount of asset without the fee.\n * @return pair The address of the orderbook for the given asset pair.\n */\n function _deposit(\n address base,\n address quote,\n uint256 price,\n uint256 amount,\n bool isBid,\n bool isMaker\n ) internal returns (uint256 withoutFee, address pair) {\n // check if amount is zero\n if (amount == 0) {\n revert AmountIsZero();\n }\n // get orderbook address from the base and quote asset\n pair = getPair(base, quote);\n // check infalid pair\n\n if (pair == address(0)) {\n revert InvalidPair(base, quote, pair);\n }\n // check if the pair is listed\n if (listingDates[pair] > block.timestamp) {\n revert PairNotListedYet(\n base,\n quote,\n listingDates[pair],\n block.timestamp\n );\n }\n\n // check if amount is valid in case of both market and limit\n uint256 converted = _convert(pair, price, amount, !isBid);\n uint256 minRequired = _convert(pair, price, 1, !isBid);\n\n if (converted <= minRequired) {\n revert OrderSizeTooSmall(converted, minRequired);\n }\n // check sender's fee\n uint256 fee = _fee(amount, msg.sender, isMaker);\n withoutFee = amount - fee;\n if (isBid) {\n // transfer input asset give user to this contract\n if (quote != WETH) {\n TransferHelper.safeTransferFrom(\n quote,\n msg.sender,\n address(this),\n amount\n );\n }\n TransferHelper.safeTransfer(quote, feeTo, fee);\n } else {\n // transfer input asset give user to this contract\n if (base != WETH) {\n TransferHelper.safeTransferFrom(\n base,\n msg.sender,\n address(this),\n amount\n );\n }\n TransferHelper.safeTransfer(base, feeTo, fee);\n }\n emit OrderDeposit(msg.sender, isBid ? quote : base, fee);\n\n return (withoutFee, pair);\n }\n\n function _fee(\n uint256 amount,\n address account,\n bool isMaker\n ) internal view returns (uint256 fee) {\n if (_isContract(feeTo) && IRevenue(feeTo).isSubscribed(account)) {\n uint32 feeNum = IRevenue(feeTo).feeOf(account, isMaker);\n return (amount * feeNum) / feeDenom;\n }\n return amount * 3 / 1000;\n }\n\n function _isContract(address addr) internal view returns (bool isContract) {\n uint size;\n assembly {\n size := extcodesize(addr)\n }\n return size > 0;\n }\n\n /**\n * @dev return converted amount from base to quote or vice versa\n * @param orderbook address of orderbook\n * @param price price of base/quote regardless of decimals of the assets in the pair represented with 8 decimals (if 1000, base is 1000x quote) proposed by a trader\n * @param amount amount of base or quote asset\n * @param isBid if true, amount is quote asset, otherwise base asset\n * @return converted converted amount from base to quote or vice versa.\n * if true, amount is quote asset, otherwise base asset\n * if orderbook does not exist, return 0\n */\n function _convert(\n address orderbook,\n uint256 price,\n uint256 amount,\n bool isBid\n ) internal view returns (uint256 converted) {\n if (orderbook == address(0)) {\n return 0;\n } else {\n return\n price == 0\n ? IOrderbook(orderbook).assetValue(amount, isBid)\n : IOrderbook(orderbook).convert(price, amount, isBid);\n }\n }\n}\n"},"src/exchange/interfaces/IOrderbookFactory.sol":{"content":"// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.24;\n\ninterface IOrderbookFactory {\n struct Pair {\n address base;\n address quote;\n }\n\n function engine() external view returns (address);\n\n function impl() external view returns (address);\n\n function createBook(\n address base_,\n address quote_\n ) external returns (address orderbook);\n\n function isClone(address vault) external view returns (bool cloned);\n\n function getBook(uint256 bookId_) external view returns (address);\n\n function getPair(address base, address quote) external view returns (address book);\n\n function getPairs(uint256 start, uint256 end) external view returns (Pair[] memory);\n\n function getPairsWithIds(uint256[] memory ids) external view returns (Pair[] memory pairs);\n\n function getPairNames(uint256 start, uint256 end) external view returns (string[] memory names);\n\n function getPairNamesWithIds(uint256[] memory ids) external view returns (string[] memory names);\n\n function getBaseQuote(address orderbook) external view returns (address base, address quote);\n\n function getByteCode() external view returns (bytes memory bytecode);\n}\n"},"src/exchange/interfaces/IOrderbook.sol":{"content":"// SPDX-License-Identifier: BUSL-1.1\n\npragma solidity ^0.8.24;\n\nimport \"../libraries/ExchangeOrderbook.sol\";\n\ninterface IOrderbook {\n function initialize(\n uint256 id_,\n address base_,\n address quote_,\n address engine_\n ) external;\n\n function setLmp(uint256 price) external;\n\n function placeAsk(\n address owner,\n uint256 price,\n uint256 amount\n ) external returns (uint32 id);\n\n function placeBid(\n address owner,\n uint256 price,\n uint256 amount\n ) external returns (uint32 id);\n\n function cancelOrder(\n bool isBid,\n uint32 orderId,\n address owner\n ) external returns (uint256 remaining);\n\n function execute(\n uint32 orderId,\n bool isBid,\n address sender,\n uint256 amount,\n bool clear\n ) external returns (address owner);\n\n function clearEmptyHead(bool isBid) external returns (uint256 head);\n\n function fpop(\n bool isBid,\n uint256 price,\n uint256 remaining\n ) external returns (uint32 orderId, uint256 required, bool clear);\n\n function getRequired(\n bool isBid,\n uint256 price,\n uint32 orderId\n ) external view returns (uint256 required);\n\n function lmp() external view returns (uint256);\n\n function heads() external view returns (uint256, uint256);\n\n function askHead() external view returns (uint256);\n\n function bidHead() external view returns (uint256);\n\n function orderHead(bool isBid, uint256 price) external view returns (uint32);\n\n function mktPrice() external view returns (uint256);\n\n function getPrices(bool isBid, uint32 n) external view returns (uint256[] memory);\n\n function nextPrice(bool isBid, uint256 price) external view returns (uint256 next);\n\n function nextOrder(\n bool isBid,\n uint256 price,\n uint32 orderId\n ) external view returns (uint32 next);\n\n function sfpop(\n bool isBid,\n uint256 price,\n uint32 orderId,\n bool isHead\n ) external view returns (uint32 id, uint256 required, bool clear);\n\n function getPricesPaginated(\n bool isBid,\n uint32 start,\n uint32 end\n ) external view returns (uint256[] memory);\n\n function getOrderIds(\n bool isBid,\n uint256 price,\n uint32 n\n ) external view returns (uint32[] memory);\n\n function getOrders(\n bool isBid,\n uint256 price,\n uint32 n\n ) external view returns (ExchangeOrderbook.Order[] memory);\n\n function getOrdersPaginated(\n bool isBid,\n uint256 price,\n uint32 start,\n uint32 end\n ) external view returns (ExchangeOrderbook.Order[] memory);\n\n function getOrder(\n bool isBid,\n uint32 orderId\n ) external view returns (ExchangeOrderbook.Order memory);\n\n function getBaseQuote() external view returns (address base, address quote);\n\n function assetValue(\n uint256 amount,\n bool isBid\n ) external view returns (uint256 converted);\n\n function isEmpty(bool isBid, uint256 price) external view returns (bool);\n\n function convertMarket(uint256 amount, bool isBid) external view returns (uint256 converted);\n\n function convert(\n uint256 price,\n uint256 amount,\n bool isBid\n ) external view returns (uint256 converted);\n}"},"src/exchange/libraries/TransferHelper.sol":{"content":"// SPDX-License-Identifier: BUSL-1.1\n\npragma solidity ^0.8.24;\n\nimport {ILSP7DigitalAsset} from \"@lukso/lsp7-contracts/contracts/ILSP7DigitalAsset.sol\";\n\n// helper methods for interacting with ERC20 tokens and sending ETH that do not consistently return true/false\nlibrary TransferHelper {\n struct TokenInfo {\n address token;\n uint8 decimals;\n string name;\n string symbol;\n uint256 totalSupply;\n }\n\n function safeApprove(address token, address to, uint256 value) internal {\n // bytes4(keccak256(bytes(\"approve(address,uint256)\")));\n (bool success, bytes memory data) = token.call(\n abi.encodeWithSelector(0x095ea7b3, to, value)\n );\n require(\n success && (data.length == 0 || abi.decode(data, (bool))),\n \"AF\"\n );\n }\n\n function safeTransfer(address token, address to, uint256 value) internal {\n // bytes4(keccak256(bytes(\"transfer(address,uint256)\")));\n (bool success, bytes memory data) = token.call(\n abi.encodeWithSelector(0xa9059cbb, to, value)\n );\n require(\n success && (data.length == 0 || abi.decode(data, (bool))),\n \"TF\"\n );\n }\n\n function safeTransferFrom(\n address token,\n address from,\n address to,\n uint256 value\n ) internal {\n // bytes4(keccak256(bytes(\"transferFrom(address,address,uint256)\")));\n (bool success, bytes memory data) = token.call(\n abi.encodeWithSelector(0x23b872dd, from, to, value)\n );\n require(\n success && (data.length == 0 || abi.decode(data, (bool))),\n \"TFF\"\n );\n }\n\n function safeTransferETH(address to, uint256 value) internal {\n (bool success, ) = to.call{value: value}(new bytes(0));\n require(success, \"ETF\");\n }\n\n function name(address token) internal view returns (string memory) {\n // bytes4(keccak256(bytes(\"name()\")));\n (bool success, bytes memory data) = token.staticcall(\n abi.encodeWithSelector(0x06fdde03)\n );\n require(success, \"NF\");\n return abi.decode(data, (string));\n }\n\n function symbol(address token) internal view returns (string memory) {\n // bytes4(keccak256(bytes(\"symbol()\")));\n (bool success, bytes memory data) = token.staticcall(\n abi.encodeWithSelector(0x95d89b41)\n );\n require(success, \"SF\");\n return abi.decode(data, (string));\n }\n\n function totalSupply(address token) internal view returns (uint256) {\n // bytes4(keccak256(bytes(\"totalSupply()\")));\n (bool success, bytes memory data) = token.staticcall(\n abi.encodeWithSelector(0x18160ddd)\n );\n require(success, \"TSF\");\n return abi.decode(data, (uint256));\n }\n\n function decimals(address token) internal view returns (uint8) {\n // bytes4(keccak256(bytes(\"decimals()\")));\n (bool success, bytes memory data) = token.staticcall(\n abi.encodeWithSelector(0x313ce567)\n );\n require(success, \"DF\");\n return abi.decode(data, (uint8));\n }\n\n function getTokenInfo(\n address token\n ) internal view returns (TokenInfo memory tokenInfo) {\n tokenInfo.token = token;\n tokenInfo.name = name(token);\n tokenInfo.symbol = symbol(token);\n tokenInfo.totalSupply = totalSupply(token);\n tokenInfo.decimals = decimals(token);\n return tokenInfo;\n }\n\n function lsp7Transfer(\n address token,\n address from,\n address to,\n uint256 value\n ) internal {\n // bytes4(keccak256(bytes(\"transfer(address,address,uint256,bool,bytes)\")));\n (bool success, bytes memory data) = token.call(\n abi.encodeWithSelector(\n ILSP7DigitalAsset.transfer.selector,\n from,\n to,\n value,\n true,\n \"\"\n )\n );\n\n // Suggest using this function for abi-encoding\n // (bool success, bytes memory data) = token.call(abi.encodeCall(ILSP7DigitalAsset.transfer, from, to, value, true, \"\"));\n require(success && (data.length == 0), \"AF\");\n }\n}\n"},"lib/openzeppelin-contracts-upgradeable/lib/openzeppelin-contracts/contracts/proxy/utils/Initializable.sol":{"content":"// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v5.0.0) (proxy/utils/Initializable.sol)\n\npragma solidity ^0.8.20;\n\n/**\n * @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed\n * behind a proxy. Since proxied contracts do not make use of a constructor, it's common to move constructor logic to an\n * external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer\n * function so it can only be called once. The {initializer} modifier provided by this contract will have this effect.\n *\n * The initialization functions use a version number. Once a version number is used, it is consumed and cannot be\n * reused. This mechanism prevents re-execution of each \"step\" but allows the creation of new initialization steps in\n * case an upgrade adds a module that needs to be initialized.\n *\n * For example:\n *\n * [.hljs-theme-light.nopadding]\n * ```solidity\n * contract MyToken is ERC20Upgradeable {\n * function initialize() initializer public {\n * __ERC20_init(\"MyToken\", \"MTK\");\n * }\n * }\n *\n * contract MyTokenV2 is MyToken, ERC20PermitUpgradeable {\n * function initializeV2() reinitializer(2) public {\n * __ERC20Permit_init(\"MyToken\");\n * }\n * }\n * ```\n *\n * TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as\n * possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}.\n *\n * CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure\n * that all initializers are idempotent. This is not verified automatically as constructors are by Solidity.\n *\n * [CAUTION]\n * ====\n * Avoid leaving a contract uninitialized.\n *\n * An uninitialized contract can be taken over by an attacker. This applies to both a proxy and its implementation\n * contract, which may impact the proxy. To prevent the implementation contract from being used, you should invoke\n * the {_disableInitializers} function in the constructor to automatically lock it when it is deployed:\n *\n * [.hljs-theme-light.nopadding]\n * ```\n * /// @custom:oz-upgrades-unsafe-allow constructor\n * constructor() {\n * _disableInitializers();\n * }\n * ```\n * ====\n */\nabstract contract Initializable {\n /**\n * @dev Storage of the initializable contract.\n *\n * It's implemented on a custom ERC-7201 namespace to reduce the risk of storage collisions\n * when using with upgradeable contracts.\n *\n * @custom:storage-location erc7201:openzeppelin.storage.Initializable\n */\n struct InitializableStorage {\n /**\n * @dev Indicates that the contract has been initialized.\n */\n uint64 _initialized;\n /**\n * @dev Indicates that the contract is in the process of being initialized.\n */\n bool _initializing;\n }\n\n // keccak256(abi.encode(uint256(keccak256(\"openzeppelin.storage.Initializable\")) - 1)) & ~bytes32(uint256(0xff))\n bytes32 private constant INITIALIZABLE_STORAGE = 0xf0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00;\n\n /**\n * @dev The contract is already initialized.\n */\n error InvalidInitialization();\n\n /**\n * @dev The contract is not initializing.\n */\n error NotInitializing();\n\n /**\n * @dev Triggered when the contract has been initialized or reinitialized.\n */\n event Initialized(uint64 version);\n\n /**\n * @dev A modifier that defines a protected initializer function that can be invoked at most once. In its scope,\n * `onlyInitializing` functions can be used to initialize parent contracts.\n *\n * Similar to `reinitializer(1)`, except that in the context of a constructor an `initializer` may be invoked any\n * number of times. This behavior in the constructor can be useful during testing and is not expected to be used in\n * production.\n *\n * Emits an {Initialized} event.\n */\n modifier initializer() {\n // solhint-disable-next-line var-name-mixedcase\n InitializableStorage storage $ = _getInitializableStorage();\n\n // Cache values to avoid duplicated sloads\n bool isTopLevelCall = !$._initializing;\n uint64 initialized = $._initialized;\n\n // Allowed calls:\n // - initialSetup: the contract is not in the initializing state and no previous version was\n // initialized\n // - construction: the contract is initialized at version 1 (no reininitialization) and the\n // current contract is just being deployed\n bool initialSetup = initialized == 0 && isTopLevelCall;\n bool construction = initialized == 1 && address(this).code.length == 0;\n\n if (!initialSetup && !construction) {\n revert InvalidInitialization();\n }\n $._initialized = 1;\n if (isTopLevelCall) {\n $._initializing = true;\n }\n _;\n if (isTopLevelCall) {\n $._initializing = false;\n emit Initialized(1);\n }\n }\n\n /**\n * @dev A modifier that defines a protected reinitializer function that can be invoked at most once, and only if the\n * contract hasn't been initialized to a greater version before. In its scope, `onlyInitializing` functions can be\n * used to initialize parent contracts.\n *\n * A reinitializer may be used after the original initialization step. This is essential to configure modules that\n * are added through upgrades and that require initialization.\n *\n * When `version` is 1, this modifier is similar to `initializer`, except that functions marked with `reinitializer`\n * cannot be nested. If one is invoked in the context of another, execution will revert.\n *\n * Note that versions can jump in increments greater than 1; this implies that if multiple reinitializers coexist in\n * a contract, executing them in the right order is up to the developer or operator.\n *\n * WARNING: Setting the version to 2**64 - 1 will prevent any future reinitialization.\n *\n * Emits an {Initialized} event.\n */\n modifier reinitializer(uint64 version) {\n // solhint-disable-next-line var-name-mixedcase\n InitializableStorage storage $ = _getInitializableStorage();\n\n if ($._initializing || $._initialized >= version) {\n revert InvalidInitialization();\n }\n $._initialized = version;\n $._initializing = true;\n _;\n $._initializing = false;\n emit Initialized(version);\n }\n\n /**\n * @dev Modifier to protect an initialization function so that it can only be invoked by functions with the\n * {initializer} and {reinitializer} modifiers, directly or indirectly.\n */\n modifier onlyInitializing() {\n _checkInitializing();\n _;\n }\n\n /**\n * @dev Reverts if the contract is not in an initializing state. See {onlyInitializing}.\n */\n function _checkInitializing() internal view virtual {\n if (!_isInitializing()) {\n revert NotInitializing();\n }\n }\n\n /**\n * @dev Locks the contract, preventing any future reinitialization. This cannot be part of an initializer call.\n * Calling this in the constructor of a contract will prevent that contract from being initialized or reinitialized\n * to any version. It is recommended to use this to lock implementation contracts that are designed to be called\n * through proxies.\n *\n * Emits an {Initialized} event the first time it is successfully executed.\n */\n function _disableInitializers() internal virtual {\n // solhint-disable-next-line var-name-mixedcase\n InitializableStorage storage $ = _getInitializableStorage();\n\n if ($._initializing) {\n revert InvalidInitialization();\n }\n if ($._initialized != type(uint64).max) {\n $._initialized = type(uint64).max;\n emit Initialized(type(uint64).max);\n }\n }\n\n /**\n * @dev Returns the highest version that has been initialized. See {reinitializer}.\n */\n function _getInitializedVersion() internal view returns (uint64) {\n return _getInitializableStorage()._initialized;\n }\n\n /**\n * @dev Returns `true` if the contract is currently initializing. See {onlyInitializing}.\n */\n function _isInitializing() internal view returns (bool) {\n return _getInitializableStorage()._initializing;\n }\n\n /**\n * @dev Returns a pointer to the storage namespace.\n */\n // solhint-disable-next-line var-name-mixedcase\n function _getInitializableStorage() private pure returns (InitializableStorage storage $) {\n assembly {\n $.slot := INITIALIZABLE_STORAGE\n }\n }\n}\n"},"src/exchange/interfaces/IWETH.sol":{"content":"// SPDX-License-Identifier: BUSL-1.1\n\npragma solidity ^0.8.24;\n\ninterface IWETH {\n function deposit() external payable;\n\n function transfer(address to, uint256 value) external returns (bool);\n\n function withdraw(uint256) external;\n}\n"},"lib/openzeppelin-contracts-upgradeable/lib/openzeppelin-contracts/contracts/utils/ReentrancyGuard.sol":{"content":"// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v5.0.0) (utils/ReentrancyGuard.sol)\n\npragma solidity ^0.8.20;\n\n/**\n * @dev Contract module that helps prevent reentrant calls to a function.\n *\n * Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier\n * available, which can be applied to functions to make sure there are no nested\n * (reentrant) calls to them.\n *\n * Note that because there is a single `nonReentrant` guard, functions marked as\n * `nonReentrant` may not call one another. This can be worked around by making\n * those functions `private`, and then adding `external` `nonReentrant` entry\n * points to them.\n *\n * TIP: If you would like to learn more about reentrancy and alternative ways\n * to protect against it, check out our blog post\n * https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].\n */\nabstract contract ReentrancyGuard {\n // Booleans are more expensive than uint256 or any type that takes up a full\n // word because each write operation emits an extra SLOAD to first read the\n // slot's contents, replace the bits taken up by the boolean, and then write\n // back. This is the compiler's defense against contract upgrades and\n // pointer aliasing, and it cannot be disabled.\n\n // The values being non-zero value makes deployment a bit more expensive,\n // but in exchange the refund on every call to nonReentrant will be lower in\n // amount. Since refunds are capped to a percentage of the total\n // transaction's gas, it is best to keep them low in cases like this one, to\n // increase the likelihood of the full refund coming into effect.\n uint256 private constant NOT_ENTERED = 1;\n uint256 private constant ENTERED = 2;\n\n uint256 private _status;\n\n /**\n * @dev Unauthorized reentrant call.\n */\n error ReentrancyGuardReentrantCall();\n\n constructor() {\n _status = NOT_ENTERED;\n }\n\n /**\n * @dev Prevents a contract from calling itself, directly or indirectly.\n * Calling a `nonReentrant` function from another `nonReentrant`\n * function is not supported. It is possible to prevent this from happening\n * by making the `nonReentrant` function external, and making it call a\n * `private` function that does the actual work.\n */\n modifier nonReentrant() {\n _nonReentrantBefore();\n _;\n _nonReentrantAfter();\n }\n\n function _nonReentrantBefore() private {\n // On the first call to nonReentrant, _status will be NOT_ENTERED\n if (_status == ENTERED) {\n revert ReentrancyGuardReentrantCall();\n }\n\n // Any calls to nonReentrant after this point will fail\n _status = ENTERED;\n }\n\n function _nonReentrantAfter() private {\n // By storing the original value once again, a refund is triggered (see\n // https://eips.ethereum.org/EIPS/eip-2200)\n _status = NOT_ENTERED;\n }\n\n /**\n * @dev Returns true if the reentrancy guard is currently set to \"entered\", which indicates there is a\n * `nonReentrant` function in the call stack.\n */\n function _reentrancyGuardEntered() internal view returns (bool) {\n return _status == ENTERED;\n }\n}\n"},"lib/openzeppelin-contracts-upgradeable/lib/openzeppelin-contracts/contracts/access/AccessControl.sol":{"content":"// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v5.0.0) (access/AccessControl.sol)\n\npragma solidity ^0.8.20;\n\nimport {IAccessControl} from \"./IAccessControl.sol\";\nimport {Context} from \"../utils/Context.sol\";\nimport {ERC165} from \"../utils/introspection/ERC165.sol\";\n\n/**\n * @dev Contract module that allows children to implement role-based access\n * control mechanisms. This is a lightweight version that doesn't allow enumerating role\n * members except through off-chain means by accessing the contract event logs. Some\n * applications may benefit from on-chain enumerability, for those cases see\n * {AccessControlEnumerable}.\n *\n * Roles are referred to by their `bytes32` identifier. These should be exposed\n * in the external API and be unique. The best way to achieve this is by\n * using `public constant` hash digests:\n *\n * ```solidity\n * bytes32 public constant MY_ROLE = keccak256(\"MY_ROLE\");\n * ```\n *\n * Roles can be used to represent a set of permissions. To restrict access to a\n * function call, use {hasRole}:\n *\n * ```solidity\n * function foo() public {\n * require(hasRole(MY_ROLE, msg.sender));\n * ...\n * }\n * ```\n *\n * Roles can be granted and revoked dynamically via the {grantRole} and\n * {revokeRole} functions. Each role has an associated admin role, and only\n * accounts that have a role's admin role can call {grantRole} and {revokeRole}.\n *\n * By default, the admin role for all roles is `DEFAULT_ADMIN_ROLE`, which means\n * that only accounts with this role will be able to grant or revoke other\n * roles. More complex role relationships can be created by using\n * {_setRoleAdmin}.\n *\n * WARNING: The `DEFAULT_ADMIN_ROLE` is also its own admin: it has permission to\n * grant and revoke this role. Extra precautions should be taken to secure\n * accounts that have been granted it. We recommend using {AccessControlDefaultAdminRules}\n * to enforce additional security measures for this role.\n */\nabstract contract AccessControl is Context, IAccessControl, ERC165 {\n struct RoleData {\n mapping(address account => bool) hasRole;\n bytes32 adminRole;\n }\n\n mapping(bytes32 role => RoleData) private _roles;\n\n bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00;\n\n /**\n * @dev Modifier that checks that an account has a specific role. Reverts\n * with an {AccessControlUnauthorizedAccount} error including the required role.\n */\n modifier onlyRole(bytes32 role) {\n _checkRole(role);\n _;\n }\n\n /**\n * @dev See {IERC165-supportsInterface}.\n */\n function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {\n return interfaceId == type(IAccessControl).interfaceId || super.supportsInterface(interfaceId);\n }\n\n /**\n * @dev Returns `true` if `account` has been granted `role`.\n */\n function hasRole(bytes32 role, address account) public view virtual returns (bool) {\n return _roles[role].hasRole[account];\n }\n\n /**\n * @dev Reverts with an {AccessControlUnauthorizedAccount} error if `_msgSender()`\n * is missing `role`. Overriding this function changes the behavior of the {onlyRole} modifier.\n */\n function _checkRole(bytes32 role) internal view virtual {\n _checkRole(role, _msgSender());\n }\n\n /**\n * @dev Reverts with an {AccessControlUnauthorizedAccount} error if `account`\n * is missing `role`.\n */\n function _checkRole(bytes32 role, address account) internal view virtual {\n if (!hasRole(role, account)) {\n revert AccessControlUnauthorizedAccount(account, role);\n }\n }\n\n /**\n * @dev Returns the admin role that controls `role`. See {grantRole} and\n * {revokeRole}.\n *\n * To change a role's admin, use {_setRoleAdmin}.\n */\n function getRoleAdmin(bytes32 role) public view virtual returns (bytes32) {\n return _roles[role].adminRole;\n }\n\n /**\n * @dev Grants `role` to `account`.\n *\n * If `account` had not been already granted `role`, emits a {RoleGranted}\n * event.\n *\n * Requirements:\n *\n * - the caller must have ``role``'s admin role.\n *\n * May emit a {RoleGranted} event.\n */\n function grantRole(bytes32 role, address account) public virtual onlyRole(getRoleAdmin(role)) {\n _grantRole(role, account);\n }\n\n /**\n * @dev Revokes `role` from `account`.\n *\n * If `account` had been granted `role`, emits a {RoleRevoked} event.\n *\n * Requirements:\n *\n * - the caller must have ``role``'s admin role.\n *\n * May emit a {RoleRevoked} event.\n */\n function revokeRole(bytes32 role, address account) public virtual onlyRole(getRoleAdmin(role)) {\n _revokeRole(role, account);\n }\n\n /**\n * @dev Revokes `role` from the calling account.\n *\n * Roles are often managed via {grantRole} and {revokeRole}: this function's\n * purpose is to provide a mechanism for accounts to lose their privileges\n * if they are compromised (such as when a trusted device is misplaced).\n *\n * If the calling account had been revoked `role`, emits a {RoleRevoked}\n * event.\n *\n * Requirements:\n *\n * - the caller must be `callerConfirmation`.\n *\n * May emit a {RoleRevoked} event.\n */\n function renounceRole(bytes32 role, address callerConfirmation) public virtual {\n if (callerConfirmation != _msgSender()) {\n revert AccessControlBadConfirmation();\n }\n\n _revokeRole(role, callerConfirmation);\n }\n\n /**\n * @dev Sets `adminRole` as ``role``'s admin role.\n *\n * Emits a {RoleAdminChanged} event.\n */\n function _setRoleAdmin(bytes32 role, bytes32 adminRole) internal virtual {\n bytes32 previousAdminRole = getRoleAdmin(role);\n _roles[role].adminRole = adminRole;\n emit RoleAdminChanged(role, previousAdminRole, adminRole);\n }\n\n /**\n * @dev Attempts to grant `role` to `account` and returns a boolean indicating if `role` was granted.\n *\n * Internal function without access restriction.\n *\n * May emit a {RoleGranted} event.\n */\n function _grantRole(bytes32 role, address account) internal virtual returns (bool) {\n if (!hasRole(role, account)) {\n _roles[role].hasRole[account] = true;\n emit RoleGranted(role, account, _msgSender());\n return true;\n } else {\n return false;\n }\n }\n\n /**\n * @dev Attempts to revoke `role` to `account` and returns a boolean indicating if `role` was revoked.\n *\n * Internal function without access restriction.\n *\n * May emit a {RoleRevoked} event.\n */\n function _revokeRole(bytes32 role, address account) internal virtual returns (bool) {\n if (hasRole(role, account)) {\n _roles[role].hasRole[account] = false;\n emit RoleRevoked(role, account, _msgSender());\n return true;\n } else {\n return false;\n }\n }\n}\n"},"src/exchange/libraries/ExchangeOrderbook.sol":{"content":"// SPDX-License-Identifier: BUSL-1.1\n\npragma solidity ^0.8.24;\n\nlibrary ExchangeOrderbook {\n // Order struct\n struct Order {\n address owner;\n uint256 price;\n uint256 depositAmount;\n }\n\n // Order Linked List\n struct OrderStorage {\n /// Hashmap-style linked list of prices to route orders\n // key: price, value: order indices linked hashmap\n mapping(uint256 => mapping(uint32 => uint32)) list;\n mapping(uint32 => Order) orders;\n // Head of the linked list(i.e. lowest ask price / highest bid price)\n mapping(uint256 => uint32) head;\n // count of the orders, used for array allocation\n uint32 count;\n address engine;\n }\n\n error OrderIdIsZero(uint32 id);\n error PriceIsZero(uint256 price);\n\n // for orders, lower depositAmount are next, higher depositAmount comes first\n function _insertId(\n OrderStorage storage self,\n uint256 price,\n uint32 id,\n uint256 amount\n ) internal {\n uint32 last = 0;\n uint32 head = self.head[price];\n mapping(uint32 => uint32) storage list = self.list[price];\n mapping(uint32 => Order) storage orders = self.orders;\n // insert order to the linked list\n // if the list is empty\n if (head == 0 || amount > self.orders[head].depositAmount) {\n self.head[price] = id;\n list[id] = head;\n return;\n }\n // Traverse through list until we find the right spot where id's deposit amount is higher than next\n while (head != 0) {\n // what if order deposit amount is bigger than the next order's deposit amount?\n uint32 next = list[head];\n if (amount < orders[next].depositAmount) {\n // Keep traversing\n head = list[head];\n last = next;\n } else if (amount > orders[next].depositAmount) {\n // This is either order is cancelled or order is at the end of the list\n if (orders[next].depositAmount == 0) {\n // Insert order at the end of the list\n list[head] = id;\n list[id] = 0;\n return;\n }\n // Insert order in the middle of the list\n list[head] = id;\n list[id] = next;\n return;\n }\n // what if there is same order with same deposit amount?\n else if (amount == orders[next].depositAmount) {\n list[id] = list[next];\n list[next] = id;\n return;\n }\n }\n }\n\n // pop front\n function _fpop(\n OrderStorage storage self,\n uint256 price\n ) internal returns (uint256) {\n uint32 first = self.head[price];\n if (first == 0) {\n return 0;\n }\n uint32 next = self.list[price][first];\n self.head[price] = next;\n delete self.list[price][first];\n return first;\n }\n\n function _createOrder(\n OrderStorage storage self,\n address owner,\n uint256 price,\n uint256 depositAmount\n ) internal returns (uint32 id) {\n if(price == 0) {\n revert PriceIsZero(price);\n }\n Order memory order = Order({\n owner: owner,\n price: price,\n depositAmount: depositAmount\n });\n // In order to prevent order overflow, order id must start from 1\n self.count = self.count == 0 || self.count == type(uint32).max\n ? 1\n : self.count + 1;\n self.orders[self.count] = order;\n return self.count;\n }\n\n function _decreaseOrder(\n OrderStorage storage self,\n uint32 id,\n uint256 amount,\n uint256 dust,\n bool clear\n ) internal returns (uint256 sendFund, uint256 deletePrice) {\n uint256 decreased = self.orders[id].depositAmount < amount\n ? 0\n : self.orders[id].depositAmount - amount;\n // remove dust\n if (decreased <= dust || clear) {\n decreased = self.orders[id].depositAmount;\n deletePrice = _deleteOrder(self, id);\n return (decreased, deletePrice);\n } else {\n self.orders[id].depositAmount = decreased;\n return (amount, deletePrice);\n }\n }\n\n function _deleteOrder(\n OrderStorage storage self,\n uint32 id\n ) internal returns (uint256 deletePrice) {\n uint256 price = self.orders[id].price;\n uint32 last = 0;\n uint32 head = self.head[price];\n uint32 next;\n uint16 i;\n mapping(uint32 => uint32) storage list = self.list[price];\n // delete id in the order linked list\n if (head == id) {\n self.head[price] = list[head];\n delete list[id];\n } else {\n // search for the order id in the linked list\n while (head != 0) {\n next = list[head];\n if (next == id) {\n list[head] = list[next];\n delete list[id];\n break;\n }\n last = head;\n head = next;\n ++i;\n }\n }\n // delete order\n delete self.orders[id];\n return self.head[price] == 0 ? price : 0;\n }\n\n // show n order ids at the price in the orderbook\n function _getOrderIds(\n OrderStorage storage self,\n uint256 price,\n uint32 n\n ) internal view returns (uint32[] memory) {\n uint32 head = self.head[price];\n uint32[] memory orders = new uint32[](n);\n uint32 i = 0;\n while (head != 0 && i < n) {\n orders[i] = head;\n head = self.list[price][head];\n i++;\n }\n return orders;\n }\n\n function _getOrders(\n OrderStorage storage self,\n uint256 price,\n uint32 n\n ) internal view returns (Order[] memory) {\n uint32 head = self.head[price];\n Order[] memory orders = new Order[](n);\n uint32 i = 0;\n while (head != 0 && i < n) {\n orders[i] = self.orders[head];\n head = self.list[price][head];\n i++;\n }\n return orders;\n }\n\n function _getOrdersPaginated(\n OrderStorage storage self,\n uint256 price,\n uint32 start,\n uint32 end\n ) internal view returns (Order[] memory) {\n uint32 head = self.head[price];\n Order[] memory orders = new Order[](end - start);\n uint32 i = 0;\n while (head != 0 && i < start) {\n head = self.list[price][head];\n i++;\n }\n if (head == 0) {\n return orders;\n }\n while (head != 0 && i < end) {\n orders[i] = self.orders[head];\n head = self.list[price][head];\n i++;\n }\n return orders;\n }\n\n function _head(\n OrderStorage storage self,\n uint256 price\n ) internal view returns (uint32) {\n return self.head[price];\n }\n\n function _isEmpty(\n OrderStorage storage self,\n uint256 price\n ) internal view returns (bool) {\n return self.head[price] == 0;\n }\n\n function _next(\n OrderStorage storage self,\n uint256 price,\n uint32 curr\n ) internal view returns (uint32) {\n return self.list[price][curr];\n }\n\n function _getOrder(\n OrderStorage storage self,\n uint32 id\n ) internal view returns (Order memory) {\n return self.orders[id];\n }\n}\n"},"node_modules/@lukso/lsp7-contracts/contracts/ILSP7DigitalAsset.sol":{"content":"// SPDX-License-Identifier: Apache-2.0\n\npragma solidity ^0.8.4;\n\n// interfaces\nimport {IERC165} from \"@openzeppelin/contracts/utils/introspection/IERC165.sol\";\nimport {\n IERC725Y\n} from \"@erc725/smart-contracts/contracts/interfaces/IERC725Y.sol\";\n\n/**\n * @title Interface of the LSP7 - Digital Asset standard, a fungible digital asset.\n */\ninterface ILSP7DigitalAsset is IERC165, IERC725Y {\n // --- Events\n\n /**\n * @dev Emitted when the `from` transferred successfully `amount` of tokens to `to`.\n * @param operator The address of the operator that executed the transfer.\n * @param from The address which tokens were sent from (balance decreased by `-amount`).\n * @param to The address that received the tokens (balance increased by `+amount`).\n * @param amount The amount of tokens transferred.\n * @param force if the transferred enforced the `to` recipient address to be a contract that implements the LSP1 standard or not.\n * @param data Any additional data included by the caller during the transfer, and sent in the LSP1 hooks to the `from` and `to` addresses.\n */\n event Transfer(\n address indexed operator,\n address indexed from,\n address indexed to,\n uint256 amount,\n bool force,\n bytes data\n );\n\n /**\n * @dev Emitted when `tokenOwner` enables `operator` for `amount` tokens.\n * @param operator The address authorized as an operator\n * @param tokenOwner The token owner\n * @param amount The amount of tokens `operator` address has access to from `tokenOwner`\n * @param operatorNotificationData The data to notify the operator about via LSP1.\n */\n event OperatorAuthorizationChanged(\n address indexed operator,\n address indexed tokenOwner,\n uint256 indexed amount,\n bytes operatorNotificationData\n );\n\n /**\n * @dev Emitted when `tokenOwner` disables `operator` for `amount` tokens and set its {`authorizedAmountFor(...)`} to `0`.\n * @param operator The address revoked from operating\n * @param tokenOwner The token owner\n * @param notified Bool indicating whether the operator has been notified or not\n * @param operatorNotificationData The data to notify the operator about via LSP1.\n */\n event OperatorRevoked(\n address indexed operator,\n address indexed tokenOwner,\n bool indexed notified,\n bytes operatorNotificationData\n );\n\n // --- Token queries\n\n /**\n * @dev Returns the number of decimals used to get its user representation.\n * If the asset contract has been set to be non-divisible via the `isNonDivisible_` parameter in\n * the `constructor`, the decimals returned wiil be `0`. Otherwise `18` is the common value.\n *\n * @custom:notice This information is only used for _display_ purposes: it in\n * no way affects any of the arithmetic of the contract, including\n * {balanceOf} and {transfer}.\n *\n * @return the number of decimals. If `0` is returned, the asset is non-divisible.\n */\n function decimals() external view returns (uint8);\n\n /**\n * @dev Returns the number of existing tokens that have been minted in this contract.\n * @return The number of existing tokens.\n */\n function totalSupply() external view returns (uint256);\n\n // --- Token owner queries\n\n /**\n * @dev Get the number of tokens owned by `tokenOwner`.\n * If the token is divisible (the {decimals} function returns `18`), the amount returned should be divided\n * by 1e18 to get a better picture of the actual balance of the `tokenOwner`.\n *\n * _Example:_\n *\n * ```\n * balanceOf(someAddress) -> 42_000_000_000_000_000_000 / 1e18 = 42 tokens\n * ```\n *\n * @param tokenOwner The address of the token holder to query the balance for.\n * @return The amount of tokens owned by `tokenOwner`.\n */\n function balanceOf(address tokenOwner) external view returns (uint256);\n\n // --- Operator functionality\n\n /**\n * @dev Sets an `amount` of tokens that an `operator` has access from the caller's balance (allowance). See {authorizedAmountFor}.\n * Notify the operator based on the LSP1-UniversalReceiver standard\n *\n * @param operator The address to authorize as an operator.\n * @param amount The allowance amount of tokens operator has access to.\n * @param operatorNotificationData The data to notify the operator about via LSP1.\n *\n * @custom:requirements\n * - `operator` cannot be the zero address.\n *\n * @custom:events {OperatorAuthorizationChanged} when allowance is given to a new operator or\n * an existing operator's allowance is updated.\n */\n function authorizeOperator(\n address operator,\n uint256 amount,\n bytes memory operatorNotificationData\n ) external;\n\n /**\n * @dev Enables `tokenOwner` to remove `operator` for its tokens, disallowing it to send any amount of tokens on its behalf.\n * This function also allows the `operator` to remove itself if it is the caller of this function\n *\n * @param operator The address to revoke as an operator.\n * @param tokenOwner The address of the token owner.\n * @param notify Boolean indicating whether to notify the operator or not.\n * @param operatorNotificationData The data to notify the operator about via LSP1.\n *\n * @custom:requirements\n * - caller MUST be `operator` or `tokenOwner`\n * - `operator` cannot be the zero address.\n *\n * @custom:events {OperatorRevoked} event with address of the operator being revoked for the caller (token holder).\n */\n function revokeOperator(\n address operator,\n address tokenOwner,\n bool notify,\n bytes memory operatorNotificationData\n ) external;\n\n /**\n * @custom:info This function in the LSP7 contract can be used as a prevention mechanism\n * against double spending allowance vulnerability.\n *\n * @notice Increase the allowance of `operator` by +`addedAmount`\n *\n * @dev Atomically increases the allowance granted to `operator` by the caller.\n * This is an alternative approach to {authorizeOperator} that can be used as a mitigation\n * for the double spending allowance problem.\n * Notify the operator based on the LSP1-UniversalReceiver standard\n *\n * @param operator The operator to increase the allowance for `msg.sender`\n * @param addedAmount The additional amount to add on top of the current operator's allowance\n *\n * @custom:requirements\n * - `operator` cannot be the same address as `msg.sender`\n * - `operator` cannot be the zero address.\n *\n * @custom:events {OperatorAuthorizationChanged} indicating the updated allowance\n */\n function increaseAllowance(\n address operator,\n uint256 addedAmount,\n bytes memory operatorNotificationData\n ) external;\n\n /**\n * @custom:info This function in the LSP7 contract can be used as a prevention mechanism\n * against the double spending allowance vulnerability.\n *\n * @notice Decrease the allowance of `operator` by -`subtractedAmount`\n *\n * @dev Atomically decreases the allowance granted to `operator` by the caller.\n * This is an alternative approach to {authorizeOperator} that can be used as a mitigation\n * for the double spending allowance problem.\n * Notify the operator based on the LSP1-UniversalReceiver standard\n *\n * @custom:events\n * - {OperatorAuthorizationChanged} event indicating the updated allowance after decreasing it.\n * - {OperatorRevoked} event if `subtractedAmount` is the full allowance,\n * indicating `operator` does not have any alauthorizedAmountForlowance left for `msg.sender`.\n *\n * @param operator The operator to decrease allowance for `msg.sender`\n * @param tokenOwner The address of the token owner.\n * @param subtractedAmount The amount to decrease by in the operator's allowance.\n *\n * @custom:requirements\n * - `operator` cannot be the zero address.\n * - `operator` must have allowance for the caller of at least `subtractedAmount`.\n */\n function decreaseAllowance(\n address operator,\n address tokenOwner,\n uint256 subtractedAmount,\n bytes memory operatorNotificationData\n ) external;\n\n /**\n * @dev Get the amount of tokens `operator` address has access to from `tokenOwner`.\n * Operators can send and burn tokens on behalf of their owners.\n *\n * @param operator The operator's address to query the authorized amount for.\n * @param tokenOwner The token owner that `operator` has allowance on.\n *\n * @return The amount of tokens the `operator`'s address has access on the `tokenOwner`'s balance.\n *\n * @custom:info If this function is called with the same address for `operator` and `tokenOwner`, it will simply read the `tokenOwner`'s balance\n * (since a tokenOwner is its own operator).\n */\n function authorizedAmountFor(\n address operator,\n address tokenOwner\n ) external view returns (uint256);\n\n /**\n * @dev Returns all `operator` addresses that are allowed to transfer or burn on behalf of `tokenOwner`.\n *\n * @param tokenOwner The token owner to get the operators for.\n * @return An array of operators allowed to transfer or burn tokens on behalf of `tokenOwner`.\n */\n function getOperatorsOf(\n address tokenOwner\n ) external view returns (address[] memory);\n\n // --- Transfer functionality\n\n /**\n * @dev Transfers an `amount` of tokens from the `from` address to the `to` address and notify both sender and recipients via the LSP1 {`universalReceiver(...)`} function.\n * If the tokens are transferred by an operator on behalf of a token holder, the allowance for the operator will be decreased by `amount` once the token transfer\n * has been completed (See {authorizedAmountFor}).\n *\n * @param from The sender address.\n * @param to The recipient address.\n * @param amount The amount of tokens to transfer.\n * @param force When set to `true`, the `to` address CAN be any address. When set to `false`, the `to` address MUST be a contract that supports the LSP1 UniversalReceiver standard.\n * @param data Any additional data the caller wants included in the emitted event, and sent in the hooks of the `from` and `to` addresses.\n *\n * @custom:requirements\n * - `from` cannot be the zero address.\n * - `to` cannot be the zero address.\n * - `from` and `to` cannot be the same address (`from` cannot send tokens to itself).\n * - `from` MUST have a balance of at least `amount` tokens.\n * - If the caller is not `from`, it must be an operator for `from` with an allowance of at least `amount` of tokens.\n *\n * @custom:events\n * - {Transfer} event when tokens get successfully transferred.\n * - if the transfer is triggered by an operator, either the {OperatorAuthorizationChanged} event will be emitted with the updated allowance or the {OperatorRevoked}\n * event will be emitted if the operator has no more allowance left.\n *\n * @custom:hint The `force` parameter **MUST be set to `true`** to transfer tokens to Externally Owned Accounts (EOAs)\n * or contracts that do not implement the LSP1 Universal Receiver Standard. Otherwise the function will revert making the transfer fail.\n *\n * @custom:info if the `to` address is a contract that implements LSP1, it will always be notified via its `universalReceiver(...)` function, regardless if `force` is set to `true` or `false`.\n *\n * @custom:warning Be aware that when either the sender or the recipient can have logic that revert in their `universalReceiver(...)` function when being notified.\n * This even if the `force` was set to `true`.\n */\n function transfer(\n address from,\n address to,\n uint256 amount,\n bool force,\n bytes memory data\n ) external;\n\n /**\n * @dev Same as {`transfer(...)`} but transfer multiple tokens based on the arrays of `from`, `to`, `amount`.\n *\n * @custom:info If any transfer in the batch fail or revert, the whole call will revert.\n *\n * @param from An array of sending addresses.\n * @param to An array of receiving addresses.\n * @param amount An array of amount of tokens to transfer for each `from -> to` transfer.\n * @param force For each transfer, when set to `true`, the `to` address CAN be any address. When set to `false`, the `to` address MUST be a contract that supports the LSP1 UniversalReceiver standard.\n * @param data An array of additional data the caller wants included in the emitted event, and sent in the hooks to `from` and `to` addresses.\n *\n * @custom:requirements\n * - `from`, `to`, `amount` lists MUST be of the same length.\n * - no values in `from` can be the zero address.\n * - no values in `to` can be the zero address.\n * - each `amount` tokens MUST be owned by `from`.\n * - for each transfer, if the caller is not `from`, it MUST be an operator for `from` with access to at least `amount` tokens.\n *\n * @custom:events {Transfer} event **for each token transfer**.\n */\n function transferBatch(\n address[] memory from,\n address[] memory to,\n uint256[] memory amount,\n bool[] memory force,\n bytes[] memory data\n ) external;\n\n /**\n * @notice Executing the following batch of abi-encoded function calls on the contract: `data`.\n *\n * @dev Allows a caller to batch different function calls in one call. Perform a `delegatecall` on self, to call different functions with preserving the context.\n * @param data An array of ABI encoded function calls to be called on the contract.\n * @return results An array of abi-encoded data returned by the functions executed.\n */\n function batchCalls(\n bytes[] calldata data\n ) external returns (bytes[] memory results);\n}\n"},"lib/openzeppelin-contracts-upgradeable/lib/openzeppelin-contracts/contracts/access/IAccessControl.sol":{"content":"// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v5.0.0) (access/IAccessControl.sol)\n\npragma solidity ^0.8.20;\n\n/**\n * @dev External interface of AccessControl declared to support ERC165 detection.\n */\ninterface IAccessControl {\n /**\n * @dev The `account` is missing a role.\n */\n error AccessControlUnauthorizedAccount(address account, bytes32 neededRole);\n\n /**\n * @dev The caller of a function is not the expected one.\n *\n * NOTE: Don't confuse with {AccessControlUnauthorizedAccount}.\n */\n error AccessControlBadConfirmation();\n\n /**\n * @dev Emitted when `newAdminRole` is set as ``role``'s admin role, replacing `previousAdminRole`\n *\n * `DEFAULT_ADMIN_ROLE` is the starting admin for all roles, despite\n * {RoleAdminChanged} not being emitted signaling this.\n */\n event RoleAdminChanged(bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole);\n\n /**\n * @dev Emitted when `account` is granted `role`.\n *\n * `sender` is the account that originated the contract call, an admin role\n * bearer except when using {AccessControl-_setupRole}.\n */\n event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender);\n\n /**\n * @dev Emitted when `account` is revoked `role`.\n *\n * `sender` is the account that originated the contract call:\n * - if using `revokeRole`, it is the admin role bearer\n * - if using `renounceRole`, it is the role bearer (i.e. `account`)\n */\n event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender);\n\n /**\n * @dev Returns `true` if `account` has been granted `role`.\n */\n function hasRole(bytes32 role, address account) external view returns (bool);\n\n /**\n * @dev Returns the admin role that controls `role`. See {grantRole} and\n * {revokeRole}.\n *\n * To change a role's admin, use {AccessControl-_setRoleAdmin}.\n */\n function getRoleAdmin(bytes32 role) external view returns (bytes32);\n\n /**\n * @dev Grants `role` to `account`.\n *\n * If `account` had not been already granted `role`, emits a {RoleGranted}\n * event.\n *\n * Requirements:\n *\n * - the caller must have ``role``'s admin role.\n */\n function grantRole(bytes32 role, address account) external;\n\n /**\n * @dev Revokes `role` from `account`.\n *\n * If `account` had been granted `role`, emits a {RoleRevoked} event.\n *\n * Requirements:\n *\n * - the caller must have ``role``'s admin role.\n */\n function revokeRole(bytes32 role, address account) external;\n\n /**\n * @dev Revokes `role` from the calling account.\n *\n * Roles are often managed via {grantRole} and {revokeRole}: this function's\n * purpose is to provide a mechanism for accounts to lose their privileges\n * if they are compromised (such as when a trusted device is misplaced).\n *\n * If the calling account had been granted `role`, emits a {RoleRevoked}\n * event.\n *\n * Requirements:\n *\n * - the caller must be `callerConfirmation`.\n */\n function renounceRole(bytes32 role, address callerConfirmation) external;\n}\n"},"lib/openzeppelin-contracts-upgradeable/lib/openzeppelin-contracts/contracts/utils/Context.sol":{"content":"// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v5.0.1) (utils/Context.sol)\n\npragma solidity ^0.8.20;\n\n/**\n * @dev Provides information about the current execution context, including the\n * sender of the transaction and its data. While these are generally available\n * via msg.sender and msg.data, they should not be accessed in such a direct\n * manner, since when dealing with meta-transactions the account sending and\n * paying for execution may not be the actual sender (as far as an application\n * is concerned).\n *\n * This contract is only required for intermediate, library-like contracts.\n */\nabstract contract Context {\n function _msgSender() internal view virtual returns (address) {\n return msg.sender;\n }\n\n function _msgData() internal view virtual returns (bytes calldata) {\n return msg.data;\n }\n\n function _contextSuffixLength() internal view virtual returns (uint256) {\n return 0;\n }\n}\n"},"lib/openzeppelin-contracts-upgradeable/lib/openzeppelin-contracts/contracts/utils/introspection/ERC165.sol":{"content":"// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v5.0.0) (utils/introspection/ERC165.sol)\n\npragma solidity ^0.8.20;\n\nimport {IERC165} from \"./IERC165.sol\";\n\n/**\n * @dev Implementation of the {IERC165} interface.\n *\n * Contracts that want to implement ERC165 should inherit from this contract and override {supportsInterface} to check\n * for the additional interface id that will be supported. For example:\n *\n * ```solidity\n * function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {\n * return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId);\n * }\n * ```\n */\nabstract contract ERC165 is IERC165 {\n /**\n * @dev See {IERC165-supportsInterface}.\n */\n function supportsInterface(bytes4 interfaceId) public view virtual returns (bool) {\n return interfaceId == type(IERC165).interfaceId;\n }\n}\n"},"lib/openzeppelin-contracts-upgradeable/lib/openzeppelin-contracts/contracts/utils/introspection/IERC165.sol":{"content":"// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v5.0.0) (utils/introspection/IERC165.sol)\n\npragma solidity ^0.8.20;\n\n/**\n * @dev Interface of the ERC165 standard, as defined in the\n * https://eips.ethereum.org/EIPS/eip-165[EIP].\n *\n * Implementers can declare support of contract interfaces, which can then be\n * queried by others ({ERC165Checker}).\n *\n * For an implementation, see {ERC165}.\n */\ninterface IERC165 {\n /**\n * @dev Returns true if this contract implements the interface defined by\n * `interfaceId`. See the corresponding\n * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section]\n * to learn more about how these ids are created.\n *\n * This function call must use less than 30 000 gas.\n */\n function supportsInterface(bytes4 interfaceId) external view returns (bool);\n}\n"},"node_modules/@erc725/smart-contracts/contracts/interfaces/IERC725Y.sol":{"content":"// SPDX-License-Identifier: CC0-1.0\npragma solidity ^0.8.0;\n\n// interfaces\nimport {IERC165} from \"@openzeppelin/contracts/utils/introspection/IERC165.sol\";\n\n/**\n * @title The interface for ERC725Y sub-standard, a generic data key/value store.\n * @dev ERC725Y provides the ability to set arbitrary data key/value pairs that can be changed over time.\n * It is intended to standardise certain data key/value pairs to allow automated read and writes from/to the contract storage.\n */\ninterface IERC725Y is IERC165 {\n /**\n * @notice The following data key/value pair has been changed in the ERC725Y storage: Data key: `dataKey`, data value: `dataValue`.\n * @dev Emitted when data at a specific `dataKey` was changed to a new value `dataValue`.\n * @param dataKey The data key for which a bytes value is set.\n * @param dataValue The value to set for the given data key.\n */\n event DataChanged(bytes32 indexed dataKey, bytes dataValue);\n\n /**\n * @notice Reading the ERC725Y storage for data key `dataKey` returned the following value: `dataValue`.\n * @dev Get in the ERC725Y storage the bytes data stored at a specific data key `dataKey`.\n * @param dataKey The data key for which to retrieve the value.\n * @return dataValue The bytes value stored under the specified data key.\n */\n function getData(\n bytes32 dataKey\n ) external view returns (bytes memory dataValue);\n\n /**\n * @notice Reading the ERC725Y storage for data keys `dataKeys` returned the following values: `dataValues`.\n * @dev Get in the ERC725Y storage the bytes data stored at multiple data keys `dataKeys`.\n * @param dataKeys The array of keys which values to retrieve\n * @return dataValues The array of data stored at multiple keys\n */\n function getDataBatch(\n bytes32[] memory dataKeys\n ) external view returns (bytes[] memory dataValues);\n\n /**\n * @notice Setting the following data key value pair in the ERC725Y storage. Data key: `dataKey`, data value: `dataValue`.\n *\n * @dev Sets a single bytes value `dataValue` in the ERC725Y storage for a specific data key `dataKey`.\n * The function is marked as payable to enable flexibility on child contracts. For instance to implement\n * a fee mechanism for setting specific data.\n *\n * @param dataKey The data key for which to set a new value.\n * @param dataValue The new bytes value to set.\n */\n function setData(bytes32 dataKey, bytes memory dataValue) external payable;\n\n /**\n * @notice Setting the following data key value pairs in the ERC725Y storage. Data keys: `dataKeys`, data values: `dataValues`.\n *\n * @dev Batch data setting function that behaves the same as {setData} but allowing to set multiple data key/value pairs in the ERC725Y storage in the same transaction.\n *\n * @param dataKeys An array of data keys to set bytes values for.\n * @param dataValues An array of bytes values to set for each `dataKeys`.\n */\n function setDataBatch(\n bytes32[] memory dataKeys,\n bytes[] memory dataValues\n ) external payable;\n}\n"}},"settings":{"remappings":["ds-test/=lib/openzeppelin-contracts/lib/forge-std/lib/ds-test/src/","erc4626-tests/=lib/openzeppelin-contracts/lib/erc4626-tests/","forge-std/=lib/forge-std/src/","openzeppelin-contracts/=lib/openzeppelin-contracts/","@openzeppelin/contracts/=lib/openzeppelin-contracts-upgradeable/lib/openzeppelin-contracts/contracts/","@openzeppelin/contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/contracts/","@lukso/=node_modules/@lukso/","@erc725/=node_modules/@erc725/","openzeppelin-contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/","openzeppelin-foundry-upgrades/=lib/openzeppelin-foundry-upgrades/src/","solidity-stringutils/=lib/openzeppelin-foundry-upgrades/lib/solidity-stringutils/"],"optimizer":{"enabled":true,"runs":200},"metadata":{"useLiteralContent":false,"bytecodeHash":"ipfs"},"outputSelection":{"*":{"":["ast"],"*":["abi","evm.bytecode","evm.deployedBytecode","evm.methodIdentifiers","metadata","storageLayout"]}},"evmVersion":"london","viaIR":false,"libraries":{}}}