Since OpenRelay introduced our Pools feature we’ve had people ask about options for different fee structures. Here are the different fee structures we’ve considered:
- ZRX Fees: The simplest way for a relayer to collect fees
- Forwarding Contracts: An abstraction that can simplify the user experience and offer new ways to collect fees
- ZRX Fee Abstraction: A forwarding contract can let users pay in Ether while the relayer collects fees in ZRX
- Ether Fees: A forwarding contract can also collect Ether on behalf of the relayer.
- Order Matching and Negative Spread: Relayers that use the matching model can take fees in the tokens being traded.
- Fee Orders: Users can pay the relayer fees by filling an additional 0x order for any token the relayer chooses
- Subscriptions: Users can pay the relayer up front for a certain amount of volume
Read on for details on each of these structures.
Fees Paid in ZRX
The most conventional way to collect fees in the 0x ecosystem is with ZRX. When a maker signs a 0x order, it specifies fees to be paid by both the maker and the taker of the order. The order doesn’t specify the token the fee will be paid in; it’s encoded into the protocol that the token will be paid in ZRX.
The easiest way to implement ZRX fees introduces a lot of user-interface friction. The user has to hold ZRX with which to pay fees. Then the user has to have allowances set allowing the ZRX exchange contract to trade ZRX on their behalf. Making a simple purchase with Ether could require as many as five transactions: One to wrap ether into WETH, one to set trading allowances for WETH, one to purchase ZRX, one to set trading allowances for ZRX, and finally one to purchase the desired asset.
ZRX based fees also present challenges if applications want to set percentage based fees. For example, if you have an order to trade WETH for MKR with fees to be paid in ZRX, you have to know the exchange rate for WETH/ZRX and MKR/ZRX in order to determine the amount of ZRX to charge as a percentage of the tokens being traded. One of the reasons OpenRelay charges flat fees in our default pool is to avoid the challenge of pricing across tokens.
One last potential problem using ZRX fees is that the fees must be specified when the order is created. If you have a limit order offering to pay 100 DAI for 1 WETH, that order might not be filled for weeks. The price of ZRX could go up 10% while the price of WETH goes down 10%, meaning the dollar value of the fees could be very different from the original intent.
With a forwarding contract, rather than sending the 0x order directly to the 0x exchange contract, the taker sends the order to a forwarding contract along with enough Ether to fill an order that is denominated in WETH. The contract converts the provided Ether into WETH then uses the WETH to fill an order for the desire token, finally sending the acquired tokens back to the user who initiated the transaction. This considerably reduces user experience friction, as they only need to send one transaction to buy something with Ether.
There are two options for collecting fees with a forwarding contract.
ZRX Fee Abstraction
The first option is to use standard ZRX fees, but have the forwarding contract convert from Ether to ZRX on demand. The order lists the fees in ZRX, but from the buyer’s perspective they’re paying fees in ETH. When calling the forwarding contract the client application includes a an order to trade WETH for ZRX. The forwarding contract first purchases ZRX, then uses the ZRX to pay the fees to acquire the target token. From the end user’s perspective this is much simpler, but the application developer still ends up dealing with ZRX, and has several of the challenges described previously.
The second option is to take fees in Ether. Since the user can send ether with their transaction, the forwarding contract can require that they send a little extra, and send that extra ether to the application developer. This makes it easier to take fees as a percentage, as the forwarding contract can take a percentage of the Ether offered for the trade.
Order Matching and Negative Spread
Rather than using an open orderbook model, some relayers use a matching model. Using our earlier example of trading WETH for MKR, a matching relayer would take one order offering WETH in exchange for MKR and fill that against another order offering MKR in exchange for WETH. This is only possible if the spread between the orders is zero or negative; that is, both orders can be filled with the tokens offered by the other order, leaving some tokens to spare. Any tokens remaining after both orders are filled go to the matcher.
This model can make it easier to take percentage based fees. When a user wishes to fill an order already on the orderbook, the relayer can instruct them to create a matching order that provides extra tokens for the relayer to take as a fee.
The last option I want to discuss is one I’m not aware of anyone using to date. Suppose you want to use an open order book model, but you want the taker to pay a fee that is a percentage of the tokens being traded.
When a maker signs their limit order, they would specify a sender contract that must submit the order to the exchange. The sender would be constrained to only fill the order if the taker also provides a “fee order”.
When a user wants to execute a trade, they send the transaction they wish to execute to the relayer. The relayer could reply with a signed fee order that must be included with the call to the sender contract.
A fee order would have the following structure:
- maker — The relayer authorizing the transaction
- taker — The user requesting to send a transaction.
- maker asset — [NotCoin][/notcoin/]
- taker asset — The asset in which the user will pay fees
- maker asset amount — 1 base unit
- taker asset amount — The amount to be paid in fees
- salt — The hash of the transaction the relayer is authorizing.
The sender contract verifies that the fee order is signed by the relayer, and verifies that the salt of the fee order matches the transaction it is requesting to execute. The sender contract executes both the transaction requested by the user and a transaction to fill the fee order, which pays fees in a token chosen by the relayer.
Another options is to not charge fees on a per-order basis at all, but instead to have traders pay a subscription fee to the relayer. If relayers want to charge subscription fees to market makers, they can simply refuse to host orders from maker addresses that are not subscribers. If they want to restrict takers based on subscriptions as well, they would need a sender contract that could identify subscribers on chain. This could be done through a simple whitelist, or perhaps through an ERC721 token outlining the subscription.
As you can see, there are a wide range of options for taking fees through 0x. Each option has trade-offs between technical complexity, user experience, and flexibility for the application developers. OpenRelay’s standard affiliate arrangement uses ZRX based fees, though abstraction through a forwarding contract is an option. If you’re interested in exploring some of the other options, talk to us about setting up a Pool for your organization, and we can help you get started.