-
Notifications
You must be signed in to change notification settings - Fork 112
orderwatch,rpc: Supply parsed contract events in emitted OrderEvents #420
Conversation
48d8593
to
11cb9a2
Compare
@fabioberger While you raise some good points, I want to see if we can find a compromise that could improve QoL for market makers. This whole thing was brought to our attention because of a specific issue. If there is a partial fill followed by a cancel in the same block, the current version of Mesh will emit a This PR asks market makers to do the work of iterating through the contract events associated with a I understand that some token contracts do not emit standardized events (some may not even emit events at all). And I agree that it is infeasible to rely on contract events to understand everything that could happen to an order. However, in this specific case, the related events ( |
@albrow what makes your ask impossible is the following scenario: Within a single block, there are the following state transitions impacting an order:
If there is a transfer before the fill/cancel, we cannot know with 100% certainty what to set the |
@fabioberger how about just adding multiple event kinds to indicate that something else happened before a cancel? Something like: {
"jsonrpc": "2.0",
"method": "mesh_subscription",
"params": {
"subscription": "0xcd0c3e8af590364c09d0fa6a1210faf5",
"result": [
{
"orderHash": "0x96e6eb6174dbf0458686bdae44c9a330d9a9eb563962512a7be545c4ecc13fd4",
"signedOrder": {
// ...
},
+ "kind": ["FILLED", "CANCELLED"],
"fillableTakerAssetAmount": 470000000000,
"contractEvents": [
// ...
]
}
]
}
} If we go this route, we could could probably also rename the |
cac7978
to
05aa3d2
Compare
Heads up @fabioberger this PR was pretty out of date so I rebased it on |
…ents the end state of the order after it's revalidation since the last time it was re-validated
zeroex/order_js.go
Outdated
} | ||
switch c.Kind { | ||
case "ERC20TransferEvent": | ||
m["parameters"] = c.Parameters.(decoder.ERC20TransferEvent).JSValue() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This also really makes me lean toward named interface approach instead of using interface{}
. If we define a ContractEventParameters
interface that inherits from js.Wrapper
we can remove this switch statement and all the type casts, drastically simplifying this code. It would just become:
m := map[string]interface{}{
"blockHash": c.BlockHash.Hex(),
"txHash": c.TxHash.Hex(),
"txIndex": c.TxIndex,
"logIndex": c.LogIndex,
"isRemoved": c.IsRemoved,
"kind": c.Kind,
+ "parameters": c.Parameters.JSValue(),
}
The only other thing we might need to do is check if c.Parameters
is nil.
Fixes: #396
This PR adds the parsed order-relevant contract events to the OrderEvents emitted from the JSON-RPC
orders
subscription. At first we thought we might be able to emit a top-level order event for every contract event. This is however not possible to do in a robust way. The reason for this is two fold:We are only able to execute
eth_call
requests at the boundaries between blocks. We are unable to execute them at particular transaction indexes within blocks. What this means is that we can only query the state of orders before or after all transactions (and their associated events) within a block have been processed. This leaves us with an end state for each order after the block is processed, and we are tasked with figuring out how the individual contract events emitted contributed to this total state update.In order to figure out how each event impacts the orders state, we could fetch the maker's balances/allowances at
currentBlockNumber - 1
, and then implement a simulator that applies every inferred state change of each contract events to these values, as well as thefillableTakerAssetAmount
retrieved from the DB. From these values, we could then compute the state of the order. The glaring issue with this approach is that sady contract events are not guarenteed to line up perfectly with the actual state changes taking place on-chain (e.g., if an order involves transferring a token that doesn't adhere perfectly to say the ERC20 standard, we won't be notified of it's balance/allowance changes). This means we could hit logical inconsistencies between our simulated state of the chain and it's actual state, leading to inaccuracies in how we map the contract events back to order state changes.Thus, I've decided to expose all the decoded order-relevant contract events together with the top-level OrderEvent which represents the overall state change of the order. We do not try to elucidate the impact of each contract event on the order (since this cannot be done reliably with the event watching approach), but we still allow the subscriber to see all discrete events related to their orders so that they can keep track of every fill/cancel as needed for the MMer use-case.
One quirk to this approach is that we might return contract events that could have but ultimately did not impact the order's state (e.g., a maker balance increase where the maker already had a sufficient balance). There is currently no reliable way to filter these out.