0xProject / 0x-launch-kit-frontend

Apache License 2.0
114 stars 208 forks source link

Historical orders #219

Closed fvictorio closed 5 years ago

fvictorio commented 5 years ago

Problem: we want to have a tab in the Order Book that shows historical orders. We can't do this right now because the relayer removes filled, canceled or invalid orders.

  1. One possible solution would be for the relayer to keep track of these orders, but this would mean having an endpoint that is not part of the SRA (or adding this endpoint to the standard). It would also mean that the amount of space needed in the relayer would grow indefinitely.

  2. Another possible solution is to use the exchange contract events to fetch the list of past orders. At the very least, we could get a list with the Filled and Canceled orders, but we wouldn't be able to get orders that were created but later removed when they became invalid (for lack of funds or any other reason). The mechanism to do this would be similar to the one we have for notifying the user of orders that were filled while they weren't using the dapp. Another advantage of doing it this way is that we also have a tab for Filled orders in the Orders component (the component that shows the orders for the connected user). That tab isn't working at this moment, because filled orders are removed from the relayer, but we could keep track of them if we got them from the contract.

fvictorio commented 5 years ago

This turned out to be harder than we expected.

The core of the issue is that we can't know from a Fill event if the order was fully filled. Not only that: we can't reconstruct the order, since the Exchange contract only keeps track of how much of an order was already filled.

One alternative is to keep track of the orders we do see in the frontend, and save all of them in local storage. Then we can ask periodically for the info associated to those orders, and check which ones are fully filled. This would be a very complex solution.

The Cancel event has similar issues: we can know that an event with a given orderHash was cancelled, but we don't know the amounts involved, and therefore we can't compute the size and price of the order and show it in the list.

We think these are strong indicators that this is something that should be handled in the relayer, since doing it in the dapp is hard and not very efficient.

fragosti commented 5 years ago

@fvictorio I do think you can see from a fill event how much was filled, as seen here: https://github.com/0xProject/0x-monorepo/blob/development/packages/website/ts/blockchain.ts#L729

It seems that you are right about not being able to see the entire order for cancel events (example of decoding: https://github.com/0xProject/0x-monorepo/blob/development/packages/website/ts/blockchain.ts#L325). For cancels the fill amounts are not logged in the solidity code. I cannot think of a good way of getting that information from the blockchain, but I think this solution requires you to store orders in localStorage anyway.

Exchange Solidity Cancel Code:

    /// @dev Updates state with results of cancelling an order.
    ///      State is only updated if the order is currently fillable.
    ///      Otherwise, updating state would have no effect.
    /// @param order that was cancelled.
    /// @param orderHash Hash of order that was cancelled.
    function updateCancelledState(
        Order memory order,
        bytes32 orderHash
    )
        internal
    {
        // Perform cancel
        cancelled[orderHash] = true;

        // Log cancel
        emit Cancel(
            order.makerAddress,
            order.feeRecipientAddress,
            msg.sender,
            orderHash,
            order.makerAssetData,
            order.takerAssetData
        );
    }

Typescript Cancel Event Log Schema:

export interface ExchangeCancelEventArgs extends DecodedLogArgs {
    makerAddress: string;
    feeRecipientAddress: string;
    senderAddress: string;
    orderHash: string;
    makerAssetData: string;
    takerAssetData: string;
}

I think we prefer just getting something working for now and not blocking this project on pushing something onto relayers.

Does this answer your question?

dekz commented 5 years ago

@fvictorio you are correct in that it is a challenge to reconstruct the original information given the partial information from logged events.

w.r.t Cancel you can assess that the order was cancelled entirely, but without the order details you cannot present the particulars (total amounts, total fee amounts etc).

w.r.t Fill purely from the event you cannot determine if the order has any remaining amount. Without the full order details you are unable to query for remaining amount.

Whilst it is possible to reconstruct input data (order details) from the transaction data, it is not foolproof and scalable as it is possible to perform these operations via another contract (and another ABI encoded input).

Any order which did not experience an on-chain interaction, it is not possible to prove its existence (orders which expire/rejected). Any orders which were cancelled via a cancelOrdersUpTo with no on-chain activity would also fall into this category.

As the SRA endpoint is designed for querying and sharing active orders, it is not possible to fulfil an exhaustive order history purely from this endpoint.

I would suggest handling only orders with on-chain activity. A relayer is in a better position to fill in this gap with a private API outside the SRA API.

fvictorio commented 5 years ago

@fragosti Sorry, I wasn't clear in my comment: I meant that we can't know from a Fill event if, after that, the order became fully filled. I think @dekz explained it much better than me.

fragosti commented 5 years ago

@fvictorio OK. We discussed this and think it is best to move forward without the "History" and "Filled" features.

For posterity, the core of the problem is that the "Fill" and "Cancel" events do not contain enough information to implement the UI as currently designed.

Some other options we could consider, but are unfeasible / out-of-scope for now:

  1. Only remove the "History" feature, and implement the "Filled" feature using Local Storage. This assumes that all the orders for the user / maker are created through the UI, and thus can be intercepted. This may not hold true.
  2. Scale down the designs and just use the information we do have as best we can.
  3. Ship 0x-launch-kit-frontend with a backend that is used to store this information.

I think #3 is the way to go in the future. We definitely do not want to expand the scope of the SRA to support this feature, and 0x-launch-kit is supposed to be an SRA implementation. Therefore this logic / state needs to go somewhere else.

Alternatively, we could enforce that 0x-launch-kit-frontend is launched with an API key for a BaaS (like firebase) if the developer wants to enable these features.