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

MultiAssetProxy: Allow multiple assets per side of a single order #23

Closed
abandeali1 opened this issue Nov 11, 2018 · 2 comments
Closed
Labels
protocol-transition-complete Protocol has transitioned to the new state vote-accepted Vote for this change has been accepted

Comments

@abandeali1
Copy link
Member

abandeali1 commented Nov 11, 2018

Summary

The current AssetPoxy contracts only allow an order to buy or sell a single asset at a time. Since makerAssetData and takerAssetData are byte arrays of arbitrary length, we can actually provide enough information to allow a maker to buy or sell multiple assets with a single order. The MultiAssetProxy will allow for this and is completely backwards compatible.

Motivation

  • This has immediate applications for NFTs. For example, selling booster packs of unique cards.
  • This can be used to sell baskets of tokens (e.g a basket of stablecoins).

Specification

Requirements

  • The MultiAssetProxy should support an arbitrary number of transfers for any asset type that is supported within the protocol (currently or in the future).
  • The MultiAssetProxy should not require users to set new approvals.
  • Ideally, the MultiAssetProxy should allow for partial fills (i.e if a maker wants to sell 2000 ZRX and 4000 BAT at once, the order can still be partially filled).

Asset data encoding

If we wish to support partial fills, the MultiAssetProxy should expect data to be encoded using the following function signature:

// 0x94cfcdd7
MultiAsset(uint256[],bytes[])

These arguments will represent an array of amounts and an array of assetDatas, where an amount at index i will correspond to the assetData at index i.

Partial Fills

Note that the amounts encoded in the top level assetData will be multiplied by the order's amount before transferring the asset. If a maker wants to sell 2000 ZRX and 4000 BAT and allow for partial fills, then amounts will equal [1, 2] and order.makerAssetAmount will be 2000. If the maker does *not *want to allow partial fills, then amounts will equal [2000, 4000] and order.makerAssetAmount will be 1.

Architecture

The MultiAssetProxy will inherit MixinAssetProxyDispatcher and MixinAuthorizable. All other AssetProxy contracts should be registered within the MultiAssetProxy, and each AssetProxy contract must also authorize the MultiAssetProxy. The MultiAssetProxy must also authorize and be registered within the Exchange contract. When an order's assetData specifies the MultiAssetProxy, the chain of calls looks like:

  1. Taker calls Exchange contract
  2. Exchange calls MultiAssetProxy
  3. MultiAssetProxy calls AssetProxy contracts [0, ..., n]

Pseudocode

pragma solidity 0.5.0;
pragma experimental ABIEncoderV2;  

import "../../utils/LibBytes/LibBytes.sol";
import "../../utils/SafeMath/SafeMath.sol";
import "../Exchange/MixinAssetProxyDispatcher.sol";
import "./MixinAuthorizable.sol";

contract MultiAssetProxy is
    SafeMath,
    MixinAssetProxyDispatcher,
    MixinAuthorizable
{
    using LibBytes for bytes;

    // Id of this proxy.
    bytes4 constant internal PROXY_ID = bytes4(keccak256("MultiAsset(uint256[],bytes[])"));

    /// @dev Transfers assets. Either succeeds or throws.
    /// @param assetData Byte array encoded for the respective asset proxy.
    /// @param from Address to transfer asset from.
    /// @param to Address to transfer asset to.
    /// @param amount Amount of asset to transfer.
    function transferFrom(
        bytes assetData,
        address from,
        address to,
        uint256 amount
    )
        external
    {
        bytes memory data = assetData.slice(4, assetData.length - 1);

        // Note: this syntax is usable with Solidity ^0.5.0. 
        // We will likely do the decoding in assembly instead for efficiency.
        (uint256[] memory amounts, bytes[] memory nestedAssetData) = abi.decode(
            data,
            (uint256[], bytes[])
        );

        for (uint i = 0; i < amounts.length; i++) {
            uint256 totalAmount = safeMul(amount, amounts[i]);
            dispatchTransferFrom(
                nestedAssetData[i],
                from,
                to,
                totalAmount
            );
        }
    }

    /// @dev Gets the proxy id associated with the proxy address.
    /// @return Proxy id.
    function getProxyId()
        external
        pure
        returns (bytes4)
    {
        return PROXY_ID;
    }
}

