0xProject / ZEIPs

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

Price Feed Asset Proxy #51

Open AusIV opened 4 years ago

AusIV commented 4 years ago

Preamble

ZEIP: 51
Title: Price Feed Asset Proxy
Author: Austin Roberts <austin.roberts@openrelay.xyz>
Type: Standard Track
Category (*only required for Standard Track): Core
Status: Draft
Created: 2019-05-22

Simple Summary

Create a new 0x Asset Proxy that allows prices to be set in a different unit of account than the token being traded. For example a maker could offer 100 ZRX in exchange for $35 worth of WETH, and even if the price of WETH moves the order could still be filled at the same dollar-denominated value.

Abstract

With this new asset proxy, makers specify two values for the asset data: the asset data of an asset to be traded, and a price feed for that asset. When a transfer is executed through the asset proxy, the asset proxy uses the specified price feed to convert the number of units to be traded from the target unit of the price feed (eg. dollars) to units of the asset being traded (eg. WETH). This allows makers to set prices in a different unit than the asset being traded, and lets the traders determine which price feeds they are willing to trust to provide accurate pricing information.

Motivation

Under the current protocol, the unit of account for a trade must be the same as the asset being traded. When trading two volatile assets with each other, this creates pricing challenges. If I'm willing to pay $35 worth of WETH to acquire 100 ZRX, I can create an order based on the current price of WETH and the current price of ZRX. But because both WETH and ZRX are fairly volatile assets, an hour from now that same order might mean I'm giving up $37 worth of WETH to acquire the same amount of ZRX. This often leads to makers creating orders with very short expiration times, and reissuing the orders based on new pricing information on a regular basis.

While on-chain price feeds may not be precise enough to support certain use cases such as high-frequency trading, we believe there are many use cases where traders would be happy to place long-standing orders if they could set prices in a more stable unit of account, and this asset proxy would enable those use cases. Some example use cases include:

Specification

Terminology

PriceFeedProxyAssetData Fields

struct PriceFeedProxyAssetData {
    // Asset proxy ID.
    bytes4 assetProxyId, // bytes4(keccak256("PriceFeedProxyAssetData(...)")),
    // Address of the price feed contract
    address priceFeed,
    // Asset data of asset to be traded
    bytes assetData,
}

PriceFeedInterface

interface PriceFeed {
  function getAssetDataQty(bytes assetData, uint256 qty) external returns uint256
}

The price feed contract specified in the priceFeed field of the PriceFeedProxyAssetData must implement a getAssetDataQty(bytes assetData, uint256 qty) method that returns the number of base units of the target asset that could be acquired given qty units of the unit of account for this price feed. Some price feeds may only support a single asset and ignore the provided assetData, while others may support many target asset types from the same price feed contract and use the provided assetData to determine pricing information.

Note that the protocol does not prescribe the unit of account for a price feed. Some price feeds may return a price in USD, others may use other fiat currencies, or even other crypto-currencies. It is up to the traders to understand the unit of account indicated by a price feed.

Price Feed Asset Proxy

The price feed asset proxy must implement the standard AssetProxy interface:

interface AssetProxy {
  function transferFrom(
    bytes calldata assetData,
    address from,
    address to,
    uint256 amount
  ) external;
  function getProxyId() external pure returns bytes4;
}

Rough pseudo-code of this function looks like:

function transferFrom(assetData, from, to, amount) {
  let priceFeedAddress = assetData.slice(16, 36);  // Get the address from bytes 16 - 36
  let targetAssetData = assetData.slice(36);       // Get the assetData from bytes 36 - end
  let targetProxyId = targetAssetData.slice(0, 4); // Get the proxyId from bytes 0 - 4 of the targetAssetData
  let qty = PriceFeed(priceFeedAddress).getAssetDataQty(targetAssetData, amount); // Get the quantity of targetAssetData to be transferred from the price feed
  AssetProxy[targetProxyId].transferFrom(targetAssetData, from, to, qty); // Have the target asset's asset proxy execute the transfer.
}

Here, amount is specified in the unit of account and the price feed translates from the unit of account to units of the target asset given the current price. It then uses the target asset's asset proxy to transfer the translated number of units from the sender to the receiver. For this to work, the price feed asset proxy must be authorized with the target asset proxy.

Rationale

The introduction of a price feed asset proxy gives users a lot of flexibility for setting prices in 0x trades. As discussed below an ERC20 compatible proxy contract could implement similar functionality without requiring protocol-level inclusion, but implementing it as an asset proxy provides greater opportunity for flexibility.

This proposal does not bind anyone to using the price feed asset proxy or trusting any given price feed. Traders who choose to use the price feed asset proxy will need to vet their price feeds to determine whether they are willing to trust that feed to provide accurate information. Traders who choose not to use the price feed asset proxy can continue using the target asset proxies without impact from this change.

Alternative Designs

We have spent quite some time exploring different options for setting prices in terms of stable units of account while trading volatile assets. One option we have explored was to use an ERC20 compatible proxy contract, which would similarly map transfer and transferFrom through a price feed to transfer the target asset. The user experience for this approach was fairly lacking for several reasons:

Risks

Copyright

Copyright and related rights waived via CC0.

abandeali1 commented 4 years ago

I like this concept a lot. How does the taker specify which asset they actually want to actually supply though? That is the biggest caveat with adding new AssetProxy contracts.

ZEIPs #43 and #48 actually both deal with similar issues. It seems that there is a greater need for makers to specify properties about an asset they wish to purchase, allow the taker to supply some data that satisfies those properties, and potentially utilize some custom settlement logic. It is probably worth coming up with a generalized solution that would address all of those needs. I'm not opposed to adding more specific AssetProxy contracts though, if that is what is needed in the short term.