Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Atomic order matching #2

abandeali1 opened this issue Jul 6, 2017 · 4 comments


None yet
3 participants
Copy link

commented Jul 6, 2017

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


@abandeali1 abandeali1 changed the title ZERC2: Support atomic order matching Support atomic order matching Jul 11, 2017

@abandeali1 abandeali1 changed the title Support atomic order matching Atomic order matching Jul 11, 2017


This comment has been minimized.

Copy link

commented Nov 30, 2017

This looks good, but I would suggest:

  • orderA.makerTokenAmount >= orderB.takerTokenAmount
  • orderB.makerTokenAmount >= orderA.takerTokenAmount
  • takes an amount as argument corresponding to the amount of orderA.makerToken to trade, where amount <= 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.


This comment has been minimized.

Copy link

commented Jan 26, 2018

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.


This comment has been minimized.

Copy link
Member Author

commented Jan 28, 2018

@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.


This comment has been minimized.

Copy link

commented Jan 28, 2018

@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.

@abandeali1 abandeali1 closed this Aug 31, 2018

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.