Issues

This addition requires a lot of extra links to be created for each AssetProxy. The current structure is suboptimal for this due to the 2 week timelock and inability to batch transactions within the AssetProxyOwner. It is possible that there may be some transaction ordering dependencies in the future since each one must be executed individually. I propose that batch transaction functionality is added to the AssetProxyOwner, although this is not immediately necessary.

@dekz
Copy link
Member

dekz commented Feb 24, 2019

Vote

Vote has started: http://0x.org/vote. Vote ends approximately 2019-02-25 5pm PST.

[
  {
    "zeip": 23,
    "title": "ZEIP23: Trade Bundles of Assets",
    "startBlock": 7234093,
    "endBlock": 7268071
  }
]

Deployment

Function call represents the state change. All state changes are made through the AssetProxyOwner (a multisig with a 2 week time-lock), which owns the contracts in the 0x protocol. Each transaction contains the relevant function call.

  1. Register MAP on AssetProxyOwner
function call: 0x5a1a66af0000000000000000000000008a13e81fa50eca62fdec7f5d16e513a86e95481b0000000000000000000000000000000000000000000000000000000000000001
multisig tx: 0xc642747400000000000000000000000017992e4ffb22730138e4b62aaa6367fa9d3699a60000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000445a1a66af0000000000000000000000008a13e81fa50eca62fdec7f5d16e513a86e95481b000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000
  1. Authorize MAP on ERC20Proxy
function call: 0x42f1181e0000000000000000000000008a13e81fa50eca62fdec7f5d16e513a86e95481b
multisig tx: 0xc64274740000000000000000000000002240dab907db71e64d3e0dba4800c83b5c502d4e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000002442f1181e0000000000000000000000008a13e81fa50eca62fdec7f5d16e513a86e95481b00000000000000000000000000000000000000000000000000000000
  1. Authorize MAP on ERC721Proxy
function call: 0x42f1181e0000000000000000000000008a13e81fa50eca62fdec7f5d16e513a86e95481b
multisig tx: 0xc6427474000000000000000000000000208e41fb445f1bb1b6780d58356e81405f3e612700000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000002442f1181e0000000000000000000000008a13e81fa50eca62fdec7f5d16e513a86e95481b00000000000000000000000000000000000000000000000000000000
  1. Register MAP on Exchange
function call: 0xc585bb930000000000000000000000008a13e81fa50eca62fdec7f5d16e513a86e95481b
multisig tx: 0xc64274740000000000000000000000004f833a24e1f95d70f028921e27040ca56e09ab0b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000024c585bb930000000000000000000000008a13e81fa50eca62fdec7f5d16e513a86e95481b00000000000000000000000000000000000000000000000000000000

Deployment Validation

The MultiAssetProxy: 0x8a13e81fa50eca62fdec7f5d16e513a86e95481b has previously been deployed. These are the state changes to connect the MultiAssetProxy to the rest of the protocol.

  1. Register MAP on AssetProxyOwner

AssetProxyOwner: 0x17992e4ffb22730138e4b62aaa6367fa9d3699a6

registerAssetProxy
 registerAssetProxy(address,bool)
 assetProxyContract                0x8a13e81fa50eca62fdec7f5d16e513a86e95481b
 isRegistered                      true

submitTransaction
 submitTransaction(address,uint256,bytes)
 destination                               0x17992e4ffb22730138e4b62aaa6367fa9d3699a6
 value                                     0
 data                                      0x5a1a66af0000000000000000000000008a13e81fa50eca62fdec7f5d16e513a86e95481b0000000000000000000000000000000000000000000000000000000000000001
  1. Authorize MAP on ERC20Proxy

