Makers
Market Orders
A market order is the simplest way for a taker to buy or sell assets on Mangrove. Each order is executed against a specific offer list, which defines the outbound and inbound tokens of the trade.
The takes specifies:
how much the want to trade (either in terms of the token they want to receive or the one they are willing to spend), and
the maximum price (or tick) they ware willing to accept.
This ensures that trades are execute only within a controller price range.
Market Orders in Mangrove vs. TradFi
In traditional finance, a market order executes immediately at the best available price. However, on-chain trading is exposed to MEV (front-running and sandwich attacks), making such unconstrained market orders unsafe.
For this reason, Mangrove's "market order" corresponds to a TradFi limit order:
It executes immediately, but only at prices equal to or better than the user's specified limit.
This "safe market order" model ensures that users never pay beyond their defined limit price.
Execution Flow
When a market order is submitted, Mangrove's matching engine processes offers in sequence:
It scans offers from the best tick upward (asks) or lowest tick downward (bids).
Offers are consumed one by one, in FIFO order for the same tick.
Matching continues until:
all offers within the taker's
maxTickare processed,the offer list ends, or
the taker's desired volume is fully filled.
For each matched offer:
Mangrove computes the trade amounts:
The inbound token is sent to the offer maker.
The offer logic (
makerExecute) is called.If successful, the outbound tokens are sent to the taker.
If the call fails, the trade is reverted and the taker receives a bounty for the gas spent.
Function Overview
Mangrove provides two entry points for market orders:
function marketOrderByTick(
OLKey memory olKey,
Tick maxTick,
uint fillVolume,
bool fillWants,
) external returns (uint takerGot, uint takerGave, uint bounty, uint feePaid);
function marketOrderByVolume(
OLKey memory olKey,
uint takerWants,
uint takerGives,
bool fillWants
) external returns (uint takerGot, uint takerGave, uint bounty, uint feePaid);Both return:
takerGot,amount ofoutbound_tknthe taker received. Includes any partial fills and reflects fees already applied (if the offer list charges one).takerGave,amount ofinbound_tknthe taker spent to execute the order.bounty,amount of native tokens (in wei) the taker earned as compensation for executing failing offers. Bounties are deducted from the failing maker’s provision.feePaid,amount ofoutbound_tkntransferred to Mangrove’s vault as payment for any configured offer list fee.
The *ByVolume variant is a wrapper around *ByTick, converting the volume ratio to the corresponding tick, rounding down to the nearest tick allowed on the offer list, such that the taker does not pay more than specified.
Inputs
marketOrderByTick(olKey, maxTick, fillVolume, fillWants)
olKey Identifies the offer list:
outbound_tkn— token to buy (received by taker)inbound_tkn— token to pay (spent by taker)tickSpacing— allowed tick spacing
maxTick Maximum tick that can be matched with this order.
fillVolume & fillWants Specify how much the taker wants to trade and when the order stops:
If
fillWants = true→fillVolumeis inoutbound_tkn(the taker specifies how much they want to receive).If
fillWants = false→fillVolumeis ininbound_tkn(the taker specifies how much they want to spend). The order ends when the specified volume is reached or no more eligible offers remain.
marketOrderByVolume(olKey, takerWants, takerGives, fillWants)
olKey Identifies the offer list (same structure as above).
takerWants & takerGives Define the desired trade amounts:
takerWants— amount ofoutbound_tknthe taker wants to receive.takerGives— amount ofinbound_tknthe taker is willing to spend.
This ratio is converted into a corresponding maxTick (rounded down).
fillWants Controls when the order stops:
If
true, the order ends when the taker has receivedtakerWants.If
false, the order ends when the taker has spenttakerGives.
Example: DAI/WETH Market
-75103
0.0005476
77
925.26
-75103
0.0005476
177
916.47
-75041
0.0005510
42
871.76
Buying DAI
A taker calls marketOrderByTick on the DAI/WETH offer list with:
maxTick= -75000 -> corresponds to a max ratio of 0.0005539fillVolume=fillWants=true
Since fillWants = true, we have:
fillVolumeis inolKey.outbound_tknand corresponds to 2,500 DAIthe market order will be considered filled once 2,500 DAI has been received.
In other words, this is a buy order for 2,500 DAI, where the taker is willing to pay up to WETH.
The market order will execute as follows:
Get 925.26 DAI for
925.26 * 0.0005476 = 0.5067WETH from offer #77Get 916.47 DAI for
916.47 * 0.0005476 = 0.5019WETH from offer #177Get the remaining
2500 - 925.26 - 916.47 = 658.27DAI for658.27 * 0.0005510 = 0.3627WETH from offer #42.
In total, the taker gets 2,500 DAI and sends 0.5067 + 0.5019 + 0.3627 = 1.3713 WETH.
Selling WETH
A taker calls marketOrderByTick on the offer list with:
maxTick= -75000 -> corresponds to a max ratio of 0.0005539fillVolume=fillWants=false
Because fillWants = false, the taker specifies how much they want to spend:
fillVolumerefers to 1.2 WETH (theinbound_tkn).The market order will end once 1.2 WETH has been sent.
This corresponds to a sell order of 1.2 WETH. At the maximum allowed ratio, the taker expects to receive at least: DAI.
Execution proceeds as follows:
Sell
925.26 * 0.0005476 = 0.5067WETH for 925.26 DAI to offer #77Sell
916.47 * 0.0005476 = 0.5019WETH for 916.47 DAI to offer #177Sell the remaining
1.2 - 0.5067 - 0.5019 = 0.1914WETH for0.1914 / 0.0005510 = 347.37DAI to offer #42.
In total, the taker gets 925.26 + 916.47 + 347.37 = 2,189.10 DAI and sends 1.2 WETH.
Market Order Behavior
Suppose you want to trade a base token (B) against a quote token (Q), on a market with tick spacing T.
Market buy:
Buy
xunits ofBusingQ. → CallmarketOrderByTickon(B, Q, T)with:fillWants = true,fillVolume = x, andmaxTick = log₁.₀₀₀₁(priceLimit).Market sell:
Sell
xunits ofBforQ. → CallmarketOrderByTickon(Q, B, T)with:fillWants = false,fillVolume = x, andmaxTick = −log₁.₀₀₀₁(priceLimit).
Order Residuals
Unlike traditional exchanges, any unfilled portion of a market order (the residual) is not posted to the order book. The order simply ends partially filled once the price limit is reached.
If you want GTC-style (“Good Till Cancelled”) behavior, this can be implemented using a maker contract.
The MangroveOrder contract in the Strat Library provides GTC and other advanced order types.
Bounties for Failing Offers
If an offer fails to deliver the promised liquidity, the taker receives a bounty in native tokens to compensate for gas costs. This bounty is deducted from the maker’s provision, the deposit they supplied when posting the offer. See Offer provisions for details.
Token Allowances
Mangrove transfers tokens using transferFrom. If the taker’s allowance for Mangrove (on the inbound token) is too low, the transaction will revert.
Active Offer Lists
Each offer list in Mangrove can be active or inactive, and the Mangrove contract itself can be alive or dead. Orders can only be executed when both Mangrove and the selected offer list are active.
Last updated