MAOB
Midpoint Anchored Order Book (MAOB)
Midpoint anchored order book with discrete bps rungs around an oracle midprice.
Source: contracts/contracts/MAOB.sol
Route: /reference/maob
Public state (getters)
FEE_DENOM()MAX_RUNGS()MAX_TAKER_FEE_BPS()PRICE_SIGFIGS()RUNG_DENOM()UNWIND_DURATION()baseDecimals()baseScale()baseToken()feeCollector()lastOrderId()minQuoteMaker()minQuoteTaker()oracle()pauseGuardian()quoteDecimals()quoteScale()quoteToken()stateExpiresAt()takerFee()
Maker actions
addLiquidityBatch(LiquidityOrder[] calldata liquidityOrders) external nonReentrant returns (uint256[] memory orderIds)addLiquidityBid(uint16 rung, uint256 quoteAmount) external nonReentrant returns (uint256 orderId)cancelOrder(uint256 orderId, bool withdrawAfter) external nonReentrant returns (uint256 refundBase, uint256 refundQuote)claimAndRequote(uint256[] calldata orderIds, LiquidityOrder[] calldata liquidityOrders) external nonReentrant returns (uint256 baseClaimed, uint256 quoteClaimed, uint256[] memory newOrderIds)claimOrders(uint256[] calldata orderIds, bool withdrawAfter) external nonReentrant returns (uint256 baseClaimed, uint256 quoteClaimed)claimOrders(uint256[] calldata orderIds) external nonReentrant returns (uint256 baseClaimed, uint256 quoteClaimed)requoteFromWithdrawable(LiquidityOrder[] calldata liquidityOrders) external nonReentrant returns (uint256[] memory orderIds)withdraw() external nonReentrant returns (uint256 baseOut, uint256 quoteOut)
Taker actions
buyBaseExactOut(uint256 baseAmount, uint256 maxQuoteIn, uint16 maxRung, uint64 deadline, uint256 minQuotePerRung) external nonReentrant returns (uint256 quoteUsed, uint256 baseFeePaid)buyBaseExactOut(uint256 baseAmount, uint256 maxQuoteIn, uint64 deadline, uint256 minQuotePerRung) external nonReentrant returns (uint256 quoteUsed, uint256 baseFeePaid)buyBaseTargetIn(uint256 quoteIn, uint256 minBaseOut, uint16 maxRung, uint64 deadline, uint256 minQuotePerRung) external nonReentrant returns (uint256 baseOut, uint256 baseFeePaid)buyBaseTargetIn(uint256 quoteIn, uint256 minBaseOut, uint64 deadline, uint256 minQuotePerRung) external nonReentrant returns (uint256 baseOut, uint256 baseFeePaid)sellBaseExactOut(uint256 quoteOut, uint256 maxBaseIn, uint16 maxRung, uint64 deadline, uint256 minQuotePerRung) external nonReentrant returns (uint256 baseUsed, uint256 quoteFeePaid)sellBaseExactOut(uint256 quoteOut, uint256 maxBaseIn, uint64 deadline, uint256 minQuotePerRung) external nonReentrant returns (uint256 baseUsed, uint256 quoteFeePaid)sellBaseTargetIn(uint256 baseAmount, uint256 minQuoteOut, uint16 maxRung, uint64 deadline, uint256 minQuotePerRung) external nonReentrant returns (uint256 quoteOut, uint256 quoteFeePaid)sellBaseTargetIn(uint256 baseAmount, uint256 minQuoteOut, uint64 deadline, uint256 minQuotePerRung) external nonReentrant returns (uint256 quoteOut, uint256 quoteFeePaid)
Admin actions
setFeeCollector(address newCollector) external onlyOwnersetMarketState(MarketState newState, string calldata reason) externalsetMinQuoteMaker(uint256 newMinQuote) external onlyOwnersetMinQuoteTaker(uint256 newMinQuote) external onlyOwnersetPauseGuardian(address newGuardian) external onlyOwner
Views
getDepth(uint16 maxPerSide) external view returns ( uint16[] memory askRungBps, uint256[] memory askVolumesInBase, uint16[] memory bidRungBps, uint256[] memory bidVolumesInQuote )getLastSegment(uint16 rung, bool isAsk) external view returns (uint256 priceQ, uint256 denom, uint256 remainder)getMidPrice() external view returns (uint256 price, uint256 precision, uint48 updatedAt)getRungs() external view returns (uint16[] memory)getRungState(uint16 rung) external view returns ( uint256 askVolume, uint256 bidVolume, uint32 askGeneration, uint32 bidGeneration, uint256 askCumulative, uint256 bidCumulative )previewOrder(uint256 orderId) external view returns (uint256 unfilledInput, uint256 claimableOutput)rungCount() external view returns (uint256)
Other
_buyBaseExactOut(uint256 baseAmount, uint256 maxQuoteIn, uint16 maxRung, uint64 deadline, uint256 minQuotePerRung ) internal returns (uint256 quoteUsed, uint256 baseFeePaid) { _checkTakerNotPaused(); if (baseAmount == 0 || maxQuoteIn == 0) revert MAOB__InvalidAmount(); _checkDeadline(deadline); _requireRung(maxRung); (uint256 midPrice, uint256 precision) = _getMidPrice(); uint256 denom = precision * baseScale; uint256 priceQ = _roundPriceQ(midPrice * quoteScale); uint256 quoteAtMid = Math.mulDiv(baseAmount, priceQ, denom); _checkMinQuoteTaker(quoteAtMid); uint256 totalQuoteUsed = _fillAsks(baseAmount, maxQuoteIn, maxRung, midPrice, denom, msg.sender, minQuotePerRung); if (totalQuoteUsed != 0) { quoteToken.safeTransferFromExact(msg.sender, address(this), totalQuoteUsed); } baseFeePaid = Math.mulDiv(baseAmount, takerFee, FEE_DENOM, Math.Rounding.Ceil); uint256 baseOut = baseAmount - baseFeePaid; baseToken.safeTransfer(msg.sender, baseOut); if (baseFeePaid != 0) { baseToken.safeTransfer(feeCollector, baseFeePaid); } return (totalQuoteUsed, baseFeePaid); } function _buyBaseTargetIn( uint256 quoteIn, uint256 minBaseOut, uint16 maxRung, uint64 deadline, uint256 minQuotePerRung ) internal returns (uint256 baseOut, uint256 baseFeePaid) { _checkTakerNotPaused(); if (quoteIn == 0) revert MAOB__InvalidAmount(); _checkMinQuoteTaker(quoteIn); _checkDeadline(deadline); _requireRung(maxRung); (uint256 midPrice, uint256 precision) = _getMidPrice(); uint256 denom = precision * baseScale; (uint256 baseAcquired, uint256 quoteUsed) = _fillAsksByQuote(quoteIn, maxRung, midPrice, denom, msg.sender, minQuotePerRung); if (baseAcquired == 0) revert MAOB__InsufficientLiquidity(); if (quoteUsed != 0) { quoteToken.safeTransferFromExact(msg.sender, address(this), quoteUsed); } baseFeePaid = Math.mulDiv(baseAcquired, takerFee, FEE_DENOM, Math.Rounding.Ceil); baseOut = baseAcquired - baseFeePaid; if (baseOut < minBaseOut) revert MAOB__MinBaseNotMet(); baseToken.safeTransfer(msg.sender, baseOut); if (baseFeePaid != 0) { baseToken.safeTransfer(feeCollector, baseFeePaid); } return (baseOut, baseFeePaid); } function _sellBaseTargetIn( uint256 baseAmount, uint256 minQuoteOut, uint16 maxRung, uint64 deadline, uint256 minQuotePerRung ) internal returns (uint256 quoteOut, uint256 quoteFeePaid) { _checkTakerNotPaused(); if (baseAmount == 0) revert MAOB__InvalidAmount(); _checkDeadline(deadline); _requireRung(maxRung); (uint256 midPrice, uint256 precision) = _getMidPrice(); uint256 denom = precision * baseScale; uint256 priceQ = _roundPriceQ(midPrice * quoteScale); uint256 quoteAtMid = Math.mulDiv(baseAmount, priceQ, denom); _checkMinQuoteTaker(quoteAtMid); (uint256 totalQuoteOut, uint256 baseUsed) = _fillBids(baseAmount, maxRung, midPrice, denom, minQuotePerRung); if (totalQuoteOut == 0) revert MAOB__InsufficientLiquidity(); if (baseUsed != 0) { baseToken.safeTransferFromExact(msg.sender, address(this), baseUsed); } quoteFeePaid = Math.mulDiv(totalQuoteOut, takerFee, FEE_DENOM, Math.Rounding.Ceil); quoteOut = totalQuoteOut - quoteFeePaid; if (quoteOut < minQuoteOut) revert MAOB__MinQuoteNotMet(); quoteToken.safeTransfer(msg.sender, quoteOut); if (quoteFeePaid != 0) { quoteToken.safeTransfer(feeCollector, quoteFeePaid); } return (quoteOut, quoteFeePaid); } function _sellBaseExactOut( uint256 quoteOut, uint256 maxBaseIn, uint16 maxRung, uint64 deadline, uint256 minQuotePerRung ) internal returns (uint256 baseUsed, uint256 quoteFeePaid) { _checkTakerNotPaused(); if (quoteOut == 0 || maxBaseIn == 0) revert MAOB__InvalidAmount(); _checkMinQuoteTaker(quoteOut); _checkDeadline(deadline); _requireRung(maxRung); (uint256 midPrice, uint256 precision) = _getMidPrice(); uint256 denom = precision * baseScale; (uint256 grossQuote,) = _grossQuoteForNet(quoteOut); uint256 quoteFilled; (baseUsed, quoteFilled) = _fillBidsByQuote(grossQuote, maxRung, midPrice, denom, maxBaseIn, minQuotePerRung); if (baseUsed != 0) { baseToken.safeTransferFromExact(msg.sender, address(this), baseUsed); } quoteFeePaid = Math.mulDiv(quoteFilled, takerFee, FEE_DENOM, Math.Rounding.Ceil); uint256 netQuote = quoteFilled - quoteFeePaid; if (netQuote < quoteOut) revert MAOB__MinQuoteNotMet(); quoteToken.safeTransfer(msg.sender, netQuote); if (quoteFeePaid != 0) { quoteToken.safeTransfer(feeCollector, quoteFeePaid); } return (baseUsed, quoteFeePaid); } // ============================================================= // ADMIN ACTIONS // ============================================================= /// @notice Update the taker fee. /// @param newFee New fee in 1e6 units (0.01 bps increments). function setTakerFee(uint32 newFee) external onlyOwner_requireOwner() internal view { if (msg.sender != owner()) revert MAOB__NotOwner(); } // ============================================================= // MAKER ACTIONS // ============================================================= /// @notice Post base liquidity as an ask in a single rung. /// @param rung Rung index (0-based). /// @param baseAmount Base token amount to deposit. /// @return orderId Newly created order id. function addLiquidityAsk(uint16 rung, uint256 baseAmount) external nonReentrant returns (uint256 orderId)
Events
FeeCollectorUpdated(address indexed oldCollector, address indexed newCollector)MarketStateChanged(MarketState newState, uint256 expiresAt, string reason)MinQuoteMakerUpdated(uint256 oldMinQuote, uint256 newMinQuote)MinQuoteTakerUpdated(uint256 oldMinQuote, uint256 newMinQuote)OrderCancelled(address indexed maker, uint256 indexed orderId, bool indexed isAsk, uint16 rung, uint256 remainingInput, uint256 refundBase, uint256 refundQuote)OrderClaimed(address indexed maker, uint256 indexed orderId, uint256 inputClaimed, uint256 outputClaimed)OrderCreated(address indexed maker, bool indexed isAsk, uint16 indexed rung, uint256 orderId, uint256 amount)PauseGuardianUpdated(address indexed oldGuardian, address indexed newGuardian)RungFilled(address indexed taker, bool indexed isBuy, uint16 indexed rung, uint256 baseAmount, uint256 quoteAmount, uint256 priceE18)TakerFeeUpdated(uint32 oldFee, uint32 newFee)Withdrawn(address indexed user, uint256 baseAmount, uint256 quoteAmount)
Errors
MAOB__InsufficientLiquidity()MAOB__InsufficientWithdrawable()MAOB__InvalidAmount()MAOB__InvalidDeadline()MAOB__InvalidDecimals()MAOB__InvalidFee()MAOB__InvalidMarketState()MAOB__InvalidMinQuote()MAOB__InvalidRung()MAOB__InvalidRungs()MAOB__MakerPaused()MAOB__MaxBaseExceeded()MAOB__MaxQuoteExceeded()MAOB__MinBaseNotMet()MAOB__MinQuoteNotMet()MAOB__NotGuardian()MAOB__NotOrderOwner()MAOB__NotOwner()MAOB__NotOwnerOrGuardian()MAOB__OracleOutOfRange()MAOB__OraclePriceMissing()MAOB__OracleStale()MAOB__OrderNotActive()MAOB__QueueOverflow()MAOB__QuoteAmountTooLow()MAOB__SameToken()MAOB__TakerPaused()MAOB__ZeroAddress()