0xProject/ZEIPs

Price Feed Asset Proxy

Opened this issue · 1 comments

AusIV commented

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:

  • NFT Vendors may wish to offer their NFTs at a stable price, while allowing
    buyers to pay in a variety of tokens.
  • Traders may set limit orders to sell assets when they reach a particular price
    point.
  • Stable-coin keepers may set long-standing orders to trade stable coins for
    volatile tokens. For example, a maker might make two orders: One to buy 1 DAI
    at a price of $.98 worth of WETH, and one to buy $1.02 of WETH at a price of
    1 DAI. If the price of DAI moves relative to the WETH price feed, the
    outstanding orders will allow the keepers to passively buy DAI as the price
    feed drops, and sell DAI as the price feed rises.
  • With the inclusion of ZEIP-28, relayers could set prices in stable terms while
    collecting fees in other assets. This can help shield relayers from price
    fluctuations in the fee asset for long-lived orders.

Specification

Terminology

  • Target Asset: The underlying asset being priced by the price feed and traded
    by the asset proxy.
  • Unit of Account: The asset in which the price of the target asset is being
    denominated (eg. USD).
  • Price Feed: A contract that tracks the price of the Target Asset denominated
    in the Unit of Account, and can be called to determine how many units of the
    Target Asset correspond to a given number of units of the Unit of Account.

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:

  • Users would have to set allowances for the price feed proxy to call
    transferFrom on the target asset.
  • Users would have to set allowances for the 0x ERC20 Asset Proxy to call
    transferFrom on the price feed proxy contract.
  • If users wanted to use a different price feed, they would have to repeat the
    above steps with a price feed proxy for the desired price feed.
  • The price feed proxy would present as an ERC20 contract where the account's
    balance fluctuates based on the allowances set by that account and the price
    of the target asset. The fluctuations violate the expectations of many
    contracts and tools that interact with ERC20 tokens.
  • The gas consumption for this approach is inevitably higher.

Risks

  • This proposal requires the existing 0x asset proxies to approve this proxy as
    a caller. As such, the implementation of this asset proxy must undergo a
    thorough security audit to avoid compromising assets protected by other asset
    proxies.
  • The users of the price feed asset proxies must place some level of trust in
    price feeds. This is mitigated to an extent by allowing the order to specify
    price feeds and not tying the user to a specific price feed. Price feed trust
    can be maximized by:
    • Using on-chain pricing sources such as Kyber and DutchX.
    • Using a medianizer to combine several pricing sources
    • Used in conjunction with ZEIP-39's StaticCallProxy, users could set a price
      range such that the order is only valid while the price is within a given
      range.
  • Takers are subject to some amount of volatility if the price feed changes
    between a transaction being submitted and the transaction being confirmed.
    This can be mitigated to a degree by putting expiration times on transactions
    per ZEIP-37.

Copyright

Copyright and related rights waived via CC0.

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.