0xProject / ZEIPs

0x Improvement Proposals
Apache License 2.0
91 stars 24 forks source link

MetaTransactionsFeatureV2 and Multiplex Upgrades #96

Open benkream0x opened 1 year ago

benkream0x commented 1 year ago

ZEIP for MetaTransactionsFeatureV2 and Multiplex Upgrades

Summary & Motivation

This ZEIP introduces 2 sets of changes

(I) The Tx Relay related changes will improve the all-in gas costs of 0x Labs’ Tx Relay product, which is currently in beta with Robinhood. The changes will bring the product towards a general launch.

Tx Relay offers users the ability to complete transactions - swaps and approvals - without holding a chain’s native token (ie: gasless transactions). These gasless transactions work by ‘wrapping’ the gas costs into the all-in price the user pays, and then leveraging the metatransaction feature of the 0x protocol. Via metatransactions, 0x Labs pays the actual native token costs of the transaction, and recoups the gas costs by collecting a portion of the tokens involved in the actual swap, onchain. Tx Relay also offers integrators the ability to collect fees on-chain.

The all-in gas costs of Tx Relay, however, are close to 350k, which makes the all-in pricing for Tx Relay trades unattractive. The changes proposed in this ZEIP will bring the all-in gas costs for Tx Relay closer to 225k.

(II) The multiplex changes will improve pricing for end-users of the 0x API by allowing professional market-makers to participate in multihop trades. The latest version of the 0x Labs RFQ system uses the 0x protocol OTC Order format, which has superior gas efficiency to the legacy RFQ Order format. However, the current multiplex implementation in the protocol does not allow OTC Orders to be included in multihop trades.

This implementation excludes professional-market makers from multihop trades, which we estimate compose nearly 50% of 0x API activity. On single-hop trades, market-makers typically fill 50% of orders in popular pairs, such as WETH-USDC. By including professional market makers, using OTC Orders, in multihop trades, we stand to improve the overall pricing of the 0x API.

Type

CORE

Github Pull Request: https://github.com/0xProject/protocol/pull/665

Specifications

Tx-Relay related changes

The goal for these changes is to reduce the gas usage for gasless metatransactions and to add the ability to transfer multiple fees inside of MetaTransactionsFeature.

The diagram below shows the current state of gasless metatransactions:

metaTxn1

This flow is particularly gas heavy because the sellToken is transferred to the flash wallet using the TransformERC20 feature, and then manipulated via the 0x transformers.

With the proposed changes, we will enable gasless metatransactions to use multiplex to transfer the sellToken directly to the AMM or access RFQ directly. This will result in a flow that uses a lot less gas. The diagram below shows the token flow through this proposed pathway, along with how we can transfer the sellToken directly as fees to integrators.

metaTxn2

To break this down further, the pull request to add this functionality breaks down into four major sets of changes:

  1. Allow executeMetaTransaction to pay out multiple fees by modifying the data structure MetaTransactionData to add an array of MetaTransactionFeeData. To accomplish this, we created a new MetaTransactionsFeatureV2 contract and MetaTransactionV2Data struct (copied from the V1 MetaTransactionsFeature and MetaTransactionData) and added code to _executeMetaTransactionPrivate to pay out these fees in the specified feeToken. This change also required the creation of the LibMetaTransactionsV2Storage contract to create a new storage bucket to store the block numbers for executed V2 MetaTransactions.
  2. Clean up MetaTransactionsFeatureV2 and MetaTransactionV2Data by removing fields and corresponding logic that are no longer needed from the MetaTransactionV2Data struct:
    • minGasPrice
    • maxGasPrice
    • value
    • feeAmount (unneeded due to the addition of the new fees array)
  3. Add four new selectors to _executeMetaTransactionPrivate in MetaTransactionsFeatureV2 to allow for calls into MultiplexFeature along with functions to create and execute the calls:
    • _executeMultiplexBatchSellTokenForTokenCall
    • _executeMultiplexBatchSellTokenForEthCall
    • _executeMultiplexMultiHopSellTokenForTokenCall
    • _executeMultiplexMultiHopSellTokenForEthCall
  4. Modify Multiplex to account for the scenario where the MultiplexFeature is entered via MetaTransactionsFeatureV2. Specifically, we refactor references to msg.sender into new parameters BatchSellParams.payer and MultiHopSellParams.payer, which we set to state.mtx.signer for metatransactions. This is because for a metatransaction, msg.sender will be some third party and state.mtx.signer will be the taker, whereas msg.sender will often times be the taker in normal Multiplex flows.

File Collections

File

contracts/zero-ex/contracts/src/IZeroEx.sol contracts/zero-ex/contracts/src/features/MetaTransactionsFeatureV2.sol contracts/zero-ex/contracts/src/features/UniswapV3Feature.sol contracts/zero-ex/contracts/src/features/interfaces/IMetaTransactionsFeatureV2.sol contracts/zero-ex/contracts/src/features/interfaces/IMultiplexFeature.sol contracts/zero-ex/contracts/src/features/interfaces/IUniswapV3Feature.sol contracts/zero-ex/contracts/src/features/multiplex/MultiplexFeature.sol contracts/zero-ex/contracts/src/features/multiplex/MultiplexLiquidityProvider.sol contracts/zero-ex/contracts/src/features/multiplex/MultiplexOtc.sol contracts/zero-ex/contracts/src/features/multiplex/MultiplexRfq.sol contracts/zero-ex/contracts/src/features/multiplex/MultiplexTransformERC20.sol contracts/zero-ex/contracts/src/features/multiplex/MultiplexUniswapV2.sol contracts/zero-ex/contracts/src/features/multiplex/MultiplexUniswapV3.sol contracts/zero-ex/contracts/src/storage/LibMetaTransactionsV2Storage.sol contracts/zero-ex/contracts/src/storage/LibStorage.sol

Multiplex changes allowing OTC orders to be filled through multihop

Currently the Exchange Proxy MultiplexFeature allows the exchange proxy to fill trades that need to pass through multiple liquidity sources. MultiplexFeature defines two overarching trade types.

Multiplex and MultiHop can also be chained together, potentially in any combination through the following functions:

Below is a map of the functions and the paths each one can take.

multiplex1 multiplex2

The changes proposed for the multiplexFeature are to allow the feature to be able to fill OtcOrders through the multiplexMultiHopSellPrivate path. To achieve this functionality we are adding the subcall.id MultiplexSubcall.OTC and internal function call _multiHopSellOtcOrderto the if statement within _executeMultiHopSell within _multiplexMultiHopSellPrivate.

The functionality of _multiHopSellOtcOrder is as follows:

The new functionality of _computeHopTarget is as follows:

We want to enforce a certain order in which to use the params.payer balance, and when to use the ExchangeProxy balance during a Multiplex. To accurately determine which balance to use, we needed to add the subcall.id == MultiplexSubcall.OTC case to the _computeHopTarget if statement.

This function has 3 main states (in the context of MultiplexSubcall.OTC):

File Collections

File

contracts/zero-ex/contracts/src/features/MultiplexFeature.sol contracts/zero-ex/contracts/src/features/MultiplexOtc.sol

Designated Team

0x Labs

Audits

The change was audited by ABDK. Final report is available below abdk_audit