Atomic order matching
abandeali1 opened this issue · comments
Motivation:
Currently, order matching of limit orders is supported through use of batchFillOrders
and batchFillOrKillOrders
. While the net result of filling a pair of opposing orders with these functions is the same as an atomic match, a taker is still required to have sufficient funds to fill the first order in order to complete the transaction. This adds an unnecessary cost of capital and is especially problematic when matching very large orders. A taker without the necessary funds to fill the initial order would be required to break down the match into smaller batches, which is also an inefficient use of gas.
Proposed Solution:
Add a matchOrders
function that will atomically fill valid crossing orders without requiring capital upfront. This will lower the barriers to entry of running a centralized matching engine and of arbitraging across exchanges. The requirements of this function would be:
- Takes 2 orders as input parameters
orderA.makerToken
==orderB.takerToken
orderA.takerToken
==orderB.makerToken
msg.sender
is a valid taker for both orders- Prices of both orders cross each other
- Makers of both orders receive amounts specified by orders
msg.sender
keeps the difference
Implementation:
TODO
This looks good, but I would suggest:
orderA.makerTokenAmount
>=orderB.takerTokenAmount
orderB.makerTokenAmount
>=orderA.takerTokenAmount
- takes an
amount
as argument corresponding to the amount oforderA.makerToken
to trade, whereamount <= orderA.remainingMakerTokenAmount
This would allow orders with differing amounts but overlapping prices to be matched.
Example: orderA offers 10 REP for 1 WETH, and orderB offers .5 WETH for 4.5 REP. Anyone could take these two, send them to the exchange contract which would cause:
- 4.5 REP A -> B
- .5 WETH B -> A
- .5 REP A -> Matcher (
msg.sender
)
This way both makers get to execute the trades they had offered, and the matcher gets to keep the spread.
How will fees operate in the matchOrders
function?
In a central matching model takerFee
as implemented does not make much sense since the central relayer is the msg.sender
and taker
always. If using the makerFee
field exclusively, signing the makerFee
into the matching engine designated taker
's order on the fly could create race conditions. Prior to v2 the relayer can partial fill the matching engine designated taker
's order at their discretion to replicate a taker
fee and side-step the signed order parameters.
In the open order book model the atomic matching mechanism is ideal for arbitrageurs to perform cross-relayer and spread arbitrage without the need of large upfront capital in every asset. Open order book relayers want to incentivize these arbitrageurs to maintenance order books and close up the arbitrage opportunities. If the takerFee
is pushed on to the msg.sender
there is a higher economic barrier since the arbitrageur will need to consider:
excess received from order match
(any asset) >gas
(ETH) +orderA.takerFee
(ZRX) +orderB.takerFee
(ZRX)
Dealing with so many variables in various assets exposes the arbitrageurs to a lot of volatility risk across assets with every filled match. It is preferable that the msg.sender
only needs to consider the price of gas
in their calculation to incentivize filling tighter arbitrage opportunities.
A naive solution could be designating the makerOrder
and takerOrder
as parameters to matchOrders
and enforcing respective fees. The problem with this approach is it introduces ambiguity to the order signer about if they will end up paying the makerFee
or the takerFee
when their order gets filled.
@ctebbe I agree using the makerFee
for the taker's order is a bit awkward. ZEIP #18 allows for what you are talking about because there is a clear distinction between the maker and taker of an order (the matcher would be sender
and doesn't pay fees).
I think in the final implementation of matchOrders
, the matcher will have to pay the takerFee
, though. It's not much different than on centralized exchanged, where arbitrageurs still have to take fees into account. Anything else seems like it could be gamed. There's no reason that a relayer should end up with partial fees only because an order was matched rather than filled directly.
@abandeali1 good point on relayer expectations re: fees. I am thinking through a way to keep matchOrders
as frictionless and fair as possible for arbitrageurs on the open order book model since they are the target user of this function and necessary for order book maintenance.
Arbitrageurs (like opportunistic takers) don't want exposure to ZRX volatility risk or management, so is there a plan to implement a generic batch function such that fee abstraction can be effectively used here? ie. the arbitrageur batches a fillOrder
in front of matchOrders
to acquire the needed ZRX to pay fees in a single transaction.