ERC20Proxy: 0x2240dab907db71e64d3e0dba4800c83b5c502d4e

addAuthorizedAddress
 addAuthorizedAddress(address)
 target                         0x8a13e81fa50eca62fdec7f5d16e513a86e95481b

submitTransaction
 submitTransaction(address,uint256,bytes)
 destination                               0x2240dab907db71e64d3e0dba4800c83b5c502d4e
 value                                     0
 data                                      0x42f1181e0000000000000000000000008a13e81fa50eca62fdec7f5d16e513a86e95481b
  1. Authorize MAP on ERC721Proxy

ERC721Proxy: 0x208e41fb445f1bb1b6780d58356e81405f3e6127

addAuthorizedAddress
 addAuthorizedAddress(address)
 target                         0x8a13e81fa50eca62fdec7f5d16e513a86e95481b

submitTransaction
 submitTransaction(address,uint256,bytes)
 destination                               0x208e41fb445f1bb1b6780d58356e81405f3e6127
 value                                     0
 data                                      0x42f1181e0000000000000000000000008a13e81fa50eca62fdec7f5d16e513a86e95481b
  1. Register MAP on Exchange

Exchange: 0x4f833a24e1f95d70f028921e27040ca56e09ab0b

registerAssetProxy
 registerAssetProxy(address)
 assetProxy                   0x8a13e81fa50eca62fdec7f5d16e513a86e95481b

submitTransaction
 submitTransaction(address,uint256,bytes)
 destination                               0x4f833a24e1f95d70f028921e27040ca56e09ab0b
 value                                     0
 data                                      0xc585bb930000000000000000000000008a13e81fa50eca62fdec7f5d16e513a86e95481b

@dekz dekz added vote-accepted Vote for this change has been accepted protocol-transitioning Protocol is transitioning to this new state labels Feb 26, 2019
@dekz
Copy link
Member

dekz commented Feb 26, 2019

The vote has been finalised and accepted.

The protocol is now transitioning to accept this state.

Transactions: 1 2 3 4

submitTransaction
 submitTransaction(address,uint256,bytes)
 destination                               0x2240dab907db71e64d3e0dba4800c83b5c502d4e
 value                                     0
 data                                      0x42f1181e0000000000000000000000008a13e81fa50eca62fdec7f5d16e513a86e95481b

submitTransaction
 submitTransaction(address,uint256,bytes)
 destination                               0x17992e4ffb22730138e4b62aaa6367fa9d3699a6
 value                                     0
 data                                      0x5a1a66af0000000000000000000000008a13e81fa50eca62fdec7f5d16e513a86e95481b0000000000000000000000000000000000000000000000000000000000000001

submitTransaction
 submitTransaction(address,uint256,bytes)
 destination                               0x4f833a24e1f95d70f028921e27040ca56e09ab0b
 value                                     0
 data                                      0xc585bb930000000000000000000000008a13e81fa50eca62fdec7f5d16e513a86e95481b

submitTransaction
 submitTransaction(address,uint256,bytes)
 destination                               0x208e41fb445f1bb1b6780d58356e81405f3e6127
 value                                     0
 data                                      0x42f1181e0000000000000000000000008a13e81fa50eca62fdec7f5d16e513a86e95481b

This protocol change will be completed on 2019-03-11.

@dekz dekz added protocol-transition-complete Protocol has transitioned to the new state and removed protocol-transitioning Protocol is transitioning to this new state labels Mar 12, 2019
@hysz hysz mentioned this issue Mar 20, 2019
@dekz dekz closed this as completed Sep 16, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
protocol-transition-complete Protocol has transitioned to the new state vote-accepted Vote for this change has been accepted
Projects
None yet
Development

No branches or pull requests

2 participants