Taking offers
Basic taker side functions
Generalities
Token allowance
ERC20 tokens transfers are initiated by Mangrove using transferFrom
. If Mangrove's allowance
on the taker's address (for tokens to be spent) is too low, the order will revert.
Active offer lists
Every Mangrove offer list can be either active or inactive, and Mangrove itself can be either alive or dead. Taking offers is only possible when Mangrove is alive on offer lists that are active.
Market order
A Market Order is Mangrove's simplest way of buying or selling assets. Such (taker) orders are run against a specific offer list with its associated outbound token (tokens that flow out of Mangrove) and inbound token (tokens that flow into Mangrove). The liquidity taker specifies how many outbound tokens she wants and how many inbound tokens she gives.
When an order is processed by Mangrove's matching engine, it consumes the offers on the selected offer list, starting from the one which as the best rank. Execution works as follows:
Mangrove checks that the current offer's entailed price is at least as good as the taker's price. Otherwise execution stops there.
Mangrove sends inbound tokens to the current offer's associated logic.
Mangrove then executes the offer logic.
If the call is successful, Mangrove sends outbound tokens to the taker. If the call or the transfer fail, Mangrove reverts the effects of steps 2. and 3.
The taker's wants and gives are reduced.
If the taker's wants has not been completely fulfilled, Mangrove moves back to step 1.
Any failed offer execution results in a bounty being sent to the caller as compensation for the wasted gas.
function marketOrder(
address outbound_tkn,
address inbound_tkn,
uint takerWants,
uint takerGives,
bool fillWants
) external returns (uint takerGot, uint takerGave, uint bounty, uint fee);
Inputs
outbound_tkn
address of the outbound token (that the taker will buy).inbound_tkn
address of the inbound token (that the taker will spend).takerWants
raw amount of outbound token the taker wants. Must fit on 160 bits.takerGives
raw amount of inbound token the taker gives. Must fit on 160 bits.fillWants
If
true
, the market order will stop as soon astakerWants
outbound tokens have been bought. It is conceptually similar to a buy order.If
false
, the market order will continue untiltakerGives
inbound tokens have been spent. It is conceptually similar to sell order.Note that market orders can stop for other reasons, such as the price being too high.
Outputs
takerGot
is the net amount of outbound tokens the taker has received (i.e after applying the offer list fee if any).takerGave
is the amount of inbound tokens the taker has sent.bounty
is the amount of native tokens (in units of wei) the taker received in compensation for cleaning failing offersfee
is the amount ofoutbound_tkn
that was sent to Mangrove's vault in payment of the potential fee associated to the(outbound_tkn, inbound_tkn)
offer list.
Specification
At the end of a Market Order the following is guaranteed to hold:
The taker will not spend more than
takerGives
.The average price paid
takerGave/(
takerGot + fee)
will be maximally close totakerGives/takerWants:
for each offer taken, the amount paid will be the expected amount + 1.
2
0.98
1
1
9.9
10
More on market order behaviour
Mangrove's market orders are configurable using the three parameters takerWants
, takerGives
and fillWants.
Suppose one wants to buy or sell some token B
(base), using token Q
(quote) as payment.
Market buy: A limit buy order for x tokens B, corresponds to a
marketOrder
on the (B
,Q
) offer list withtakerWants=x
(the volume one wishes to buy) and withtakerGives
such thattakerGives/x
is the limit price cap, and settingfillWants
totrue
.Market sell: A limit sell order for x tokens B, corresponds to a
marketOrder
on the (Q
,B
) offer list withtakerGives=x
(the volume one wishes to sell) and withtakerWants
such thattakerGives/x
is the limit price cap, and settingfillWants
tofalse
.
On order residuals
Contrary to GTC orders on regular orderbook based exchanges, the residual of your order (i.e. the volume you were not able to buy/sell due to hitting your price limit) will not be put on the market as an offer. Instead, the market order will simply end partially filled.
Market order prices are volume-weighted
Consider the following A-B offer list:
1
1
1
1
2
2
1
2
3
6
2
3
A regular limit order with takerWants
set to 3 A and takerGives
set to 6 B would consume offers until it hits an offer with a price above 2, so it would consume offers #1 and #2, but not offer #3.
In Mangrove, a "market order" with the same parameters will however consume offers #1 and #2 completely and #3 partially (for 3 Bs only), and result in the taker spending 6 (1+2+6/2) and receiving (1+1+2/2), which corresponds to a volume-weighted price of 2, complying with the Taker Order.
Offer sniping
It is also possible to target specific offer IDs in the offer list. This is called Offer Sniping.
function snipes(
address outbound_tkn,
address inbound_tkn,
uint[4][] memory targets,
bool fillWants
)
external
returns (
uint successes,
uint takerGot,
uint takerGave,
uint bounty,
uint fee
);
Inputs
outbound_tkn
outbound token address (received by the taker)inbound_tkn
inbound token address (sent by the taker)targets
an array of offers to take. Each element oftargets
is auint[4]
's of the form[offerId, takerWants, takerGives, gasreq_permitted]
where:offerId
is the ID of an offer that should be taken.takerWants
the amount of outbound tokens the taker wants from that offer. Must fit in auint96
.takerGives
the amount of inbound tokens the taker is willing to give to that offer. Must fit in auint96
.gasreq_permitted
is the maximumgasreq
the taker will tolerate for that offer. If the offer'sgasreq
is higher thangasreq_permitted
, the offer will not be sniped.
fillWants
specifies whether you are acting as a buyer of outbound tokens, in which case you will buy at mosttakerWants
, or a seller of inbound tokens, in which case you will buy as many tokens as possible as long as you don't spend more thantakerGives
.
Protection against malicious offer updates
Offers can be updated, so if targets
was just an array of offerId
s, there would be no way to protect against a malicious offer update mined right before a snipe. The offer could suddenly have a worse price, or require a lot more gas.
If you only want to take offers without any checks on the offer contents, you can simply:
Set
takerWants
to0
,Set
takerGives
totype(uint96).max
,Set
gasreq_permitted
totype(uint).max
, andSet
fillWants
tofalse
.
Outputs
successes
is the number of sniped offers that transferred the expected volume to the taker (in particularsuccesses < target.length
if and only if some of the sniped offers reneged on their trade andbounty > 0
).takerGot, takerGet, bounty, fee
as inmarketOrder
.
Example
13
10
10
80_000
2
1
2
250_000
Last updated