From 564b1394183efda36bb00529682a031f62698741 Mon Sep 17 00:00:00 2001 From: fabioberger Date: Sun, 15 Sep 2019 23:14:47 +0200 Subject: [PATCH 01/17] Remove unnecessary snapshotting --- zeroex/ordervalidator/order_validator_test.go | 1 + 1 file changed, 1 insertion(+) diff --git a/zeroex/ordervalidator/order_validator_test.go b/zeroex/ordervalidator/order_validator_test.go index 71d290299..a0d6c7791 100644 --- a/zeroex/ordervalidator/order_validator_test.go +++ b/zeroex/ordervalidator/order_validator_test.go @@ -138,6 +138,7 @@ func TestBatchValidateOffChainCases(t *testing.T) { for _, testCase := range testCases { + ethClient := ethclient.NewClient(rpcClient) signedOrders := []*zeroex.SignedOrder{ From 94d47cfd56509bf4caf1c684d66827e1c204368c Mon Sep 17 00:00:00 2001 From: fabioberger Date: Sun, 15 Sep 2019 23:14:54 +0200 Subject: [PATCH 02/17] Remove duplicate test --- zeroex/ordervalidator/order_validator_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zeroex/ordervalidator/order_validator_test.go b/zeroex/ordervalidator/order_validator_test.go index a0d6c7791..dfc91f0da 100644 --- a/zeroex/ordervalidator/order_validator_test.go +++ b/zeroex/ordervalidator/order_validator_test.go @@ -137,7 +137,7 @@ func TestBatchValidateOffChainCases(t *testing.T) { } for _, testCase := range testCases { - + ethClient := ethclient.NewClient(rpcClient) From a47fbd014f291bde726bb83e15162e25a673bb68 Mon Sep 17 00:00:00 2001 From: fabioberger Date: Tue, 17 Sep 2019 12:07:23 +0200 Subject: [PATCH 03/17] Refactor OrderEvents to include contract events instead of txnHashes --- browser/ts/index.ts | 290 +++++++++++++++++- core/validation.go | 3 +- docs/rpc_api.md | 22 +- rpc/clients/typescript/src/types.ts | 166 +++++++++- rpc/clients/typescript/src/ws_client.ts | 115 ++++++- rpc/clients/typescript/test/ws_client_test.ts | 20 +- zeroex/order.go | 92 ++++-- zeroex/order_js.go | 55 +++- zeroex/order_test.go | 2 +- zeroex/ordervalidator/order_validator.go | 8 +- zeroex/ordervalidator/order_validator_test.go | 12 +- .../orderwatch/{ => decoder}/event_decoder.go | 122 +++++++- .../{ => decoder}/event_decoder_test.go | 22 +- zeroex/orderwatch/order_watcher.go | 130 ++++---- zeroex/orderwatch/topics.go | 3 +- 15 files changed, 937 insertions(+), 125 deletions(-) rename zeroex/orderwatch/{ => decoder}/event_decoder.go (81%) rename zeroex/orderwatch/{ => decoder}/event_decoder_test.go (98%) diff --git a/browser/ts/index.ts b/browser/ts/index.ts index 701f1cd43..d7287b813 100644 --- a/browser/ts/index.ts +++ b/browser/ts/index.ts @@ -125,25 +125,197 @@ interface WrapperSignedOrder { signature: string; } -// The type for order events exposed by MeshWrapper. -interface WrapperOrderEvent { +export interface ERC20TransferEvent { + from: string; + to: string; + value: BigNumber; +} + +export interface WrapperERC20TransferEvent { + from: string; + to: string; + value: string; +} + +export interface ERC20ApprovalEvent { + owner: string; + spender: string; + value: BigNumber; +} + +export interface WrapperERC20ApprovalEvent { + owner: string; + spender: string; + value: string; +} + +export interface ERC721TransferEvent { + from: string; + to: string; + tokenId: BigNumber; +} + +export interface WrapperERC721TransferEvent { + from: string; + to: string; + tokenId: string; +} + +export interface ERC721ApprovalEvent { + owner: string; + approved: string; + tokenId: BigNumber; +} + +export interface WrapperERC721ApprovalEvent { + owner: string; + approved: string; + tokenId: string; +} + +export interface ERC721ApprovalForAllEvent { + owner: string; + operator: string; + approved: boolean; +} + +export interface ExchangeFillEvent { + makerAddress: string; + takerAddress: string; + senderAddress: string; + feeRecipientAddress: string; + makerAssetFilledAmount: BigNumber; + takerAssetFilledAmount: BigNumber; + makerFeePaid: BigNumber; + takerFeePaid: BigNumber; orderHash: string; - signedOrder: WrapperSignedOrder; - kind: string; - fillableTakerAssetAmount: string; - txHashes: string[]; + makerAssetData: string; + takerAssetData: string; } +export interface WrapperExchangeFillEvent { + makerAddress: string; + takerAddress: string; + senderAddress: string; + feeRecipientAddress: string; + makerAssetFilledAmount: string; + takerAssetFilledAmount: string; + makerFeePaid: string; + takerFeePaid: string; + orderHash: string; + makerAssetData: string; + takerAssetData: string; +} + +export interface ExchangeCancelEvent { + makerAddress: string; + senderAddress: string; + feeRecipientAddress: string; + orderHash: string; + makerAssetData: string; + takerAssetData: string; +} + +export interface ExchangeCancelUpToEvent { + makerAddress: string; + senderAddress: string; + orderEpoch: BigNumber; +} + +export interface WrapperExchangeCancelUpToEvent { + makerAddress: string; + senderAddress: string; + orderEpoch: string; +} + +export interface WethWithdrawalEvent { + owner: string; + value: BigNumber; +} + +export interface WrapperWethWithdrawalEvent { + owner: string; + value: string; +} + +export interface WethDepositEvent { + owner: string; + value: BigNumber; +} + +export interface WrapperWethDepositEvent { + owner: string; + value: string; +} + +enum ContractEventKind { + ERC20TransferEvent = 'ERC20TransferEvent', + ERC20ApprovalEvent = 'ERC20ApprovalEvent', + ERC721TransferEvent = 'ERC721TransferEvent', + ERC721ApprovalEvent = 'ERC721ApprovalEvent', + ExchangeFillEvent = 'ExchangeFillEvent', + ExchangeCancelEvent = 'ExchangeCancelEvent', + ExchangeCancelUpToEvent = 'ExchangeCancelUpToEvent', + WethDepositEvent = 'WethDepositEvent', + WethWithdrawalEvent = 'WethWithdrawalEvent', +} + +type WrapperContractEventParameters = WrapperERC20TransferEvent | WrapperERC20ApprovalEvent | WrapperERC721TransferEvent | WrapperERC721ApprovalEvent | WrapperExchangeFillEvent | WrapperExchangeCancelUpToEvent | WrapperWethWithdrawalEvent | WrapperWethDepositEvent | ERC721ApprovalForAllEvent | ExchangeCancelEvent; + +type ContractEventParameters = ERC20TransferEvent | ERC20ApprovalEvent | ERC721TransferEvent | ERC721ApprovalEvent | ExchangeFillEvent | ExchangeCancelUpToEvent | WethWithdrawalEvent | WethDepositEvent | ERC721ApprovalForAllEvent | ExchangeCancelEvent; + /** * Order events are fired by Mesh whenever an order is added, canceled, expired, * or filled. */ +export interface ContractEvent { + blockHash: string; + txHash: string; + txIndex: number; + logIndex: number; + isRemoved: string; + address: string; + kind: ContractEventKind; + parameters: ContractEventParameters; +} + +// The type for order events exposed by MeshWrapper. +export interface WrapperContractEvent { + blockHash: string; + txHash: string; + txIndex: number; + logIndex: number; + isRemoved: string; + address: string; + kind: string; + parameters: WrapperContractEventParameters; +} + +export enum OrderEventKind { + Invalid = 'INVALID', + Added = 'ADDED', + Filled = 'FILLED', + FullyFilled = 'FULLY_FILLED', + Cancelled = 'CANCELLED', + Expired = 'EXPIRED', + Unfunded = 'UNFUNDED', + FillabilityIncreased = 'FILLABILITY_INCREASED', +} + +export interface WrapperOrderEvent { + orderHash: string; + signedOrder: WrapperSignedOrder; + kind: OrderEventKind; + fillableTakerAssetAmount: string; + contractEvents: WrapperContractEvent[]; +} + export interface OrderEvent { orderHash: string; signedOrder: SignedOrder; - kind: string; + kind: OrderEventKind; fillableTakerAssetAmount: BigNumber; - txHashes: string[]; + contractEvents: ContractEvent[]; } // The type for validation results exposed by MeshWrapper. @@ -367,6 +539,107 @@ function wrapperSignedOrderToSignedOrder(wrapperSignedOrder: WrapperSignedOrder) }; } +function wrapperContractEventsToContractEvents(wrapperContractEvents: WrapperContractEvent[]): ContractEvent[] { + const contractEvents: ContractEvent[] = []; + if (wrapperContractEvents === null) { + return contractEvents; + } + wrapperContractEvents.forEach(wrapperContractEvent => { + const kind = wrapperContractEvent.kind as ContractEventKind; + const rawParameters = wrapperContractEvent.parameters; + let parameters: ContractEventParameters; + switch (kind) { + case ContractEventKind.ERC20TransferEvent: + const erc20TransferEvent = rawParameters as WrapperERC20TransferEvent; + parameters = { + from: erc20TransferEvent.from, + to: erc20TransferEvent.to, + value: new BigNumber(erc20TransferEvent.value), + }; + break; + case ContractEventKind.ERC20ApprovalEvent: + const erc20ApprovalEvent = rawParameters as WrapperERC20ApprovalEvent; + parameters = { + owner: erc20ApprovalEvent.owner, + spender: erc20ApprovalEvent.spender, + value: new BigNumber(erc20ApprovalEvent.value), + }; + break; + case ContractEventKind.ERC721TransferEvent: + const erc721TransferEvent = rawParameters as WrapperERC721TransferEvent; + parameters = { + from: erc721TransferEvent.from, + to: erc721TransferEvent.to, + tokenId: new BigNumber(erc721TransferEvent.tokenId), + }; + break; + case ContractEventKind.ERC721ApprovalEvent: + const erc721ApprovalEvent = rawParameters as WrapperERC721ApprovalEvent; + parameters = { + owner: erc721ApprovalEvent.owner, + approved: erc721ApprovalEvent.approved, + tokenId: new BigNumber(erc721ApprovalEvent.tokenId), + }; + break; + case ContractEventKind.ExchangeFillEvent: + const exchangeFillEvent = rawParameters as WrapperExchangeFillEvent; + parameters = { + makerAddress: exchangeFillEvent.makerAddress, + takerAddress: exchangeFillEvent.takerAddress, + senderAddress: exchangeFillEvent.senderAddress, + feeRecipientAddress: exchangeFillEvent.feeRecipientAddress, + makerAssetFilledAmount: new BigNumber(exchangeFillEvent.makerAssetFilledAmount), + takerAssetFilledAmount: new BigNumber(exchangeFillEvent.takerAssetFilledAmount), + makerFeePaid: new BigNumber(exchangeFillEvent.makerFeePaid), + takerFeePaid: new BigNumber(exchangeFillEvent.takerFeePaid), + orderHash: exchangeFillEvent.orderHash, + makerAssetData: exchangeFillEvent.makerAssetData, + takerAssetData: exchangeFillEvent.takerAssetData, + }; + break; + case ContractEventKind.ExchangeCancelEvent: + parameters = rawParameters as ExchangeCancelEvent; + break; + case ContractEventKind.ExchangeCancelUpToEvent: + const exchangeCancelUpToEvent = rawParameters as WrapperExchangeCancelUpToEvent; + parameters = { + makerAddress: exchangeCancelUpToEvent.makerAddress, + senderAddress: exchangeCancelUpToEvent.senderAddress, + orderEpoch: new BigNumber(exchangeCancelUpToEvent.orderEpoch), + }; + break; + case ContractEventKind.WethDepositEvent: + const wethDepositEvent = rawParameters as WrapperWethDepositEvent; + parameters = { + owner: wethDepositEvent.owner, + value: new BigNumber(wethDepositEvent.value), + }; + break; + case ContractEventKind.WethWithdrawalEvent: + const wethWithdrawalEvent = rawParameters as WrapperWethWithdrawalEvent; + parameters = { + owner: wethWithdrawalEvent.owner, + value: new BigNumber(wethWithdrawalEvent.value), + }; + break; + default: + throw new Error(`Unrecognized ContractEventKind: ${kind}`); + } + const contractEvent: ContractEvent = { + blockHash: wrapperContractEvent.blockHash, + txHash: wrapperContractEvent.txHash, + txIndex: wrapperContractEvent.txIndex, + logIndex: wrapperContractEvent.logIndex, + isRemoved: wrapperContractEvent.isRemoved, + address: wrapperContractEvent.address, + kind, + parameters, + }; + contractEvents.push(contractEvent); + }); + return contractEvents; + } + function signedOrderToWrapperSignedOrder(signedOrder: SignedOrder): WrapperSignedOrder { return { ...signedOrder, @@ -384,6 +657,7 @@ function wrapperOrderEventToOrderEvent(wrapperOrderEvent: WrapperOrderEvent): Or ...wrapperOrderEvent, signedOrder: wrapperSignedOrderToSignedOrder(wrapperOrderEvent.signedOrder), fillableTakerAssetAmount: new BigNumber(wrapperOrderEvent.fillableTakerAssetAmount), + contractEvents: wrapperContractEventsToContractEvents(wrapperOrderEvent.contractEvents), }; } diff --git a/core/validation.go b/core/validation.go index 47418e796..a06e80e81 100644 --- a/core/validation.go +++ b/core/validation.go @@ -10,6 +10,7 @@ import ( "github.com/0xProject/0x-mesh/p2p" "github.com/0xProject/0x-mesh/zeroex" "github.com/0xProject/0x-mesh/zeroex/ordervalidator" + "github.com/ethereum/go-ethereum/rpc" log "github.com/sirupsen/logrus" "github.com/xeipuuv/gojsonschema" ) @@ -192,7 +193,7 @@ func (app *App) validateOrders(orders []*zeroex.SignedOrder) (*ordervalidator.Va validMeshOrders = append(validMeshOrders, order) } areNewOrders := true - zeroexResults := app.orderValidator.BatchValidate(validMeshOrders, areNewOrders) + zeroexResults := app.orderValidator.BatchValidate(validMeshOrders, areNewOrders, rpc.LatestBlockNumber) zeroexResults.Accepted = append(zeroexResults.Accepted, results.Accepted...) zeroexResults.Rejected = append(zeroexResults.Rejected, results.Rejected...) return zeroexResults, nil diff --git a/docs/rpc_api.md b/docs/rpc_api.md index f8c2fae71..f7fad5e47 100644 --- a/docs/rpc_api.md +++ b/docs/rpc_api.md @@ -213,7 +213,7 @@ Gets certain configurations and stats about a Mesh node. Allows the caller to subscribe to a stream of `OrderEvents`. An `OrderEvent` contains either newly discovered orders found by Mesh via the P2P network, or updates to the fillability of a previously discovered order (e.g., if an order gets filled, cancelled, expired, etc...). `OrderEvent`s _do not_ correspond 1-to-1 to smart contract events. Rather, an `OrderEvent` about an orders fillability change represents the aggregate change to it's fillability given _all_ the transactions included within the most recently mined block. -**Example:** If an order is both `filled` and `cancelled` within a single block, only a cancellation `OrderEvent` will be emitted (since this is the final state of the order after this block is mined). The cancellation `OrderEvent` _will_ however list the hashes of all transactions that impacted the order's fillability within the block. Using these `txHashes`, one can fetch all the individual smart contract events impacting to an order if they are needed. +**Example:** If an order is both `filled` and `cancelled` within a single block, only a cancellation `OrderEvent` will be emitted (since this is the final state of the order after this block is mined). The cancellation `OrderEvent` _will_ however list the contract events intercepted that could have impacted this orders fillability. This list will include both the fill event and cancellation event. Mesh has implemented subscriptions in the [same manner as Geth](https://github.com/ethereum/go-ethereum/wiki/RPC-PUB-SUB). In order to start a subscription, you must send the following payload: @@ -267,7 +267,25 @@ Mesh has implemented subscriptions in the [same manner as Geth](https://github.c }, "kind": "CANCELLED", "fillableTakerAssetAmount": 0, - "txHashes": ["0x9e6830a7044b39e107f410e4f765995fd0d3d69d5c3b3582a1701b9d68167560"] + "contractEvents": [ + { + "blockHash": "0x1be2eb6174dbf0458686bdae44c9a330d9a9eb563962512a7be545c4ec11a4d2", + "txHash": "0xbcce172374dbf0458686bdae44c9a330d9a9eb563962512a7be545c4ec232e3a", + "txIndex": 23, + "logIndex": 0, + "isRemoved": false, + "address": "0x4f833a24e1f95d70f028921e27040ca56e09ab0b", + "kind": "ExchangeCancelEvent", + "parameters": { + "makerAddress": "0x50f84bbee6fb250d6f49e854fa280445369d64d9", + "senderAddress": "0x0000000000000000000000000000000000000000", + "feeRecipientAddress": "0xa258b39954cef5cb142fd567a46cddb31a670124", + "orderHash": "0x96e6eb6174dbf0458686bdae44c9a330d9a9eb563962512a7be545c4ecc13fd4", + "makerAssetData": "0xf47261b00000000000000000000000000f5d2fb29fb7d3cfee444a200298f468908cc942", + "takerAssetData": "0xf47261b0000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2" + } + } + ] } ] } diff --git a/rpc/clients/typescript/src/types.ts b/rpc/clients/typescript/src/types.ts index 8097aa6ee..cd4bd7bdc 100644 --- a/rpc/clients/typescript/src/types.ts +++ b/rpc/clients/typescript/src/types.ts @@ -1,5 +1,6 @@ import { SignedOrder } from '@0x/types'; import { BigNumber } from '@0x/utils'; +import { string } from 'prop-types'; /** * WebSocketClient configs @@ -50,6 +51,167 @@ export interface StringifiedSignedOrder { signature: string; } +export interface ERC20TransferEvent { + from: string; + to: string; + value: BigNumber; +} + +export interface StringifiedERC20TransferEvent { + from: string; + to: string; + value: string; +} + +export interface ERC20ApprovalEvent { + owner: string; + spender: string; + value: BigNumber; +} + +export interface StringifiedERC20ApprovalEvent { + owner: string; + spender: string; + value: string; +} + +export interface ERC721TransferEvent { + from: string; + to: string; + tokenId: BigNumber; +} + +export interface StringifiedERC721TransferEvent { + from: string; + to: string; + tokenId: string; +} + +export interface ERC721ApprovalEvent { + owner: string; + approved: string; + tokenId: BigNumber; +} + +export interface StringifiedERC721ApprovalEvent { + owner: string; + approved: string; + tokenId: string; +} + +export interface ERC721ApprovalForAllEvent { + owner: string; + operator: string; + approved: boolean; +} + +export interface ExchangeFillEvent { + makerAddress: string; + takerAddress: string; + senderAddress: string; + feeRecipientAddress: string; + makerAssetFilledAmount: BigNumber; + takerAssetFilledAmount: BigNumber; + makerFeePaid: BigNumber; + takerFeePaid: BigNumber; + orderHash: string; + makerAssetData: string; + takerAssetData: string; +} + +export interface StringifiedExchangeFillEvent { + makerAddress: string; + takerAddress: string; + senderAddress: string; + feeRecipientAddress: string; + makerAssetFilledAmount: string; + takerAssetFilledAmount: string; + makerFeePaid: string; + takerFeePaid: string; + orderHash: string; + makerAssetData: string; + takerAssetData: string; +} + +export interface ExchangeCancelEvent { + makerAddress: string; + senderAddress: string; + feeRecipientAddress: string; + orderHash: string; + makerAssetData: string; + takerAssetData: string; +} + +export interface ExchangeCancelUpToEvent { + makerAddress: string; + senderAddress: string; + orderEpoch: BigNumber; +} + +export interface StringifiedExchangeCancelUpToEvent { + makerAddress: string; + senderAddress: string; + orderEpoch: string; +} + +export interface WethWithdrawalEvent { + owner: string; + value: BigNumber; +} + +export interface StringifiedWethWithdrawalEvent { + owner: string; + value: string; +} + +export interface WethDepositEvent { + owner: string; + value: BigNumber; +} + +export interface StringifiedWethDepositEvent { + owner: string; + value: string; +} + +export enum ContractEventKind { + ERC20TransferEvent = 'ERC20TransferEvent', + ERC20ApprovalEvent = 'ERC20ApprovalEvent', + ERC721TransferEvent = 'ERC721TransferEvent', + ERC721ApprovalEvent = 'ERC721ApprovalEvent', + ExchangeFillEvent = 'ExchangeFillEvent', + ExchangeCancelEvent = 'ExchangeCancelEvent', + ExchangeCancelUpToEvent = 'ExchangeCancelUpToEvent', + WethDepositEvent = 'WethDepositEvent', + WethWithdrawalEvent = 'WethWithdrawalEvent', +} + +export type StringifiedContractEventParameters = StringifiedERC20TransferEvent | StringifiedERC20ApprovalEvent | StringifiedERC721TransferEvent | StringifiedERC721ApprovalEvent | StringifiedExchangeFillEvent | StringifiedExchangeCancelUpToEvent | StringifiedWethWithdrawalEvent | StringifiedWethDepositEvent | ERC721ApprovalForAllEvent | ExchangeCancelEvent; + +export interface StringifiedContractEvent { + blockHash: string; + txHash: string; + txIndex: number; + logIndex: number; + isRemoved: string; + address: string; + kind: string; + parameters: StringifiedContractEventParameters; +} + +export type ContractEventParameters = ERC20TransferEvent | ERC20ApprovalEvent | ERC721TransferEvent | ERC721ApprovalEvent | ExchangeFillEvent | ExchangeCancelUpToEvent | WethWithdrawalEvent | WethDepositEvent | ERC721ApprovalForAllEvent | ExchangeCancelEvent; + +export interface ContractEvent { + blockHash: string; + txHash: string; + txIndex: number; + logIndex: number; + isRemoved: string; + address: string; + kind: ContractEventKind; + parameters: ContractEventParameters; +} + export enum OrderEventKind { Invalid = 'INVALID', Added = 'ADDED', @@ -76,7 +238,7 @@ export interface RawOrderEvent { signedOrder: StringifiedSignedOrder; kind: OrderEventKind; fillableTakerAssetAmount: string; - txHashes: string[]; + contractEvents: StringifiedContractEvent[]; } export interface OrderEvent { @@ -84,7 +246,7 @@ export interface OrderEvent { signedOrder: SignedOrder; kind: OrderEventKind; fillableTakerAssetAmount: BigNumber; - txHashes: string[]; + contractEvents: ContractEvent[]; } export interface RawAcceptedOrderInfo { diff --git a/rpc/clients/typescript/src/ws_client.ts b/rpc/clients/typescript/src/ws_client.ts index f27bb797c..ab1f36a71 100644 --- a/rpc/clients/typescript/src/ws_client.ts +++ b/rpc/clients/typescript/src/ws_client.ts @@ -8,6 +8,10 @@ import * as WebSocket from 'websocket'; import { AcceptedOrderInfo, + ContractEvent, + ContractEventKind, + ContractEventParameters, + ExchangeCancelEvent, GetOrdersResponse, GetStatsResponse, HeartbeatEventPayload, @@ -19,6 +23,15 @@ import { RawOrderInfo, RawValidationResults, RejectedOrderInfo, + StringifiedContractEvent, + StringifiedERC20ApprovalEvent, + StringifiedERC20TransferEvent, + StringifiedERC721ApprovalEvent, + StringifiedERC721TransferEvent, + StringifiedExchangeCancelUpToEvent, + StringifiedExchangeFillEvent, + StringifiedWethDepositEvent, + StringifiedWethWithdrawalEvent, ValidationResults, WSOpts, } from './types'; @@ -69,6 +82,106 @@ export class WSClient { }); return orderInfos; } + private static _convertStringifiedContractEvents(rawContractEvents: StringifiedContractEvent[]): ContractEvent[] { + const contractEvents: ContractEvent[] = []; + if (rawContractEvents === null) { + return contractEvents; + } + rawContractEvents.forEach(rawContractEvent => { + const kind = rawContractEvent.kind as ContractEventKind; + const rawParameters = rawContractEvent.parameters; + let parameters: ContractEventParameters; + switch (kind) { + case ContractEventKind.ERC20TransferEvent: + const erc20TransferEvent = rawParameters as StringifiedERC20TransferEvent; + parameters = { + from: erc20TransferEvent.from, + to: erc20TransferEvent.to, + value: new BigNumber(erc20TransferEvent.value), + }; + break; + case ContractEventKind.ERC20ApprovalEvent: + const erc20ApprovalEvent = rawParameters as StringifiedERC20ApprovalEvent; + parameters = { + owner: erc20ApprovalEvent.owner, + spender: erc20ApprovalEvent.spender, + value: new BigNumber(erc20ApprovalEvent.value), + }; + break; + case ContractEventKind.ERC721TransferEvent: + const erc721TransferEvent = rawParameters as StringifiedERC721TransferEvent; + parameters = { + from: erc721TransferEvent.from, + to: erc721TransferEvent.to, + tokenId: new BigNumber(erc721TransferEvent.tokenId), + }; + break; + case ContractEventKind.ERC721ApprovalEvent: + const erc721ApprovalEvent = rawParameters as StringifiedERC721ApprovalEvent; + parameters = { + owner: erc721ApprovalEvent.owner, + approved: erc721ApprovalEvent.approved, + tokenId: new BigNumber(erc721ApprovalEvent.tokenId), + }; + break; + case ContractEventKind.ExchangeFillEvent: + const exchangeFillEvent = rawParameters as StringifiedExchangeFillEvent; + parameters = { + makerAddress: exchangeFillEvent.makerAddress, + takerAddress: exchangeFillEvent.takerAddress, + senderAddress: exchangeFillEvent.senderAddress, + feeRecipientAddress: exchangeFillEvent.feeRecipientAddress, + makerAssetFilledAmount: new BigNumber(exchangeFillEvent.makerAssetFilledAmount), + takerAssetFilledAmount: new BigNumber(exchangeFillEvent.takerAssetFilledAmount), + makerFeePaid: new BigNumber(exchangeFillEvent.makerFeePaid), + takerFeePaid: new BigNumber(exchangeFillEvent.takerFeePaid), + orderHash: exchangeFillEvent.orderHash, + makerAssetData: exchangeFillEvent.makerAssetData, + takerAssetData: exchangeFillEvent.takerAssetData, + }; + break; + case ContractEventKind.ExchangeCancelEvent: + parameters = rawParameters as ExchangeCancelEvent; + break; + case ContractEventKind.ExchangeCancelUpToEvent: + const exchangeCancelUpToEvent = rawParameters as StringifiedExchangeCancelUpToEvent; + parameters = { + makerAddress: exchangeCancelUpToEvent.makerAddress, + senderAddress: exchangeCancelUpToEvent.senderAddress, + orderEpoch: new BigNumber(exchangeCancelUpToEvent.orderEpoch), + }; + break; + case ContractEventKind.WethDepositEvent: + const wethDepositEvent = rawParameters as StringifiedWethDepositEvent; + parameters = { + owner: wethDepositEvent.owner, + value: new BigNumber(wethDepositEvent.value), + }; + break; + case ContractEventKind.WethWithdrawalEvent: + const wethWithdrawalEvent = rawParameters as StringifiedWethWithdrawalEvent; + parameters = { + owner: wethWithdrawalEvent.owner, + value: new BigNumber(wethWithdrawalEvent.value), + }; + break; + default: + throw new Error(`Unrecognized ContractEventKind: ${kind}`); + } + const contractEvent: ContractEvent = { + blockHash: rawContractEvent.blockHash, + txHash: rawContractEvent.txHash, + txIndex: rawContractEvent.txIndex, + logIndex: rawContractEvent.logIndex, + isRemoved: rawContractEvent.isRemoved, + address: rawContractEvent.address, + kind, + parameters, + }; + contractEvents.push(contractEvent); + }); + return contractEvents; + } /** * Instantiates a new WSClient instance * @param url WS server endpoint @@ -169,7 +282,7 @@ export class WSClient { signedOrder: orderParsingUtils.convertOrderStringFieldsToBigNumber(rawOrderEvent.signedOrder), kind: rawOrderEvent.kind, fillableTakerAssetAmount: new BigNumber(rawOrderEvent.fillableTakerAssetAmount), - txHashes: rawOrderEvent.txHashes, + contractEvents: WSClient._convertStringifiedContractEvents(rawOrderEvent.contractEvents), }; orderEvents.push(orderEvent); }); diff --git a/rpc/clients/typescript/test/ws_client_test.ts b/rpc/clients/typescript/test/ws_client_test.ts index 0f868b3ca..0459435c9 100644 --- a/rpc/clients/typescript/test/ws_client_test.ts +++ b/rpc/clients/typescript/test/ws_client_test.ts @@ -323,7 +323,25 @@ describe('WSClient', () => { }, "kind": "CANCELLED", "fillableTakerAssetAmount": 0, - "txHashes": ["0x9e6830a7044b39e107f410e4f765995fd0d3d69d5c3b3582a1701b9d68167560"] + "contractEvents": [ + { + "blockHash": "0x1be2eb6174dbf0458686bdae44c9a330d9a9eb563962512a7be545c4ec11a4d2", + "txHash": "0xbcce172374dbf0458686bdae44c9a330d9a9eb563962512a7be545c4ec232e3a", + "txIndex": 23, + "logIndex": 0, + "isRemoved": false, + "address": "0x4f833a24e1f95d70f028921e27040ca56e09ab0b", + "kind": "ExchangeCancelEvent", + "parameters": { + "makerAddress": "0x50f84bbee6fb250d6f49e854fa280445369d64d9", + "senderAddress": "0x0000000000000000000000000000000000000000", + "feeRecipientAddress": "0xa258b39954cef5cb142fd567a46cddb31a670124", + "orderHash": "0x96e6eb6174dbf0458686bdae44c9a330d9a9eb563962512a7be545c4ecc13fd4", + "makerAssetData": "0xf47261b00000000000000000000000000f5d2fb29fb7d3cfee444a200298f468908cc942", + "takerAssetData": "0xf47261b0000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2" + } + } + ] } ] } diff --git a/zeroex/order.go b/zeroex/order.go index 6a9a761f2..10897e25e 100644 --- a/zeroex/order.go +++ b/zeroex/order.go @@ -8,6 +8,7 @@ import ( "strings" "github.com/0xProject/0x-mesh/ethereum" + "github.com/0xProject/0x-mesh/zeroex/orderwatch/decoder" "github.com/0xProject/0x-mesh/ethereum/wrappers" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/math" @@ -74,6 +75,68 @@ const ( OSInvalidTakerAssetData ) +// ContractEvent is an event emitted by a smart contract +type ContractEvent struct { + BlockHash common.Hash + TxHash common.Hash + TxIndex uint + LogIndex uint + IsRemoved bool + Address common.Address + Kind string + Parameters interface{} +} + +// MarshalJSON implements a custom JSON marshaller for the ContractEvent type +func (c ContractEvent) MarshalJSON() ([]byte, error) { + m := map[string]interface{}{ + "blockHash": c.BlockHash.Hex(), + "txHash": c.TxHash.Hex(), + "txIndex": c.TxIndex, + "logIndex": c.LogIndex, + "isRemoved": c.IsRemoved, + "address": c.Address, + "kind": c.Kind, + } + switch c.Kind { + case "ERC20TransferEvent": + m["parameters"] = c.Parameters.(decoder.ERC20TransferEvent) + return json.Marshal(m) + + case "ERC20ApprovalEvent": + m["parameters"] = c.Parameters.(decoder.ERC20ApprovalEvent) + + case "ERC721TransferEvent": + m["parameters"] = c.Parameters.(decoder.ERC721TransferEvent) + + case "ERC721ApprovalEvent": + m["parameters"] = c.Parameters.(decoder.ERC721ApprovalEvent) + + case "ERC721ApprovalForAllEvent": + m["parameters"] = c.Parameters.(decoder.ERC721ApprovalForAllEvent) + + case "WethWithdrawalEvent": + m["parameters"] = c.Parameters.(decoder.WethWithdrawalEvent) + + case "WethDepositEvent": + m["parameters"] = c.Parameters.(decoder.WethDepositEvent) + + case "ExchangeFillEvent": + m["parameters"] = c.Parameters.(decoder.ExchangeFillEvent) + + case "ExchangeCancelEvent": + m["parameters"] = c.Parameters.(decoder.ExchangeCancelEvent) + + case "ExchangeCancelUpToEvent": + m["parameters"] = c.Parameters.(decoder.ExchangeCancelUpToEvent) + + default: + panic(fmt.Sprintf("Unrecognized event encountered: %s", c.Kind)) + } + + return json.Marshal(m) +} + // OrderEvent is the order event emitted by Mesh nodes on the "orders" topic // when calling JSON-RPC method `mesh_subscribe` type OrderEvent struct { @@ -81,31 +144,28 @@ type OrderEvent struct { SignedOrder *SignedOrder `json:"signedOrder"` Kind OrderEventKind `json:"kind"` FillableTakerAssetAmount *big.Int `json:"fillableTakerAssetAmount"` - // The hashes of the Ethereum transactions that caused the order status to change. - // Could be because of multiple transactions, not just a single transaction. - TxHashes []common.Hash `json:"txHashes"` + // All the contract events that triggered this orders re-evaluation. They did not + // all necessarily cause the orders state change itself, only it's re-evaluation. + // Since it's state _did_ change, at least one of them did cause the actual state change. + ContractEvents []*ContractEvent `json:"contractEvents"` } type orderEventJSON struct { - OrderHash string `json:"orderHash"` - SignedOrder *SignedOrder `json:"signedOrder"` - Kind string `json:"kind"` - FillableTakerAssetAmount string `json:"fillableTakerAssetAmount"` - TxHashes []string `json:"txHashes"` + OrderHash string `json:"orderHash"` + SignedOrder *SignedOrder `json:"signedOrder"` + Kind string `json:"kind"` + FillableTakerAssetAmount string `json:"fillableTakerAssetAmount"` + ContractEvents []*ContractEvent `json:"contractEvents"` } // MarshalJSON implements a custom JSON marshaller for the OrderEvent type func (o OrderEvent) MarshalJSON() ([]byte, error) { - stringifiedTxHashes := []string{} - for _, txHash := range o.TxHashes { - stringifiedTxHashes = append(stringifiedTxHashes, txHash.Hex()) - } return json.Marshal(map[string]interface{}{ "orderHash": o.OrderHash.Hex(), "signedOrder": o.SignedOrder, "kind": o.Kind, "fillableTakerAssetAmount": o.FillableTakerAssetAmount.String(), - "txHashes": stringifiedTxHashes, + "contractEvents": o.ContractEvents, }) } @@ -128,11 +188,7 @@ func (o *OrderEvent) fromOrderEventJSON(orderEventJSON orderEventJSON) error { if !ok { return errors.New("Invalid uint256 number encountered for FillableTakerAssetAmount") } - txHashes := []common.Hash{} - for _, txHash := range orderEventJSON.TxHashes { - txHashes = append(txHashes, common.HexToHash(txHash)) - } - o.TxHashes = txHashes + o.ContractEvents = orderEventJSON.ContractEvents return nil } diff --git a/zeroex/order_js.go b/zeroex/order_js.go index fdc421f01..a2e32dfa3 100644 --- a/zeroex/order_js.go +++ b/zeroex/order_js.go @@ -7,20 +7,21 @@ import ( "strings" "syscall/js" + "github.com/0xProject/0x-mesh/ethereum" "github.com/ethereum/go-ethereum/common" ) func (o OrderEvent) JSValue() js.Value { - stringifiedTxHashes := []interface{}{} - for _, txHash := range o.TxHashes { - stringifiedTxHashes = append(stringifiedTxHashes, txHash.Hex()) + contractEventsJSValues := []js.Value{} + for _, contractEvent := range o.ContractEvents { + contractEventsJSValues = append(contractEventsJSValues, contractEvent.JSValue()) } return js.ValueOf(map[string]interface{}{ "orderHash": o.OrderHash.Hex(), "signedOrder": o.SignedOrder.JSValue(), "kind": string(o.Kind), "fillableTakerAssetAmount": o.FillableTakerAssetAmount.String(), - "txHashes": stringifiedTxHashes, + "contractEvents": contractEventsJSValues, }) } @@ -55,3 +56,49 @@ func (s SignedOrder) JSValue() js.Value { "signature": signature, }) } + +func (c ContractEvent) JSValue() js.Value { + m := map[string]interface{}{ + "blockHash": c.BlockHash.Hex(), + "txHash": c.TxHash.Hex(), + "txIndex": c.TxIndex, + "logIndex": c.LogIndex, + "isRemoved": c.IsRemoved, + "kind": c.Kind, + } + switch c.Kind { + case "ERC20TransferEvent": + m["parameters"] = c.Parameters.(ethereum.ERC20TransferEvent) + + case "ERC20ApprovalEvent": + m["parameters"] = c.Parameters.(ethereum.ERC20ApprovalEvent) + + case "ERC721TransferEvent": + m["parameters"] = c.Parameters.(ethereum.ERC721TransferEvent) + + case "ERC721ApprovalEvent": + m["parameters"] = c.Parameters.(ethereum.ERC721ApprovalEvent) + + case "ERC721ApprovalForAllEvent": + m["parameters"] = c.Parameters.(ethereum.ERC721ApprovalForAllEvent) + + case "WethWithdrawalEvent": + m["parameters"] = c.Parameters.(ethereum.WethWithdrawalEvent) + + case "WethDepositEvent": + m["parameters"] = c.Parameters.(ethereum.WethDepositEvent) + + case "ExchangeFillEvent": + m["parameters"] = c.Parameters.(ethereum.ExchangeFillEvent) + + case "ExchangeCancelEvent": + m["parameters"] = c.Parameters.(ethereum.ExchangeCancelEvent) + + case "ExchangeCancelUpToEvent": + m["parameters"] = c.Parameters.(ethereum.ExchangeCancelUpToEvent) + + default: + panic(fmt.Sprintf("Unrecognized event encountered: %s", c.Kind)) + } + return js.ValueOf(m) +} diff --git a/zeroex/order_test.go b/zeroex/order_test.go index 760171f37..3060b7560 100644 --- a/zeroex/order_test.go +++ b/zeroex/order_test.go @@ -58,7 +58,7 @@ func TestMarshalUnmarshalOrderEvent(t *testing.T) { SignedOrder: signedOrder, Kind: EKOrderAdded, FillableTakerAssetAmount: big.NewInt(2000), - TxHashes: []common.Hash{common.HexToHash("0x3fcd58a6613265e2b0deba902d7ff693f330a0af6e5b04805b44bbffd8a415d3")}, + ContractEvents: []*ContractEvent{}, } buf := &bytes.Buffer{} diff --git a/zeroex/ordervalidator/order_validator.go b/zeroex/ordervalidator/order_validator.go index 9263d3eda..a1f65f752 100644 --- a/zeroex/ordervalidator/order_validator.go +++ b/zeroex/ordervalidator/order_validator.go @@ -22,6 +22,7 @@ import ( "github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/common/math" "github.com/ethereum/go-ethereum/ethclient" + "github.com/ethereum/go-ethereum/rpc" "github.com/jpillora/backoff" log "github.com/sirupsen/logrus" ) @@ -273,7 +274,7 @@ func New(ethClient *ethclient.Client, networkID int, maxRequestContentLength int // requests concurrently. If a request fails, re-attempt it up to four times before giving up. // If it some requests fail, this method still returns whatever order information it was able to // retrieve. -func (o *OrderValidator) BatchValidate(rawSignedOrders []*zeroex.SignedOrder, areNewOrders bool) *ValidationResults { +func (o *OrderValidator) BatchValidate(rawSignedOrders []*zeroex.SignedOrder, areNewOrders bool, blockNumber rpc.BlockNumber) *ValidationResults { if len(rawSignedOrders) == 0 { return &ValidationResults{} } @@ -335,6 +336,11 @@ func (o *OrderValidator) BatchValidate(rawSignedOrders []*zeroex.SignedOrder, ar Pending: false, Context: ctx, } + if blockNumber == rpc.PendingBlockNumber { + opts.Pending = true + } else if blockNumber != rpc.LatestBlockNumber { + opts.BlockNumber = big.NewInt(int64(blockNumber)) + } results, err := o.devUtils.GetOrderRelevantStates(opts, orders, signatures) if err != nil { diff --git a/zeroex/ordervalidator/order_validator_test.go b/zeroex/ordervalidator/order_validator_test.go index dfc91f0da..020ee1b69 100644 --- a/zeroex/ordervalidator/order_validator_test.go +++ b/zeroex/ordervalidator/order_validator_test.go @@ -23,6 +23,7 @@ import ( "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/ethclient" ethrpc "github.com/ethereum/go-ethereum/rpc" + "github.com/ethereum/go-ethereum/rpc" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -137,8 +138,7 @@ func TestBatchValidateOffChainCases(t *testing.T) { } for _, testCase := range testCases { - - + ethClient := ethclient.NewClient(rpcClient) signedOrders := []*zeroex.SignedOrder{ @@ -175,7 +175,7 @@ func TestBatchValidateAValidOrder(t *testing.T) { orderValidator, err := New(ethClient, constants.TestNetworkID, constants.TestMaxContentLength, 0) require.NoError(t, err) - validationResults := orderValidator.BatchValidate(signedOrders, areNewOrders) + validationResults := orderValidator.BatchValidate(signedOrders, areNewOrders, rpc.LatestBlockNumber) assert.Len(t, validationResults.Accepted, 1) require.Len(t, validationResults.Rejected, 0) orderHash, err := signedOrder.ComputeOrderHash() @@ -200,7 +200,7 @@ func TestBatchValidateSignatureInvalid(t *testing.T) { orderValidator, err := New(ethClient, constants.TestNetworkID, constants.TestMaxContentLength, 0) require.NoError(t, err) - validationResults := orderValidator.BatchValidate(signedOrders, areNewOrders) + validationResults := orderValidator.BatchValidate(signedOrders, areNewOrders, rpc.LatestBlockNumber) assert.Len(t, validationResults.Accepted, 0) require.Len(t, validationResults.Rejected, 1) assert.Equal(t, ROInvalidSignature, validationResults.Rejected[0].Status) @@ -225,7 +225,7 @@ func TestBatchValidateUnregisteredCoordinatorSoftCancels(t *testing.T) { orderValidator, err := New(ethClient, constants.TestNetworkID, constants.TestMaxContentLength, 0) require.NoError(t, err) - validationResults := orderValidator.BatchValidate(signedOrders, areNewOrders) + validationResults := orderValidator.BatchValidate(signedOrders, areNewOrders, rpc.LatestBlockNumber) assert.Len(t, validationResults.Accepted, 0) require.Len(t, validationResults.Rejected, 1) assert.Equal(t, ROCoordinatorEndpointNotFound, validationResults.Rejected[0].Status) @@ -277,7 +277,7 @@ func TestBatchValidateCoordinatorSoftCancels(t *testing.T) { _, err = orderValidator.coordinatorRegistry.SetCoordinatorEndpoint(opts, testServer.URL) require.NoError(t, err) - validationResults := orderValidator.BatchValidate(signedOrders, areNewOrders) + validationResults := orderValidator.BatchValidate(signedOrders, areNewOrders, rpc.LatestBlockNumber) assert.Len(t, validationResults.Accepted, 0) require.Len(t, validationResults.Rejected, 1) assert.Equal(t, ROCoordinatorSoftCancelled, validationResults.Rejected[0].Status) diff --git a/zeroex/orderwatch/event_decoder.go b/zeroex/orderwatch/decoder/event_decoder.go similarity index 81% rename from zeroex/orderwatch/event_decoder.go rename to zeroex/orderwatch/decoder/event_decoder.go index e01032af4..0fc1e7168 100644 --- a/zeroex/orderwatch/event_decoder.go +++ b/zeroex/orderwatch/decoder/event_decoder.go @@ -1,6 +1,7 @@ -package orderwatch +package decoder import ( + "encoding/json" "errors" "fmt" "math/big" @@ -40,6 +41,15 @@ type ERC20TransferEvent struct { Value *big.Int } +// MarshalJSON implements a custom JSON marshaller for the ERC20TransferEvent type +func (e ERC20TransferEvent) MarshalJSON() ([]byte, error) { + return json.Marshal(map[string]interface{}{ + "from": e.From.Hex(), + "to": e.To.Hex(), + "value": e.Value.String(), + }) +} + // ERC20ApprovalEvent represents an ERC20 Approval event type ERC20ApprovalEvent struct { Owner common.Address @@ -47,6 +57,15 @@ type ERC20ApprovalEvent struct { Value *big.Int } +// MarshalJSON implements a custom JSON marshaller for the ERC20ApprovalEvent type +func (e ERC20ApprovalEvent) MarshalJSON() ([]byte, error) { + return json.Marshal(map[string]interface{}{ + "owner": e.Owner.Hex(), + "spender": e.Spender.Hex(), + "value": e.Value.String(), + }) +} + // ERC721TransferEvent represents an ERC721 Transfer event type ERC721TransferEvent struct { From common.Address @@ -54,6 +73,15 @@ type ERC721TransferEvent struct { TokenId *big.Int } +// MarshalJSON implements a custom JSON marshaller for the ERC721TransferEvent type +func (e ERC721TransferEvent) MarshalJSON() ([]byte, error) { + return json.Marshal(map[string]interface{}{ + "from": e.From.Hex(), + "to": e.To.Hex(), + "tokenId": e.TokenId.String(), + }) +} + // ERC721ApprovalEvent represents an ERC721 Approval event type ERC721ApprovalEvent struct { Owner common.Address @@ -61,6 +89,15 @@ type ERC721ApprovalEvent struct { TokenId *big.Int } +// MarshalJSON implements a custom JSON marshaller for the ERC721ApprovalEvent type +func (e ERC721ApprovalEvent) MarshalJSON() ([]byte, error) { + return json.Marshal(map[string]interface{}{ + "owner": e.Owner.Hex(), + "approved": e.Approved.Hex(), + "tokenId": e.TokenId.String(), + }) +} + // ERC721ApprovalForAllEvent represents an ERC721 ApprovalForAll event type ERC721ApprovalForAllEvent struct { Owner common.Address @@ -68,6 +105,15 @@ type ERC721ApprovalForAllEvent struct { Approved bool } +// MarshalJSON implements a custom JSON marshaller for the ERC721ApprovalForAllEvent type +func (e ERC721ApprovalForAllEvent) MarshalJSON() ([]byte, error) { + return json.Marshal(map[string]interface{}{ + "owner": e.Owner.Hex(), + "operator": e.Operator.Hex(), + "approved": e.Approved, + }) +} + // ExchangeFillEvent represents a 0x Exchange Fill event type ExchangeFillEvent struct { MakerAddress common.Address @@ -83,6 +129,31 @@ type ExchangeFillEvent struct { TakerAssetData []byte } +// MarshalJSON implements a custom JSON marshaller for the ExchangeFillEvent type +func (e ExchangeFillEvent) MarshalJSON() ([]byte, error) { + makerAssetData := "" + if len(e.MakerAssetData) != 0 { + makerAssetData = fmt.Sprintf("0x%s", common.Bytes2Hex(e.MakerAssetData)) + } + takerAssetData := "" + if len(e.TakerAssetData) != 0 { + takerAssetData = fmt.Sprintf("0x%s", common.Bytes2Hex(e.TakerAssetData)) + } + return json.Marshal(map[string]interface{}{ + "makerAddress": e.MakerAddress.Hex(), + "takerAddress": e.TakerAddress.Hex(), + "senderAddress": e.SenderAddress.Hex(), + "feeRecipientAddress": e.FeeRecipientAddress.Hex(), + "makerAssetFilledAmount": e.MakerAssetFilledAmount.String(), + "takerAssetFilledAmount": e.TakerAssetFilledAmount.String(), + "makerFeePaid": e.MakerFeePaid.String(), + "takerFeePaid": e.TakerFeePaid.String(), + "orderHash": e.OrderHash.Hex(), + "makerAssetData": makerAssetData, + "takerAssetData": takerAssetData, + }) +} + // ExchangeCancelEvent represents a 0x Exchange Cancel event type ExchangeCancelEvent struct { MakerAddress common.Address @@ -93,6 +164,26 @@ type ExchangeCancelEvent struct { TakerAssetData []byte } +// MarshalJSON implements a custom JSON marshaller for the ExchangeCancelEvent type +func (e ExchangeCancelEvent) MarshalJSON() ([]byte, error) { + makerAssetData := "" + if len(e.MakerAssetData) != 0 { + makerAssetData = fmt.Sprintf("0x%s", common.Bytes2Hex(e.MakerAssetData)) + } + takerAssetData := "" + if len(e.TakerAssetData) != 0 { + takerAssetData = fmt.Sprintf("0x%s", common.Bytes2Hex(e.TakerAssetData)) + } + return json.Marshal(map[string]interface{}{ + "makerAddress": e.MakerAddress.Hex(), + "senderAddress": e.SenderAddress.Hex(), + "feeRecipientAddress": e.FeeRecipientAddress.Hex(), + "orderHash": e.OrderHash.Hex(), + "makerAssetData": makerAssetData, + "takerAssetData": takerAssetData, + }) +} + // ExchangeCancelUpToEvent represents a 0x Exchange CancelUpTo event type ExchangeCancelUpToEvent struct { MakerAddress common.Address @@ -100,18 +191,43 @@ type ExchangeCancelUpToEvent struct { OrderEpoch *big.Int } +// MarshalJSON implements a custom JSON marshaller for the ExchangeCancelUpToEvent type +func (e ExchangeCancelUpToEvent) MarshalJSON() ([]byte, error) { + return json.Marshal(map[string]interface{}{ + "makerAddress": e.MakerAddress.Hex(), + "senderAddress": e.SenderAddress.Hex(), + "orderEpoch": e.OrderEpoch.String(), + }) +} + // WethWithdrawalEvent represents a wrapped Ether Withdraw event type WethWithdrawalEvent struct { Owner common.Address Value *big.Int } +// MarshalJSON implements a custom JSON marshaller for the WethWithdrawalEvent type +func (e WethWithdrawalEvent) MarshalJSON() ([]byte, error) { + return json.Marshal(map[string]interface{}{ + "owner": e.Owner.Hex(), + "value": e.Value.String(), + }) +} + // WethDepositEvent represents a wrapped Ether Deposit event type WethDepositEvent struct { Owner common.Address Value *big.Int } +// MarshalJSON implements a custom JSON marshaller for the WethDepositEvent type +func (e WethDepositEvent) MarshalJSON() ([]byte, error) { + return json.Marshal(map[string]interface{}{ + "owner": e.Owner.Hex(), + "value": e.Value.String(), + }) +} + // UnsupportedEventError is thrown when an unsupported topic is encountered type UnsupportedEventError struct { Topics []common.Hash @@ -151,8 +267,8 @@ type Decoder struct { exchangeTopicToEventName map[common.Hash]string } -// NewDecoder instantiates a new 0x order-relevant events decoder -func NewDecoder() (*Decoder, error) { +// New instantiates a new 0x order-relevant events decoder +func New() (*Decoder, error) { erc20ABI, err := abi.JSON(strings.NewReader(erc20EventsAbi)) if err != nil { return nil, err diff --git a/zeroex/orderwatch/event_decoder_test.go b/zeroex/orderwatch/decoder/event_decoder_test.go similarity index 98% rename from zeroex/orderwatch/event_decoder_test.go rename to zeroex/orderwatch/decoder/event_decoder_test.go index 75a1797e4..48ef5a141 100644 --- a/zeroex/orderwatch/event_decoder_test.go +++ b/zeroex/orderwatch/decoder/event_decoder_test.go @@ -1,4 +1,4 @@ -package orderwatch +package decoder import ( "encoding/json" @@ -36,7 +36,7 @@ func TestDecodeERC20Transfer(t *testing.T) { if err != nil { t.Fatal(err.Error()) } - decoder, err := NewDecoder() + decoder, err := New() if err != nil { t.Fatal(err.Error()) } @@ -62,7 +62,7 @@ func TestDecodeERC20Approval(t *testing.T) { if err != nil { t.Fatal(err.Error()) } - decoder, err := NewDecoder() + decoder, err := New() if err != nil { t.Fatal(err.Error()) } @@ -89,7 +89,7 @@ func TestDecodeERC721Transfer(t *testing.T) { if err != nil { t.Fatal(err.Error()) } - decoder, err := NewDecoder() + decoder, err := New() if err != nil { t.Fatal(err.Error()) } @@ -115,7 +115,7 @@ func TestDecodeERC721Approval(t *testing.T) { if err != nil { t.Fatal(err.Error()) } - decoder, err := NewDecoder() + decoder, err := New() if err != nil { t.Fatal(err.Error()) } @@ -141,7 +141,7 @@ func TestDecodeERC721ApprovalForAll(t *testing.T) { if err != nil { t.Fatal(err.Error()) } - decoder, err := NewDecoder() + decoder, err := New() if err != nil { t.Fatal(err.Error()) } @@ -167,7 +167,7 @@ func TestDecodeExchangeFill(t *testing.T) { if err != nil { t.Fatal(err.Error()) } - decoder, err := NewDecoder() + decoder, err := New() if err != nil { t.Fatal(err.Error()) } @@ -200,7 +200,7 @@ func TestDecodeExchangeCancel(t *testing.T) { if err != nil { t.Fatal(err.Error()) } - decoder, err := NewDecoder() + decoder, err := New() if err != nil { t.Fatal(err.Error()) } @@ -227,7 +227,7 @@ func TestDecodeExchangeCancelUpTo(t *testing.T) { if err != nil { t.Fatal(err.Error()) } - decoder, err := NewDecoder() + decoder, err := New() if err != nil { t.Fatal(err.Error()) } @@ -251,7 +251,7 @@ func TestDecodeWethDeposit(t *testing.T) { if err != nil { t.Fatal(err.Error()) } - decoder, err := NewDecoder() + decoder, err := New() if err != nil { t.Fatal(err.Error()) } @@ -274,7 +274,7 @@ func TestDecodeWethWithdrawal(t *testing.T) { if err != nil { t.Fatal(err.Error()) } - decoder, err := NewDecoder() + decoder, err := New() if err != nil { t.Fatal(err.Error()) } diff --git a/zeroex/orderwatch/order_watcher.go b/zeroex/orderwatch/order_watcher.go index 53599e010..8c939398c 100644 --- a/zeroex/orderwatch/order_watcher.go +++ b/zeroex/orderwatch/order_watcher.go @@ -15,8 +15,10 @@ import ( "github.com/0xProject/0x-mesh/meshdb" "github.com/0xProject/0x-mesh/zeroex" "github.com/0xProject/0x-mesh/zeroex/ordervalidator" + "github.com/0xProject/0x-mesh/zeroex/orderwatch/decoder" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/event" + "github.com/ethereum/go-ethereum/rpc" logger "github.com/sirupsen/logrus" ) @@ -42,7 +44,7 @@ var expirationPollingInterval = 50 * time.Millisecond type Watcher struct { meshDB *meshdb.MeshDB blockWatcher *blockwatch.Watcher - eventDecoder *Decoder + eventDecoder *decoder.Decoder assetDataDecoder *zeroex.AssetDataDecoder blockSubscription event.Subscription contractAddresses ethereum.ContractAddresses @@ -58,7 +60,7 @@ type Watcher struct { // New instantiates a new order watcher func New(meshDB *meshdb.MeshDB, blockWatcher *blockwatch.Watcher, orderValidator *ordervalidator.OrderValidator, networkID int, expirationBuffer time.Duration) (*Watcher, error) { - decoder, err := NewDecoder() + decoder, err := decoder.New() if err != nil { return nil, err } @@ -239,18 +241,22 @@ func (w *Watcher) handleBlockEvents(events []*blockwatch.Event) error { defer func() { _ = ordersColTxn.Discard() }() - hashToOrderWithTxHashes := map[common.Hash]*OrderWithTxHashes{} + orderHashToDBOrder := map[common.Hash]*meshdb.Order{} + orderHashToEvents := map[common.Hash][]*zeroex.ContractEvent{} + var latestBlockNumber rpc.BlockNumber for _, event := range events { + latestBlockNumber = rpc.BlockNumber(event.BlockHeader.Number.Int64()) for _, log := range event.BlockHeader.Logs { + var parameters interface{} eventType, err := w.eventDecoder.FindEventType(log) if err != nil { switch err.(type) { - case UntrackedTokenError: + case decoder.UntrackedTokenError: continue - case UnsupportedEventError: + case decoder.UnsupportedEventError: logger.WithFields(logger.Fields{ - "topics": err.(UnsupportedEventError).Topics, - "contractAddress": err.(UnsupportedEventError).ContractAddress, + "topics": err.(decoder.UnsupportedEventError).Topics, + "contractAddress": err.(decoder.UnsupportedEventError).ContractAddress, }).Info("unsupported event found while trying to find its event type") continue default: @@ -263,7 +269,7 @@ func (w *Watcher) handleBlockEvents(events []*blockwatch.Event) error { var orders []*meshdb.Order switch eventType { case "ERC20TransferEvent": - var transferEvent ERC20TransferEvent + var transferEvent decoder.ERC20TransferEvent err = w.eventDecoder.Decode(log, &transferEvent) if err != nil { if isNonCritical := w.checkDecodeErr(err, eventType); isNonCritical { @@ -271,6 +277,7 @@ func (w *Watcher) handleBlockEvents(events []*blockwatch.Event) error { } return err } + parameters = transferEvent fromOrders, err := w.findOrders(transferEvent.From, log.Address, nil) if err != nil { return err @@ -283,7 +290,7 @@ func (w *Watcher) handleBlockEvents(events []*blockwatch.Event) error { orders = append(orders, toOrders...) case "ERC20ApprovalEvent": - var approvalEvent ERC20ApprovalEvent + var approvalEvent decoder.ERC20ApprovalEvent err = w.eventDecoder.Decode(log, &approvalEvent) if err != nil { if isNonCritical := w.checkDecodeErr(err, eventType); isNonCritical { @@ -295,13 +302,14 @@ func (w *Watcher) handleBlockEvents(events []*blockwatch.Event) error { if approvalEvent.Spender != w.contractAddresses.ERC20Proxy { continue } + parameters = approvalEvent orders, err = w.findOrders(approvalEvent.Owner, log.Address, nil) if err != nil { return err } case "ERC721TransferEvent": - var transferEvent ERC721TransferEvent + var transferEvent decoder.ERC721TransferEvent err = w.eventDecoder.Decode(log, &transferEvent) if err != nil { if isNonCritical := w.checkDecodeErr(err, eventType); isNonCritical { @@ -309,6 +317,7 @@ func (w *Watcher) handleBlockEvents(events []*blockwatch.Event) error { } return err } + parameters = transferEvent fromOrders, err := w.findOrders(transferEvent.From, log.Address, transferEvent.TokenId) if err != nil { return err @@ -321,7 +330,7 @@ func (w *Watcher) handleBlockEvents(events []*blockwatch.Event) error { orders = append(orders, toOrders...) case "ERC721ApprovalEvent": - var approvalEvent ERC721ApprovalEvent + var approvalEvent decoder.ERC721ApprovalEvent err = w.eventDecoder.Decode(log, &approvalEvent) if err != nil { if isNonCritical := w.checkDecodeErr(err, eventType); isNonCritical { @@ -333,13 +342,14 @@ func (w *Watcher) handleBlockEvents(events []*blockwatch.Event) error { if approvalEvent.Approved != w.contractAddresses.ERC721Proxy { continue } + parameters = approvalEvent orders, err = w.findOrders(approvalEvent.Owner, log.Address, approvalEvent.TokenId) if err != nil { return err } case "ERC721ApprovalForAllEvent": - var approvalForAllEvent ERC721ApprovalForAllEvent + var approvalForAllEvent decoder.ERC721ApprovalForAllEvent err = w.eventDecoder.Decode(log, &approvalForAllEvent) if err != nil { if isNonCritical := w.checkDecodeErr(err, eventType); isNonCritical { @@ -351,13 +361,14 @@ func (w *Watcher) handleBlockEvents(events []*blockwatch.Event) error { if approvalForAllEvent.Operator != w.contractAddresses.ERC721Proxy { continue } + parameters = approvalForAllEvent orders, err = w.findOrders(approvalForAllEvent.Owner, log.Address, nil) if err != nil { return err } case "WethWithdrawalEvent": - var withdrawalEvent WethWithdrawalEvent + var withdrawalEvent decoder.WethWithdrawalEvent err = w.eventDecoder.Decode(log, &withdrawalEvent) if err != nil { if isNonCritical := w.checkDecodeErr(err, eventType); isNonCritical { @@ -365,13 +376,14 @@ func (w *Watcher) handleBlockEvents(events []*blockwatch.Event) error { } return err } + parameters = withdrawalEvent orders, err = w.findOrders(withdrawalEvent.Owner, log.Address, nil) if err != nil { return err } case "WethDepositEvent": - var depositEvent WethDepositEvent + var depositEvent decoder.WethDepositEvent err = w.eventDecoder.Decode(log, &depositEvent) if err != nil { if isNonCritical := w.checkDecodeErr(err, eventType); isNonCritical { @@ -379,13 +391,14 @@ func (w *Watcher) handleBlockEvents(events []*blockwatch.Event) error { } return err } + parameters = depositEvent orders, err = w.findOrders(depositEvent.Owner, log.Address, nil) if err != nil { return err } case "ExchangeFillEvent": - var exchangeFillEvent ExchangeFillEvent + var exchangeFillEvent decoder.ExchangeFillEvent err = w.eventDecoder.Decode(log, &exchangeFillEvent) if err != nil { if isNonCritical := w.checkDecodeErr(err, eventType); isNonCritical { @@ -393,13 +406,14 @@ func (w *Watcher) handleBlockEvents(events []*blockwatch.Event) error { } return err } + parameters = exchangeFillEvent order := w.findOrder(exchangeFillEvent.OrderHash) if order != nil { orders = append(orders, order) } case "ExchangeCancelEvent": - var exchangeCancelEvent ExchangeCancelEvent + var exchangeCancelEvent decoder.ExchangeCancelEvent err = w.eventDecoder.Decode(log, &exchangeCancelEvent) if err != nil { if isNonCritical := w.checkDecodeErr(err, eventType); isNonCritical { @@ -407,6 +421,7 @@ func (w *Watcher) handleBlockEvents(events []*blockwatch.Event) error { } return err } + parameters = exchangeCancelEvent orders = []*meshdb.Order{} order := w.findOrder(exchangeCancelEvent.OrderHash) if order != nil { @@ -414,7 +429,7 @@ func (w *Watcher) handleBlockEvents(events []*blockwatch.Event) error { } case "ExchangeCancelUpToEvent": - var exchangeCancelUpToEvent ExchangeCancelUpToEvent + var exchangeCancelUpToEvent decoder.ExchangeCancelUpToEvent err = w.eventDecoder.Decode(log, &exchangeCancelUpToEvent) if err != nil { if isNonCritical := w.checkDecodeErr(err, eventType); isNonCritical { @@ -422,6 +437,7 @@ func (w *Watcher) handleBlockEvents(events []*blockwatch.Event) error { } return err } + parameters = exchangeCancelUpToEvent orders, err = w.meshDB.FindOrdersByMakerAddressAndMaxSalt(exchangeCancelUpToEvent.MakerAddress, exchangeCancelUpToEvent.OrderEpoch) if err != nil { logger.WithFields(logger.Fields{ @@ -436,22 +452,27 @@ func (w *Watcher) handleBlockEvents(events []*blockwatch.Event) error { }).Error("unknown eventType encountered") return err } + contractEvent := &zeroex.ContractEvent{ + BlockHash: log.BlockHash, + TxHash: log.TxHash, + TxIndex: log.TxIndex, + LogIndex: log.Index, + IsRemoved: log.Removed, + Address: log.Address, + Kind: eventType, + Parameters: parameters, + } for _, order := range orders { - orderWithTxHashes, ok := hashToOrderWithTxHashes[order.Hash] - if !ok { - hashToOrderWithTxHashes[order.Hash] = &OrderWithTxHashes{ - Order: order, - TxHashes: map[common.Hash]interface{}{ - log.TxHash: struct{}{}, - }, - } + orderHashToDBOrder[order.Hash] = order + if _, ok := orderHashToEvents[order.Hash]; !ok { + orderHashToEvents[order.Hash] = []*zeroex.ContractEvent{contractEvent} } else { - orderWithTxHashes.TxHashes[log.TxHash] = struct{}{} + orderHashToEvents[order.Hash] = append(orderHashToEvents[order.Hash], contractEvent) } } } } - return w.generateOrderEventsIfChanged(ordersColTxn, hashToOrderWithTxHashes) + return w.generateOrderEventsIfChanged(ordersColTxn, orderHashToDBOrder, orderHashToEvents, latestBlockNumber) } func (w *Watcher) cleanup(ctx context.Context) error { @@ -468,19 +489,18 @@ func (w *Watcher) cleanup(ctx context.Context) error { }).Error("Failed to find orders by LastUpdatedBefore") return err } - hashToOrderWithTxHashes := map[common.Hash]*OrderWithTxHashes{} + orderHashToDBOrder := map[common.Hash]*meshdb.Order{} + orderHashToEvents := map[common.Hash][]*zeroex.ContractEvent{} // No events when running cleanup job for _, order := range orders { select { case <-ctx.Done(): return nil default: } - hashToOrderWithTxHashes[order.Hash] = &OrderWithTxHashes{ - Order: order, - TxHashes: map[common.Hash]interface{}{}, - } + orderHashToDBOrder[order.Hash] = order + orderHashToEvents[order.Hash] = []*zeroex.ContractEvent{} } - return w.generateOrderEventsIfChanged(ordersColTxn, hashToOrderWithTxHashes) + return w.generateOrderEventsIfChanged(ordersColTxn, orderHashToDBOrder, orderHashToEvents, rpc.LatestBlockNumber) } // Add adds a 0x order to the DB and watches it for changes in fillability. It @@ -546,11 +566,6 @@ func (w *Watcher) Subscribe(sink chan<- []*zeroex.OrderEvent) event.Subscription return w.orderScope.Track(w.orderFeed.Subscribe(sink)) } -type OrderWithTxHashes struct { - Order *meshdb.Order - TxHashes map[common.Hash]interface{} -} - func (w *Watcher) findOrder(orderHash common.Hash) *meshdb.Order { order := meshdb.Order{} err := w.meshDB.Orders.FindByID(orderHash.Bytes(), &order) @@ -579,10 +594,9 @@ func (w *Watcher) findOrders(makerAddress, tokenAddress common.Address, tokenID return orders, nil } -func (w *Watcher) generateOrderEventsIfChanged(ordersColTxn *db.Transaction, hashToOrderWithTxHashes map[common.Hash]*OrderWithTxHashes) error { +func (w *Watcher) generateOrderEventsIfChanged(ordersColTxn *db.Transaction, orderHashToDBOrder map[common.Hash]*meshdb.Order, orderHashToEvents map[common.Hash][]*zeroex.ContractEvent, validationBlockNumber rpc.BlockNumber) error { signedOrders := []*zeroex.SignedOrder{} - for _, orderWithTxHashes := range hashToOrderWithTxHashes { - order := orderWithTxHashes.Order + for _, order := range orderHashToDBOrder { if order.IsRemoved && time.Since(order.LastUpdated) > permanentlyDeleteAfter { if err := w.permanentlyDeleteOrder(ordersColTxn, order); err != nil { return err @@ -595,18 +609,11 @@ func (w *Watcher) generateOrderEventsIfChanged(ordersColTxn *db.Transaction, has return nil } areNewOrders := false - validationResults := w.orderValidator.BatchValidate(signedOrders, areNewOrders) + validationResults := w.orderValidator.BatchValidate(signedOrders, areNewOrders, validationBlockNumber) orderEvents := []*zeroex.OrderEvent{} for _, acceptedOrderInfo := range validationResults.Accepted { - orderWithTxHashes := hashToOrderWithTxHashes[acceptedOrderInfo.OrderHash] - txHashes := make([]common.Hash, len(orderWithTxHashes.TxHashes)) - txHashIndex := 0 - for txHash := range orderWithTxHashes.TxHashes { - txHashes[txHashIndex] = txHash - txHashIndex++ - } - order := orderWithTxHashes.Order + order := orderHashToDBOrder[acceptedOrderInfo.OrderHash] oldFillableAmount := order.FillableTakerAssetAmount newFillableAmount := acceptedOrderInfo.FillableTakerAssetAmount oldAmountIsMoreThenNewAmount := oldFillableAmount.Cmp(newFillableAmount) == 1 @@ -623,7 +630,7 @@ func (w *Watcher) generateOrderEventsIfChanged(ordersColTxn *db.Transaction, has SignedOrder: order.SignedOrder, FillableTakerAssetAmount: acceptedOrderInfo.FillableTakerAssetAmount, Kind: zeroex.EKOrderAdded, - TxHashes: txHashes, + ContractEvents: orderHashToEvents[order.Hash], } orderEvents = append(orderEvents, orderEvent) } else if oldFillableAmount.Cmp(newFillableAmount) == 0 { @@ -637,7 +644,7 @@ func (w *Watcher) generateOrderEventsIfChanged(ordersColTxn *db.Transaction, has SignedOrder: order.SignedOrder, Kind: zeroex.EKOrderFilled, FillableTakerAssetAmount: acceptedOrderInfo.FillableTakerAssetAmount, - TxHashes: txHashes, + ContractEvents: orderHashToEvents[order.Hash], } orderEvents = append(orderEvents, orderEvent) } else if oldFillableAmount.Cmp(big.NewInt(0)) == 1 && !oldAmountIsMoreThenNewAmount { @@ -649,7 +656,7 @@ func (w *Watcher) generateOrderEventsIfChanged(ordersColTxn *db.Transaction, has SignedOrder: order.SignedOrder, Kind: zeroex.EKOrderFillabilityIncreased, FillableTakerAssetAmount: acceptedOrderInfo.FillableTakerAssetAmount, - TxHashes: txHashes, + ContractEvents: orderHashToEvents[order.Hash], } orderEvents = append(orderEvents, orderEvent) } @@ -659,8 +666,7 @@ func (w *Watcher) generateOrderEventsIfChanged(ordersColTxn *db.Transaction, has case ordervalidator.MeshError: // TODO(fabio): Do we want to handle MeshErrors somehow here? case ordervalidator.ZeroExValidation: - orderWithTxHashes := hashToOrderWithTxHashes[rejectedOrderInfo.OrderHash] - order := orderWithTxHashes.Order + order := orderHashToDBOrder[rejectedOrderInfo.OrderHash] oldFillableAmount := order.FillableTakerAssetAmount if oldFillableAmount.Cmp(big.NewInt(0)) == 0 { // If the oldFillableAmount was already 0, this order is already flagged for removal. @@ -675,18 +681,12 @@ func (w *Watcher) generateOrderEventsIfChanged(ordersColTxn *db.Transaction, has logger.WithError(err).WithField("rejectedOrderStatus", rejectedOrderInfo.Status).Error("no OrderEventKind corresponding to RejectedOrderStatus") return err } - txHashes := make([]common.Hash, len(orderWithTxHashes.TxHashes)) - txHashIndex := 0 - for txHash := range orderWithTxHashes.TxHashes { - txHashes[txHashIndex] = txHash - txHashIndex++ - } orderEvent := &zeroex.OrderEvent{ OrderHash: rejectedOrderInfo.OrderHash, SignedOrder: rejectedOrderInfo.SignedOrder, FillableTakerAssetAmount: big.NewInt(0), Kind: kind, - TxHashes: txHashes, + ContractEvents: orderHashToEvents[order.Hash], } orderEvents = append(orderEvents, orderEvent) } @@ -787,11 +787,11 @@ func (w *Watcher) permanentlyDeleteOrder(ordersColTxn *db.Transaction, order *me // Logs the error and returns true if the error is non-critical. func (w *Watcher) checkDecodeErr(err error, eventType string) bool { - if _, ok := err.(UnsupportedEventError); ok { + if _, ok := err.(decoder.UnsupportedEventError); ok { logger.WithFields(logger.Fields{ "eventType": eventType, - "topics": err.(UnsupportedEventError).Topics, - "contractAddress": err.(UnsupportedEventError).ContractAddress, + "topics": err.(decoder.UnsupportedEventError).Topics, + "contractAddress": err.(decoder.UnsupportedEventError).ContractAddress, }).Warn("unsupported event found") return true } diff --git a/zeroex/orderwatch/topics.go b/zeroex/orderwatch/topics.go index d9f940f4b..2f86efc31 100644 --- a/zeroex/orderwatch/topics.go +++ b/zeroex/orderwatch/topics.go @@ -1,6 +1,7 @@ package orderwatch import ( + "github.com/0xProject/0x-mesh/zeroex/orderwatch/decoder" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/crypto" ) @@ -9,7 +10,7 @@ import ( // the logs retrieved for Ethereum blocks func GetRelevantTopics() []common.Hash { topics := []common.Hash{} - for _, signature := range EVENT_SIGNATURES { + for _, signature := range decoder.EVENT_SIGNATURES { topic := common.BytesToHash(crypto.Keccak256([]byte(signature))) topics = append(topics, topic) } From 610e32b7817138bb6eae48d33df902457957d6d2 Mon Sep 17 00:00:00 2001 From: fabioberger Date: Tue, 17 Sep 2019 12:43:53 +0200 Subject: [PATCH 04/17] Update DB Sync guide --- docs/db_syncing.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/db_syncing.md b/docs/db_syncing.md index 8c0278b02..9e1c0172d 100644 --- a/docs/db_syncing.md +++ b/docs/db_syncing.md @@ -25,6 +25,8 @@ Subscribe to the Mesh node's `orders` subscription over a WS connection. This ca **Note:** Updates refer to updating the order's `fillableTakerAssetAmount` in the DB. +**Note 2:** If we receive any event other than `ADDED` and `FILLABILITY_INCREASED` for an order we do not find in our database, we ignore the event and noop. + #### 2. Get all orders currently stored in Mesh There might have been orders stored in Mesh that the DB doesn't know about at this time. Because of this, we must fetch all currently stored orders in the Mesh node and upsert them in the database. This can be done using the [mesh_getOrders](rpc_api.md#mesh_getorders) JSON-RPC method. This method creates a snapshot of the Mesh node's internal DB of orders when first called, and allows for subsequent paginated requests against this snapshot. Because we are already subscribed to order events, any new orders added/removed after the snapshot is made will be discovered via that subscription. From 091caf53289a954ce6070e4dbd28729851a93206 Mon Sep 17 00:00:00 2001 From: fabioberger Date: Tue, 17 Sep 2019 21:26:20 +0200 Subject: [PATCH 05/17] Fix rebase issue --- zeroex/order_js.go | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/zeroex/order_js.go b/zeroex/order_js.go index a2e32dfa3..5562e2add 100644 --- a/zeroex/order_js.go +++ b/zeroex/order_js.go @@ -7,7 +7,7 @@ import ( "strings" "syscall/js" - "github.com/0xProject/0x-mesh/ethereum" + "github.com/0xProject/0x-mesh/zeroex/orderwatch/decoder" "github.com/ethereum/go-ethereum/common" ) @@ -68,34 +68,34 @@ func (c ContractEvent) JSValue() js.Value { } switch c.Kind { case "ERC20TransferEvent": - m["parameters"] = c.Parameters.(ethereum.ERC20TransferEvent) + m["parameters"] = c.Parameters.(decoder.ERC20TransferEvent) case "ERC20ApprovalEvent": - m["parameters"] = c.Parameters.(ethereum.ERC20ApprovalEvent) + m["parameters"] = c.Parameters.(decoder.ERC20ApprovalEvent) case "ERC721TransferEvent": - m["parameters"] = c.Parameters.(ethereum.ERC721TransferEvent) + m["parameters"] = c.Parameters.(decoder.ERC721TransferEvent) case "ERC721ApprovalEvent": - m["parameters"] = c.Parameters.(ethereum.ERC721ApprovalEvent) + m["parameters"] = c.Parameters.(decoder.ERC721ApprovalEvent) case "ERC721ApprovalForAllEvent": - m["parameters"] = c.Parameters.(ethereum.ERC721ApprovalForAllEvent) + m["parameters"] = c.Parameters.(decoder.ERC721ApprovalForAllEvent) case "WethWithdrawalEvent": - m["parameters"] = c.Parameters.(ethereum.WethWithdrawalEvent) + m["parameters"] = c.Parameters.(decoder.WethWithdrawalEvent) case "WethDepositEvent": - m["parameters"] = c.Parameters.(ethereum.WethDepositEvent) + m["parameters"] = c.Parameters.(decoder.WethDepositEvent) case "ExchangeFillEvent": - m["parameters"] = c.Parameters.(ethereum.ExchangeFillEvent) + m["parameters"] = c.Parameters.(decoder.ExchangeFillEvent) case "ExchangeCancelEvent": - m["parameters"] = c.Parameters.(ethereum.ExchangeCancelEvent) + m["parameters"] = c.Parameters.(decoder.ExchangeCancelEvent) case "ExchangeCancelUpToEvent": - m["parameters"] = c.Parameters.(ethereum.ExchangeCancelUpToEvent) + m["parameters"] = c.Parameters.(decoder.ExchangeCancelUpToEvent) default: panic(fmt.Sprintf("Unrecognized event encountered: %s", c.Kind)) From 5f29e2df2305719b4fa89b96f5a701e946f26c2d Mon Sep 17 00:00:00 2001 From: fabioberger Date: Wed, 18 Sep 2019 10:59:33 +0200 Subject: [PATCH 06/17] Add JSValue methods for contract event structs --- zeroex/order_js.go | 28 ++--- zeroex/orderwatch/decoder/event_decoder.go | 12 +- zeroex/orderwatch/decoder/event_decoder_js.go | 116 ++++++++++++++++++ 3 files changed, 136 insertions(+), 20 deletions(-) create mode 100644 zeroex/orderwatch/decoder/event_decoder_js.go diff --git a/zeroex/order_js.go b/zeroex/order_js.go index 5562e2add..2a284c461 100644 --- a/zeroex/order_js.go +++ b/zeroex/order_js.go @@ -12,16 +12,16 @@ import ( ) func (o OrderEvent) JSValue() js.Value { - contractEventsJSValues := []js.Value{} - for _, contractEvent := range o.ContractEvents { - contractEventsJSValues = append(contractEventsJSValues, contractEvent.JSValue()) + contractEventsJS := make([]interface{}, len(o.ContractEvents)) + for i, contractEvent := range o.ContractEvents { + contractEventsJS[i] = contractEvent.JSValue() } return js.ValueOf(map[string]interface{}{ "orderHash": o.OrderHash.Hex(), "signedOrder": o.SignedOrder.JSValue(), "kind": string(o.Kind), "fillableTakerAssetAmount": o.FillableTakerAssetAmount.String(), - "contractEvents": contractEventsJSValues, + "contractEvents": contractEventsJS, }) } @@ -68,34 +68,34 @@ func (c ContractEvent) JSValue() js.Value { } switch c.Kind { case "ERC20TransferEvent": - m["parameters"] = c.Parameters.(decoder.ERC20TransferEvent) + m["parameters"] = c.Parameters.(decoder.ERC20TransferEvent).JSValue() case "ERC20ApprovalEvent": - m["parameters"] = c.Parameters.(decoder.ERC20ApprovalEvent) + m["parameters"] = c.Parameters.(decoder.ERC20ApprovalEvent).JSValue() case "ERC721TransferEvent": - m["parameters"] = c.Parameters.(decoder.ERC721TransferEvent) + m["parameters"] = c.Parameters.(decoder.ERC721TransferEvent).JSValue() case "ERC721ApprovalEvent": - m["parameters"] = c.Parameters.(decoder.ERC721ApprovalEvent) + m["parameters"] = c.Parameters.(decoder.ERC721ApprovalEvent).JSValue() case "ERC721ApprovalForAllEvent": - m["parameters"] = c.Parameters.(decoder.ERC721ApprovalForAllEvent) + m["parameters"] = c.Parameters.(decoder.ERC721ApprovalForAllEvent).JSValue() case "WethWithdrawalEvent": - m["parameters"] = c.Parameters.(decoder.WethWithdrawalEvent) + m["parameters"] = c.Parameters.(decoder.WethWithdrawalEvent).JSValue() case "WethDepositEvent": - m["parameters"] = c.Parameters.(decoder.WethDepositEvent) + m["parameters"] = c.Parameters.(decoder.WethDepositEvent).JSValue() case "ExchangeFillEvent": - m["parameters"] = c.Parameters.(decoder.ExchangeFillEvent) + m["parameters"] = c.Parameters.(decoder.ExchangeFillEvent).JSValue() case "ExchangeCancelEvent": - m["parameters"] = c.Parameters.(decoder.ExchangeCancelEvent) + m["parameters"] = c.Parameters.(decoder.ExchangeCancelEvent).JSValue() case "ExchangeCancelUpToEvent": - m["parameters"] = c.Parameters.(decoder.ExchangeCancelUpToEvent) + m["parameters"] = c.Parameters.(decoder.ExchangeCancelUpToEvent).JSValue() default: panic(fmt.Sprintf("Unrecognized event encountered: %s", c.Kind)) diff --git a/zeroex/orderwatch/decoder/event_decoder.go b/zeroex/orderwatch/decoder/event_decoder.go index 0fc1e7168..ec5fe18a9 100644 --- a/zeroex/orderwatch/decoder/event_decoder.go +++ b/zeroex/orderwatch/decoder/event_decoder.go @@ -207,10 +207,10 @@ type WethWithdrawalEvent struct { } // MarshalJSON implements a custom JSON marshaller for the WethWithdrawalEvent type -func (e WethWithdrawalEvent) MarshalJSON() ([]byte, error) { +func (w WethWithdrawalEvent) MarshalJSON() ([]byte, error) { return json.Marshal(map[string]interface{}{ - "owner": e.Owner.Hex(), - "value": e.Value.String(), + "owner": w.Owner.Hex(), + "value": w.Value.String(), }) } @@ -221,10 +221,10 @@ type WethDepositEvent struct { } // MarshalJSON implements a custom JSON marshaller for the WethDepositEvent type -func (e WethDepositEvent) MarshalJSON() ([]byte, error) { +func (w WethDepositEvent) MarshalJSON() ([]byte, error) { return json.Marshal(map[string]interface{}{ - "owner": e.Owner.Hex(), - "value": e.Value.String(), + "owner": w.Owner.Hex(), + "value": w.Value.String(), }) } diff --git a/zeroex/orderwatch/decoder/event_decoder_js.go b/zeroex/orderwatch/decoder/event_decoder_js.go new file mode 100644 index 000000000..ed3f343d3 --- /dev/null +++ b/zeroex/orderwatch/decoder/event_decoder_js.go @@ -0,0 +1,116 @@ +// +build js,wasm + +package decoder + +import ( + "fmt" + "syscall/js" + + "github.com/ethereum/go-ethereum/common" +) + +func (e ERC20TransferEvent) JSValue() js.Value { + return js.ValueOf(map[string]interface{}{ + "from": e.From.Hex(), + "to": e.To.Hex(), + "value": e.Value.String(), + }) +} + +func (e ERC20ApprovalEvent) JSValue() js.Value { + return js.ValueOf(map[string]interface{}{ + "owner": e.Owner.Hex(), + "spender": e.Spender.Hex(), + "value": e.Value.String(), + }) +} + +func (e ERC721TransferEvent) JSValue() js.Value { + return js.ValueOf(map[string]interface{}{ + "from": e.From.Hex(), + "to": e.To.Hex(), + "tokenId": e.TokenId.String(), + }) +} + +func (e ERC721ApprovalEvent) JSValue() js.Value { + return js.ValueOf(map[string]interface{}{ + "owner": e.Owner.Hex(), + "approved": e.Approved.Hex(), + "tokenId": e.TokenId.String(), + }) +} + +func (e ERC721ApprovalForAllEvent) JSValue() js.Value { + return js.ValueOf(map[string]interface{}{ + "owner": e.Owner.Hex(), + "operator": e.Operator.Hex(), + "approved": e.Approved, + }) +} + +func (e ExchangeFillEvent) JSValue() js.Value { + makerAssetData := "" + if len(e.MakerAssetData) != 0 { + makerAssetData = fmt.Sprintf("0x%s", common.Bytes2Hex(e.MakerAssetData)) + } + takerAssetData := "" + if len(e.TakerAssetData) != 0 { + takerAssetData = fmt.Sprintf("0x%s", common.Bytes2Hex(e.TakerAssetData)) + } + return js.ValueOf(map[string]interface{}{ + "makerAddress": e.MakerAddress.Hex(), + "takerAddress": e.TakerAddress.Hex(), + "senderAddress": e.SenderAddress.Hex(), + "feeRecipientAddress": e.FeeRecipientAddress.Hex(), + "makerAssetFilledAmount": e.MakerAssetFilledAmount.String(), + "takerAssetFilledAmount": e.TakerAssetFilledAmount.String(), + "makerFeePaid": e.MakerFeePaid.String(), + "takerFeePaid": e.TakerFeePaid.String(), + "orderHash": e.OrderHash.Hex(), + "makerAssetData": makerAssetData, + "takerAssetData": takerAssetData, + }) +} + +func (e ExchangeCancelEvent) JSValue() js.Value { + makerAssetData := "" + if len(e.MakerAssetData) != 0 { + makerAssetData = fmt.Sprintf("0x%s", common.Bytes2Hex(e.MakerAssetData)) + } + takerAssetData := "" + if len(e.TakerAssetData) != 0 { + takerAssetData = fmt.Sprintf("0x%s", common.Bytes2Hex(e.TakerAssetData)) + } + return js.ValueOf(map[string]interface{}{ + "makerAddress": e.MakerAddress.Hex(), + "senderAddress": e.SenderAddress.Hex(), + "feeRecipientAddress": e.FeeRecipientAddress.Hex(), + "orderHash": e.OrderHash.Hex(), + "makerAssetData": makerAssetData, + "takerAssetData": takerAssetData, + }) +} + +func (e ExchangeCancelUpToEvent) JSValue() js.Value { + return js.ValueOf(map[string]interface{}{ + "makerAddress": e.MakerAddress.Hex(), + "senderAddress": e.SenderAddress.Hex(), + "orderEpoch": e.OrderEpoch.String(), + }) +} + +func (w WethWithdrawalEvent) JSValue() js.Value { + return js.ValueOf(map[string]interface{}{ + "owner": w.Owner.Hex(), + "value": w.Value.String(), + }) +} + +func (w WethDepositEvent) JSValue() js.Value { + return js.ValueOf(map[string]interface{}{ + "owner": w.Owner.Hex(), + "value": w.Value.String(), + }) +} + From df266278e44baf67d70df7af04c4e00dc5f550e3 Mon Sep 17 00:00:00 2001 From: fabioberger Date: Wed, 18 Sep 2019 11:11:47 +0200 Subject: [PATCH 07/17] Remove unused import --- rpc/clients/typescript/src/types.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/rpc/clients/typescript/src/types.ts b/rpc/clients/typescript/src/types.ts index cd4bd7bdc..bcaf368a5 100644 --- a/rpc/clients/typescript/src/types.ts +++ b/rpc/clients/typescript/src/types.ts @@ -1,6 +1,5 @@ import { SignedOrder } from '@0x/types'; import { BigNumber } from '@0x/utils'; -import { string } from 'prop-types'; /** * WebSocketClient configs From 05aa3d2ef95a2ee9a52aa51cad77f53513da8f74 Mon Sep 17 00:00:00 2001 From: fabioberger Date: Wed, 18 Sep 2019 12:47:41 +0200 Subject: [PATCH 08/17] Add CHANGELOG entry --- CHANGELOG.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0638894e9..ec7372c67 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,11 @@ This changelog is a work in progress and may contain notes for versions which have not actually been released. Check the [Releases](https://github.com/0xProject/0x-mesh/releases) page to see full release notes and more information about the latest released versions. -## v4.1.0-beta +## v5.0.0-beta + +### Breaking changes 🛠 + +- Removes the `txHashes` key in the `OrderEvent`s emitted from the `orders` JSON-RPC subscription and replaced it with `contractEvents`, an array of decoded order-relevant contract events. Parsing these events allows callers to find every discrete order fill/cancel event. ### Features ✅ From d5fbc5654c5004d14aa34a176ad6c4c39d12acb6 Mon Sep 17 00:00:00 2001 From: fabioberger Date: Wed, 2 Oct 2019 11:51:22 +0800 Subject: [PATCH 09/17] Rename OrderEvent Kind to EndState to better elucidate that it represents the end state of the order after it's revalidation since the last time it was re-validated --- browser/ts/index.ts | 6 ++-- docs/db_syncing.md | 4 +-- docs/rpc_api.md | 6 ++-- integration-tests/integration_test.go | 6 ++-- rpc/clients/typescript/src/index.ts | 2 +- rpc/clients/typescript/src/types.ts | 6 ++-- rpc/clients/typescript/src/ws_client.ts | 2 +- rpc/clients/typescript/test/ws_client_test.ts | 2 +- zeroex/order.go | 31 ++++++++++--------- zeroex/order_js.go | 2 +- zeroex/order_test.go | 2 +- zeroex/ordervalidator/order_validator.go | 16 +++++----- zeroex/orderwatch/order_watcher.go | 18 +++++------ zeroex/orderwatch/order_watcher_test.go | 22 ++++++------- 14 files changed, 63 insertions(+), 62 deletions(-) diff --git a/browser/ts/index.ts b/browser/ts/index.ts index d7287b813..484b4312e 100644 --- a/browser/ts/index.ts +++ b/browser/ts/index.ts @@ -291,7 +291,7 @@ export interface WrapperContractEvent { parameters: WrapperContractEventParameters; } -export enum OrderEventKind { +export enum OrderEventEndState { Invalid = 'INVALID', Added = 'ADDED', Filled = 'FILLED', @@ -305,7 +305,7 @@ export enum OrderEventKind { export interface WrapperOrderEvent { orderHash: string; signedOrder: WrapperSignedOrder; - kind: OrderEventKind; + endState: OrderEventEndState; fillableTakerAssetAmount: string; contractEvents: WrapperContractEvent[]; } @@ -313,7 +313,7 @@ export interface WrapperOrderEvent { export interface OrderEvent { orderHash: string; signedOrder: SignedOrder; - kind: OrderEventKind; + endState: OrderEventEndState; fillableTakerAssetAmount: BigNumber; contractEvents: ContractEvent[]; } diff --git a/docs/db_syncing.md b/docs/db_syncing.md index 9e1c0172d..3050b9e18 100644 --- a/docs/db_syncing.md +++ b/docs/db_syncing.md @@ -14,9 +14,9 @@ When first connecting the DB and Mesh node, we first need to make sure both have #### 1. Subscribe to Mesh -Subscribe to the Mesh node's `orders` subscription over a WS connection. This can be done using our [golang](https://godoc.org/github.com/0xProject/0x-mesh/rpc) or [Typescript/Javascript](json_rpc_clients/typescript/README.md) clients or any other JSON-RPC WebSocket client. Whenever you receive an order event from this subscription, make the appropriate updates to your DB. Each order event has an associated [OrderEventKind](https://godoc.org/github.com/0xProject/0x-mesh/zeroex#pkg-constants). +Subscribe to the Mesh node's `orders` subscription over a WS connection. This can be done using our [golang](https://godoc.org/github.com/0xProject/0x-mesh/rpc) or [Typescript/Javascript](json_rpc_clients/typescript/README.md) clients or any other JSON-RPC WebSocket client. Whenever you receive an order event from this subscription, make the appropriate updates to your DB. Each order event has an associated [OrderEventEndState](https://godoc.org/github.com/0xProject/0x-mesh/zeroex#pkg-constants). -| Kind | DB operation | +| End state | DB operation | |--------------------------------------------|---------------------------------| | ADDED | Insert | | FILLED | Update | diff --git a/docs/rpc_api.md b/docs/rpc_api.md index f7fad5e47..64589fa82 100644 --- a/docs/rpc_api.md +++ b/docs/rpc_api.md @@ -211,7 +211,7 @@ Gets certain configurations and stats about a Mesh node. ### `mesh_subscribe` to `orders` topic -Allows the caller to subscribe to a stream of `OrderEvents`. An `OrderEvent` contains either newly discovered orders found by Mesh via the P2P network, or updates to the fillability of a previously discovered order (e.g., if an order gets filled, cancelled, expired, etc...). `OrderEvent`s _do not_ correspond 1-to-1 to smart contract events. Rather, an `OrderEvent` about an orders fillability change represents the aggregate change to it's fillability given _all_ the transactions included within the most recently mined block. +Allows the caller to subscribe to a stream of `OrderEvents`. An `OrderEvent` contains either newly discovered orders found by Mesh via the P2P network, or updates to the fillability of a previously discovered order (e.g., if an order gets filled, cancelled, expired, etc...). `OrderEvent`s _do not_ correspond 1-to-1 to smart contract events. Rather, an `OrderEvent` about an orders fillability change represents the aggregate change to it's fillability given _all_ the transactions included within the most recently mined/reverted blocks. **Example:** If an order is both `filled` and `cancelled` within a single block, only a cancellation `OrderEvent` will be emitted (since this is the final state of the order after this block is mined). The cancellation `OrderEvent` _will_ however list the contract events intercepted that could have impacted this orders fillability. This list will include both the fill event and cancellation event. @@ -265,7 +265,7 @@ Mesh has implemented subscriptions in the [same manner as Geth](https://github.c "salt": "1559422141994", "signature": "0x1cf16c2f3a210965b5e17f51b57b869ba4ddda33df92b0017b4d8da9dacd3152b122a73844eaf50ccde29a42950239ba36a525ed7f1698a8a5e1896cf7d651aed203" }, - "kind": "CANCELLED", + "endState": "CANCELLED", "fillableTakerAssetAmount": 0, "contractEvents": [ { @@ -292,7 +292,7 @@ Mesh has implemented subscriptions in the [same manner as Geth](https://github.c } ``` -See the [OrderEvent](https://godoc.org/github.com/0xProject/0x-mesh/zeroex#OrderEvent) type declaration as well as the [EventKind](https://godoc.org/github.com/0xProject/0x-mesh/zeroex#pkg-constants) event types for a complete list of the events that could be emitted. +See the [OrderEvent](https://godoc.org/github.com/0xProject/0x-mesh/zeroex#OrderEvent) type declaration as well as the [OrderEventEndState](https://godoc.org/github.com/0xProject/0x-mesh/zeroex#pkg-constants) types for a complete list of the events that could be emitted. To unsubscribe, send a `mesh_unsubscribe` request specifying the `subscriptionId`. diff --git a/integration-tests/integration_test.go b/integration-tests/integration_test.go index 795b98905..c821f6eda 100644 --- a/integration-tests/integration_test.go +++ b/integration-tests/integration_test.go @@ -196,7 +196,7 @@ func TestBrowserIntegration(t *testing.T) { // Next, wait for the order to be received. expectedOrderEventLog := orderEventLog{ OrderHash: standaloneOrderHash.Hex(), - Kind: "ADDED", + EndState: "ADDED", } _, err = waitForOrderEventLog(ctx, browserLogMessages, expectedOrderEventLog) assert.NoError(t, err, "Browser node did not receive order sent by standalone node") @@ -419,7 +419,7 @@ func waitForReceivedOrderLog(ctx context.Context, logMessages <-chan string, exp // by Mesh. They need to be explicitly logged. type orderEventLog struct { OrderHash string `json:"orderHash"` - Kind string `json:"kind"` + EndState string `json:"endState"` } func waitForOrderEventLog(ctx context.Context, logMessages <-chan string, expectedLog orderEventLog) (string, error) { @@ -429,7 +429,7 @@ func waitForOrderEventLog(ctx context.Context, logMessages <-chan string, expect return false } return foundLog.OrderHash == expectedLog.OrderHash && - foundLog.Kind == expectedLog.Kind + foundLog.EndState == expectedLog.EndState }) } diff --git a/rpc/clients/typescript/src/index.ts b/rpc/clients/typescript/src/index.ts index f64224fb4..17e860b98 100644 --- a/rpc/clients/typescript/src/index.ts +++ b/rpc/clients/typescript/src/index.ts @@ -2,7 +2,7 @@ export { WSClient } from './ws_client'; export { ClientConfig, WSOpts, - OrderEventKind, + OrderEventEndState, OrderEventPayload, OrderEvent, OrderInfo, diff --git a/rpc/clients/typescript/src/types.ts b/rpc/clients/typescript/src/types.ts index bcaf368a5..f4c2333d2 100644 --- a/rpc/clients/typescript/src/types.ts +++ b/rpc/clients/typescript/src/types.ts @@ -211,7 +211,7 @@ export interface ContractEvent { parameters: ContractEventParameters; } -export enum OrderEventKind { +export enum OrderEventEndState { Invalid = 'INVALID', Added = 'ADDED', Filled = 'FILLED', @@ -235,7 +235,7 @@ export interface HeartbeatEventPayload { export interface RawOrderEvent { orderHash: string; signedOrder: StringifiedSignedOrder; - kind: OrderEventKind; + endState: OrderEventEndState; fillableTakerAssetAmount: string; contractEvents: StringifiedContractEvent[]; } @@ -243,7 +243,7 @@ export interface RawOrderEvent { export interface OrderEvent { orderHash: string; signedOrder: SignedOrder; - kind: OrderEventKind; + endState: OrderEventEndState; fillableTakerAssetAmount: BigNumber; contractEvents: ContractEvent[]; } diff --git a/rpc/clients/typescript/src/ws_client.ts b/rpc/clients/typescript/src/ws_client.ts index ab1f36a71..7c653547e 100644 --- a/rpc/clients/typescript/src/ws_client.ts +++ b/rpc/clients/typescript/src/ws_client.ts @@ -280,7 +280,7 @@ export class WSClient { const orderEvent = { orderHash: rawOrderEvent.orderHash, signedOrder: orderParsingUtils.convertOrderStringFieldsToBigNumber(rawOrderEvent.signedOrder), - kind: rawOrderEvent.kind, + endState: rawOrderEvent.endState, fillableTakerAssetAmount: new BigNumber(rawOrderEvent.fillableTakerAssetAmount), contractEvents: WSClient._convertStringifiedContractEvents(rawOrderEvent.contractEvents), }; diff --git a/rpc/clients/typescript/test/ws_client_test.ts b/rpc/clients/typescript/test/ws_client_test.ts index 0459435c9..cf6c1a381 100644 --- a/rpc/clients/typescript/test/ws_client_test.ts +++ b/rpc/clients/typescript/test/ws_client_test.ts @@ -321,7 +321,7 @@ describe('WSClient', () => { "salt": "1559422141994", "signature": "0x1cf16c2f3a210965b5e17f51b57b869ba4ddda33df92b0017b4d8da9dacd3152b122a73844eaf50ccde29a42950239ba36a525ed7f1698a8a5e1896cf7d651aed203" }, - "kind": "CANCELLED", + "endState": "CANCELLED", "fillableTakerAssetAmount": 0, "contractEvents": [ { diff --git a/zeroex/order.go b/zeroex/order.go index 10897e25e..2aa964410 100644 --- a/zeroex/order.go +++ b/zeroex/order.go @@ -142,7 +142,7 @@ func (c ContractEvent) MarshalJSON() ([]byte, error) { type OrderEvent struct { OrderHash common.Hash `json:"orderHash"` SignedOrder *SignedOrder `json:"signedOrder"` - Kind OrderEventKind `json:"kind"` + EndState OrderEventEndState `json:"endState"` FillableTakerAssetAmount *big.Int `json:"fillableTakerAssetAmount"` // All the contract events that triggered this orders re-evaluation. They did not // all necessarily cause the orders state change itself, only it's re-evaluation. @@ -153,7 +153,7 @@ type OrderEvent struct { type orderEventJSON struct { OrderHash string `json:"orderHash"` SignedOrder *SignedOrder `json:"signedOrder"` - Kind string `json:"kind"` + EndState string `json:"endState"` FillableTakerAssetAmount string `json:"fillableTakerAssetAmount"` ContractEvents []*ContractEvent `json:"contractEvents"` } @@ -163,7 +163,7 @@ func (o OrderEvent) MarshalJSON() ([]byte, error) { return json.Marshal(map[string]interface{}{ "orderHash": o.OrderHash.Hex(), "signedOrder": o.SignedOrder, - "kind": o.Kind, + "endState": o.EndState, "fillableTakerAssetAmount": o.FillableTakerAssetAmount.String(), "contractEvents": o.ContractEvents, }) @@ -182,7 +182,7 @@ func (o *OrderEvent) UnmarshalJSON(data []byte) error { func (o *OrderEvent) fromOrderEventJSON(orderEventJSON orderEventJSON) error { o.OrderHash = common.HexToHash(orderEventJSON.OrderHash) o.SignedOrder = orderEventJSON.SignedOrder - o.Kind = OrderEventKind(orderEventJSON.Kind) + o.EndState = OrderEventEndState(orderEventJSON.EndState) var ok bool o.FillableTakerAssetAmount, ok = math.ParseBig256(orderEventJSON.FillableTakerAssetAmount) if !ok { @@ -192,23 +192,24 @@ func (o *OrderEvent) fromOrderEventJSON(orderEventJSON orderEventJSON) error { return nil } -// OrderEventKind enumerates all the possible order event types -type OrderEventKind string +// OrderEventEndState enumerates all the possible order event types. An OrderEventEndState describes the +// end state of a 0x order after revalidation +type OrderEventEndState string -// OrderEventKind values +// OrderEventEndState values const ( - EKInvalid = OrderEventKind("INVALID") - EKOrderAdded = OrderEventKind("ADDED") - EKOrderFilled = OrderEventKind("FILLED") - EKOrderFullyFilled = OrderEventKind("FULLY_FILLED") - EKOrderCancelled = OrderEventKind("CANCELLED") - EKOrderExpired = OrderEventKind("EXPIRED") + ESInvalid = OrderEventEndState("INVALID") + ESOrderAdded = OrderEventEndState("ADDED") + ESOrderFilled = OrderEventEndState("FILLED") + ESOrderFullyFilled = OrderEventEndState("FULLY_FILLED") + ESOrderCancelled = OrderEventEndState("CANCELLED") + ESOrderExpired = OrderEventEndState("EXPIRED") // An order becomes unfunded if the maker transfers the balance / changes their // allowance backing an order - EKOrderBecameUnfunded = OrderEventKind("UNFUNDED") + ESOrderBecameUnfunded = OrderEventEndState("UNFUNDED") // Fillability for an order can increase if a previously processed fill event // gets reverted, or if a maker tops up their balance/allowance backing an order - EKOrderFillabilityIncreased = OrderEventKind("FILLABILITY_INCREASED") + ESOrderFillabilityIncreased = OrderEventEndState("FILLABILITY_INCREASED") ) var eip712OrderTypes = signer.Types{ diff --git a/zeroex/order_js.go b/zeroex/order_js.go index 2a284c461..d0d3c3fdd 100644 --- a/zeroex/order_js.go +++ b/zeroex/order_js.go @@ -19,7 +19,7 @@ func (o OrderEvent) JSValue() js.Value { return js.ValueOf(map[string]interface{}{ "orderHash": o.OrderHash.Hex(), "signedOrder": o.SignedOrder.JSValue(), - "kind": string(o.Kind), + "endState": string(o.EndState), "fillableTakerAssetAmount": o.FillableTakerAssetAmount.String(), "contractEvents": contractEventsJS, }) diff --git a/zeroex/order_test.go b/zeroex/order_test.go index 3060b7560..173f02596 100644 --- a/zeroex/order_test.go +++ b/zeroex/order_test.go @@ -56,7 +56,7 @@ func TestMarshalUnmarshalOrderEvent(t *testing.T) { orderEvent := OrderEvent{ OrderHash: orderHash, SignedOrder: signedOrder, - Kind: EKOrderAdded, + EndState: ESOrderAdded, FillableTakerAssetAmount: big.NewInt(2000), ContractEvents: []*ContractEvent{}, } diff --git a/zeroex/ordervalidator/order_validator.go b/zeroex/ordervalidator/order_validator.go index a1f65f752..cdd349e75 100644 --- a/zeroex/ordervalidator/order_validator.go +++ b/zeroex/ordervalidator/order_validator.go @@ -186,20 +186,20 @@ var ( // ROInvalidSchemaCode is the RejectedOrderStatus emitted if an order doesn't conform to the order schema const ROInvalidSchemaCode = "InvalidSchema" -// ConvertRejectOrderCodeToOrderEventKind converts an RejectOrderCode to an OrderEventKind type -func ConvertRejectOrderCodeToOrderEventKind(rejectedOrderStatus RejectedOrderStatus) (zeroex.OrderEventKind, bool) { +// ConvertRejectOrderCodeToOrderEventEndState converts an RejectOrderCode to an OrderEventEndState type +func ConvertRejectOrderCodeToOrderEventEndState(rejectedOrderStatus RejectedOrderStatus) (zeroex.OrderEventEndState, bool) { switch rejectedOrderStatus { case ROExpired: - return zeroex.EKOrderExpired, true + return zeroex.ESOrderExpired, true case ROFullyFilled: - return zeroex.EKOrderFullyFilled, true + return zeroex.ESOrderFullyFilled, true case ROCancelled: - return zeroex.EKOrderCancelled, true + return zeroex.ESOrderCancelled, true case ROUnfunded: - return zeroex.EKOrderBecameUnfunded, true + return zeroex.ESOrderBecameUnfunded, true default: - // Catch-all returns Invalid OrderEventKind - return zeroex.EKInvalid, false + // Catch-all returns Invalid OrderEventEndState + return zeroex.ESInvalid, false } } diff --git a/zeroex/orderwatch/order_watcher.go b/zeroex/orderwatch/order_watcher.go index 8c939398c..d354984e1 100644 --- a/zeroex/orderwatch/order_watcher.go +++ b/zeroex/orderwatch/order_watcher.go @@ -229,7 +229,7 @@ func (w *Watcher) handleExpiration(expiredOrders []expirationwatch.ExpiredItem) OrderHash: common.HexToHash(expiredOrder.ID), SignedOrder: order.SignedOrder, FillableTakerAssetAmount: big.NewInt(0), - Kind: zeroex.EKOrderExpired, + EndState: zeroex.ESOrderExpired, } orderEvents = append(orderEvents, orderEvent) } @@ -532,7 +532,7 @@ func (w *Watcher) Add(orderInfo *ordervalidator.AcceptedOrderInfo) error { OrderHash: orderInfo.OrderHash, SignedOrder: orderInfo.SignedOrder, FillableTakerAssetAmount: orderInfo.FillableTakerAssetAmount, - Kind: zeroex.EKOrderAdded, + EndState: zeroex.ESOrderAdded, } w.orderFeed.Send([]*zeroex.OrderEvent{orderEvent}) @@ -629,7 +629,7 @@ func (w *Watcher) generateOrderEventsIfChanged(ordersColTxn *db.Transaction, ord OrderHash: acceptedOrderInfo.OrderHash, SignedOrder: order.SignedOrder, FillableTakerAssetAmount: acceptedOrderInfo.FillableTakerAssetAmount, - Kind: zeroex.EKOrderAdded, + EndState: zeroex.ESOrderAdded, ContractEvents: orderHashToEvents[order.Hash], } orderEvents = append(orderEvents, orderEvent) @@ -642,7 +642,7 @@ func (w *Watcher) generateOrderEventsIfChanged(ordersColTxn *db.Transaction, ord orderEvent := &zeroex.OrderEvent{ OrderHash: acceptedOrderInfo.OrderHash, SignedOrder: order.SignedOrder, - Kind: zeroex.EKOrderFilled, + EndState: zeroex.ESOrderFilled, FillableTakerAssetAmount: acceptedOrderInfo.FillableTakerAssetAmount, ContractEvents: orderHashToEvents[order.Hash], } @@ -654,7 +654,7 @@ func (w *Watcher) generateOrderEventsIfChanged(ordersColTxn *db.Transaction, ord orderEvent := &zeroex.OrderEvent{ OrderHash: acceptedOrderInfo.OrderHash, SignedOrder: order.SignedOrder, - Kind: zeroex.EKOrderFillabilityIncreased, + EndState: zeroex.ESOrderFillabilityIncreased, FillableTakerAssetAmount: acceptedOrderInfo.FillableTakerAssetAmount, ContractEvents: orderHashToEvents[order.Hash], } @@ -675,17 +675,17 @@ func (w *Watcher) generateOrderEventsIfChanged(ordersColTxn *db.Transaction, ord } else { // If oldFillableAmount > 0, it got fullyFilled, cancelled, expired or unfunded, emit event w.unwatchOrder(ordersColTxn, order, big.NewInt(0)) - kind, ok := ordervalidator.ConvertRejectOrderCodeToOrderEventKind(rejectedOrderInfo.Status) + endState, ok := ordervalidator.ConvertRejectOrderCodeToOrderEventEndState(rejectedOrderInfo.Status) if !ok { - err := fmt.Errorf("no OrderEventKind corresponding to RejectedOrderStatus: %q", rejectedOrderInfo.Status) - logger.WithError(err).WithField("rejectedOrderStatus", rejectedOrderInfo.Status).Error("no OrderEventKind corresponding to RejectedOrderStatus") + err := fmt.Errorf("no OrderEventEndState corresponding to RejectedOrderStatus: %q", rejectedOrderInfo.Status) + logger.WithError(err).WithField("rejectedOrderStatus", rejectedOrderInfo.Status).Error("no OrderEventEndState corresponding to RejectedOrderStatus") return err } orderEvent := &zeroex.OrderEvent{ OrderHash: rejectedOrderInfo.OrderHash, SignedOrder: rejectedOrderInfo.SignedOrder, FillableTakerAssetAmount: big.NewInt(0), - Kind: kind, + EndState: endState, ContractEvents: orderHashToEvents[order.Hash], } orderEvents = append(orderEvents, orderEvent) diff --git a/zeroex/orderwatch/order_watcher_test.go b/zeroex/orderwatch/order_watcher_test.go index 9c6d62bc4..6af88d364 100644 --- a/zeroex/orderwatch/order_watcher_test.go +++ b/zeroex/orderwatch/order_watcher_test.go @@ -116,7 +116,7 @@ func TestOrderWatcherUnfundedInsufficientERC20Balance(t *testing.T) { orderEvents := waitForOrderEvents(t, orderEventsChan, 4*time.Second) require.Len(t, orderEvents, 1) orderEvent := orderEvents[0] - assert.Equal(t, zeroex.EKOrderBecameUnfunded, orderEvent.Kind) + assert.Equal(t, zeroex.ESOrderBecameUnfunded, orderEvent.EndState) var orders []*meshdb.Order err = meshDB.Orders.FindAll(&orders) @@ -154,7 +154,7 @@ func TestOrderWatcherUnfundedInsufficientERC721Balance(t *testing.T) { orderEvents := waitForOrderEvents(t, orderEventsChan, 4*time.Second) require.Len(t, orderEvents, 1) orderEvent := orderEvents[0] - assert.Equal(t, zeroex.EKOrderBecameUnfunded, orderEvent.Kind) + assert.Equal(t, zeroex.ESOrderBecameUnfunded, orderEvent.EndState) var orders []*meshdb.Order err = meshDB.Orders.FindAll(&orders) @@ -193,7 +193,7 @@ func TestOrderWatcherUnfundedInsufficientERC721Allowance(t *testing.T) { orderEvents := waitForOrderEvents(t, orderEventsChan, 4*time.Second) require.Len(t, orderEvents, 1) orderEvent := orderEvents[0] - assert.Equal(t, zeroex.EKOrderBecameUnfunded, orderEvent.Kind) + assert.Equal(t, zeroex.ESOrderBecameUnfunded, orderEvent.EndState) var orders []*meshdb.Order err = meshDB.Orders.FindAll(&orders) @@ -232,7 +232,7 @@ func TestOrderWatcherUnfundedInsufficientERC20Allowance(t *testing.T) { orderEvents := waitForOrderEvents(t, orderEventsChan, 4*time.Second) require.Len(t, orderEvents, 1) orderEvent := orderEvents[0] - assert.Equal(t, zeroex.EKOrderBecameUnfunded, orderEvent.Kind) + assert.Equal(t, zeroex.ESOrderBecameUnfunded, orderEvent.EndState) var orders []*meshdb.Order err = meshDB.Orders.FindAll(&orders) @@ -270,7 +270,7 @@ func TestOrderWatcherUnfundedThenFundedAgain(t *testing.T) { orderEvents := waitForOrderEvents(t, orderEventsChan, 4*time.Second) require.Len(t, orderEvents, 1) orderEvent := orderEvents[0] - assert.Equal(t, zeroex.EKOrderBecameUnfunded, orderEvent.Kind) + assert.Equal(t, zeroex.ESOrderBecameUnfunded, orderEvent.EndState) var orders []*meshdb.Order err = meshDB.Orders.FindAll(&orders) @@ -292,7 +292,7 @@ func TestOrderWatcherUnfundedThenFundedAgain(t *testing.T) { orderEvents = <-orderEventsChan require.Len(t, orderEvents, 1) orderEvent = orderEvents[0] - assert.Equal(t, zeroex.EKOrderAdded, orderEvent.Kind) + assert.Equal(t, zeroex.ESOrderAdded, orderEvent.EndState) var newOrders []*meshdb.Order err = meshDB.Orders.FindAll(&newOrders) @@ -375,7 +375,7 @@ func TestOrderWatcherWETHWithdrawAndDeposit(t *testing.T) { orderEvents := waitForOrderEvents(t, orderEventsChan, 4*time.Second) require.Len(t, orderEvents, 1) orderEvent := orderEvents[0] - assert.Equal(t, zeroex.EKOrderBecameUnfunded, orderEvent.Kind) + assert.Equal(t, zeroex.ESOrderBecameUnfunded, orderEvent.EndState) var orders []*meshdb.Order err = meshDB.Orders.FindAll(&orders) @@ -396,7 +396,7 @@ func TestOrderWatcherWETHWithdrawAndDeposit(t *testing.T) { orderEvents = <-orderEventsChan require.Len(t, orderEvents, 1) orderEvent = orderEvents[0] - assert.Equal(t, zeroex.EKOrderAdded, orderEvent.Kind) + assert.Equal(t, zeroex.ESOrderAdded, orderEvent.EndState) var newOrders []*meshdb.Order err = meshDB.Orders.FindAll(&newOrders) @@ -435,7 +435,7 @@ func TestOrderWatcherCanceled(t *testing.T) { orderEvents := waitForOrderEvents(t, orderEventsChan, 4*time.Second) require.Len(t, orderEvents, 1) orderEvent := orderEvents[0] - assert.Equal(t, zeroex.EKOrderCancelled, orderEvent.Kind) + assert.Equal(t, zeroex.ESOrderCancelled, orderEvent.EndState) var orders []*meshdb.Order err = meshDB.Orders.FindAll(&orders) @@ -474,7 +474,7 @@ func TestOrderWatcherCancelUpTo(t *testing.T) { orderEvents := waitForOrderEvents(t, orderEventsChan, 4*time.Second) require.Len(t, orderEvents, 1) orderEvent := orderEvents[0] - assert.Equal(t, zeroex.EKOrderCancelled, orderEvent.Kind) + assert.Equal(t, zeroex.ESOrderCancelled, orderEvent.EndState) var orders []*meshdb.Order err = meshDB.Orders.FindAll(&orders) @@ -513,7 +513,7 @@ func TestOrderWatcherERC20Filled(t *testing.T) { orderEvents := waitForOrderEvents(t, orderEventsChan, 4*time.Second) require.Len(t, orderEvents, 1) orderEvent := orderEvents[0] - assert.Equal(t, zeroex.EKOrderFullyFilled, orderEvent.Kind) + assert.Equal(t, zeroex.ESOrderFullyFilled, orderEvent.EndState) var orders []*meshdb.Order err = meshDB.Orders.FindAll(&orders) From e4ba0046a8e19cccf514ed6bce1faeffc3d9a9c0 Mon Sep 17 00:00:00 2001 From: fabioberger Date: Wed, 2 Oct 2019 12:13:10 +0800 Subject: [PATCH 10/17] Add a CHANGELOG entry about renaming Kind to EndState --- CHANGELOG.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ec7372c67..f02fcd019 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,7 +6,8 @@ This changelog is a work in progress and may contain notes for versions which ha ### Breaking changes 🛠 -- Removes the `txHashes` key in the `OrderEvent`s emitted from the `orders` JSON-RPC subscription and replaced it with `contractEvents`, an array of decoded order-relevant contract events. Parsing these events allows callers to find every discrete order fill/cancel event. +- Removes the `txHashes` key in the `OrderEvent`s emitted from the `orders` JSON-RPC subscription and replaced it with `contractEvents`, an array of decoded order-relevant contract events. Parsing these events allows callers to find every discrete order fill/cancel event. ([#420](https://github.com/0xProject/0x-mesh/pull/420)) +- Renames the `Kind` key in `OrderEvent` to `EndState` to better elucidate that it represents the aggregate change to the orders state since it was last re-validated. As an end state, it does not capture any possible intermediate states the order might have been in since the last re-validation. Intermediate states can be inferred from the `contractEvents` included ([#420](https://github.com/0xProject/0x-mesh/pull/420)) ### Features ✅ From a6cac3dc8f10da35330656224b969abce6f3d821 Mon Sep 17 00:00:00 2001 From: fabioberger Date: Wed, 2 Oct 2019 12:17:55 +0800 Subject: [PATCH 11/17] Stop exporting Wrapper* interfaces --- browser/ts/index.ts | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/browser/ts/index.ts b/browser/ts/index.ts index 484b4312e..324eea401 100644 --- a/browser/ts/index.ts +++ b/browser/ts/index.ts @@ -131,7 +131,7 @@ export interface ERC20TransferEvent { value: BigNumber; } -export interface WrapperERC20TransferEvent { +interface WrapperERC20TransferEvent { from: string; to: string; value: string; @@ -143,7 +143,7 @@ export interface ERC20ApprovalEvent { value: BigNumber; } -export interface WrapperERC20ApprovalEvent { +interface WrapperERC20ApprovalEvent { owner: string; spender: string; value: string; @@ -155,7 +155,7 @@ export interface ERC721TransferEvent { tokenId: BigNumber; } -export interface WrapperERC721TransferEvent { +interface WrapperERC721TransferEvent { from: string; to: string; tokenId: string; @@ -167,7 +167,7 @@ export interface ERC721ApprovalEvent { tokenId: BigNumber; } -export interface WrapperERC721ApprovalEvent { +interface WrapperERC721ApprovalEvent { owner: string; approved: string; tokenId: string; @@ -193,7 +193,7 @@ export interface ExchangeFillEvent { takerAssetData: string; } -export interface WrapperExchangeFillEvent { +interface WrapperExchangeFillEvent { makerAddress: string; takerAddress: string; senderAddress: string; @@ -222,7 +222,7 @@ export interface ExchangeCancelUpToEvent { orderEpoch: BigNumber; } -export interface WrapperExchangeCancelUpToEvent { +interface WrapperExchangeCancelUpToEvent { makerAddress: string; senderAddress: string; orderEpoch: string; @@ -233,7 +233,7 @@ export interface WethWithdrawalEvent { value: BigNumber; } -export interface WrapperWethWithdrawalEvent { +interface WrapperWethWithdrawalEvent { owner: string; value: string; } @@ -243,7 +243,7 @@ export interface WethDepositEvent { value: BigNumber; } -export interface WrapperWethDepositEvent { +interface WrapperWethDepositEvent { owner: string; value: string; } @@ -280,7 +280,7 @@ export interface ContractEvent { } // The type for order events exposed by MeshWrapper. -export interface WrapperContractEvent { +interface WrapperContractEvent { blockHash: string; txHash: string; txIndex: number; @@ -302,7 +302,7 @@ export enum OrderEventEndState { FillabilityIncreased = 'FILLABILITY_INCREASED', } -export interface WrapperOrderEvent { +interface WrapperOrderEvent { orderHash: string; signedOrder: WrapperSignedOrder; endState: OrderEventEndState; From ffc3f0e07b658203d1f5477cc8f41c6db650c74d Mon Sep 17 00:00:00 2001 From: fabioberger Date: Wed, 2 Oct 2019 12:18:35 +0800 Subject: [PATCH 12/17] Move comment to correct place --- browser/ts/index.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/browser/ts/index.ts b/browser/ts/index.ts index 324eea401..facd19faf 100644 --- a/browser/ts/index.ts +++ b/browser/ts/index.ts @@ -264,10 +264,6 @@ type WrapperContractEventParameters = WrapperERC20TransferEvent | WrapperERC20A type ContractEventParameters = ERC20TransferEvent | ERC20ApprovalEvent | ERC721TransferEvent | ERC721ApprovalEvent | ExchangeFillEvent | ExchangeCancelUpToEvent | WethWithdrawalEvent | WethDepositEvent | ERC721ApprovalForAllEvent | ExchangeCancelEvent; -/** - * Order events are fired by Mesh whenever an order is added, canceled, expired, - * or filled. - */ export interface ContractEvent { blockHash: string; txHash: string; @@ -310,6 +306,10 @@ interface WrapperOrderEvent { contractEvents: WrapperContractEvent[]; } +/** + * Order events are fired by Mesh whenever an order is added, canceled, expired, + * or filled. + */ export interface OrderEvent { orderHash: string; signedOrder: SignedOrder; From 248e185622259cc25c277ed68b529ca07302510f Mon Sep 17 00:00:00 2001 From: fabioberger Date: Wed, 2 Oct 2019 12:19:35 +0800 Subject: [PATCH 13/17] Improve example wording --- docs/rpc_api.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/docs/rpc_api.md b/docs/rpc_api.md index 64589fa82..cde7f440f 100644 --- a/docs/rpc_api.md +++ b/docs/rpc_api.md @@ -213,7 +213,10 @@ Gets certain configurations and stats about a Mesh node. Allows the caller to subscribe to a stream of `OrderEvents`. An `OrderEvent` contains either newly discovered orders found by Mesh via the P2P network, or updates to the fillability of a previously discovered order (e.g., if an order gets filled, cancelled, expired, etc...). `OrderEvent`s _do not_ correspond 1-to-1 to smart contract events. Rather, an `OrderEvent` about an orders fillability change represents the aggregate change to it's fillability given _all_ the transactions included within the most recently mined/reverted blocks. -**Example:** If an order is both `filled` and `cancelled` within a single block, only a cancellation `OrderEvent` will be emitted (since this is the final state of the order after this block is mined). The cancellation `OrderEvent` _will_ however list the contract events intercepted that could have impacted this orders fillability. This list will include both the fill event and cancellation event. +**Example:** If an order is both `filled` and `cancelled` within a single block, the `EndState` +of the `OrderEvent` will be `CANCELLED` (since this is the final state of the order after this block is +mined). The `OrderEvent` _will_ however list the contract events intercepted that could have impacted +this orders fillability. This list will include both the fill event and cancellation event. Mesh has implemented subscriptions in the [same manner as Geth](https://github.com/ethereum/go-ethereum/wiki/RPC-PUB-SUB). In order to start a subscription, you must send the following payload: From efdcac029fdccf4a49294efa71dc9c1c7dd766d4 Mon Sep 17 00:00:00 2001 From: fabioberger Date: Wed, 2 Oct 2019 12:39:59 +0800 Subject: [PATCH 14/17] Add documentation for new `blockNumber` param to BatchValidate --- zeroex/ordervalidator/order_validator.go | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/zeroex/ordervalidator/order_validator.go b/zeroex/ordervalidator/order_validator.go index cdd349e75..744cbb16f 100644 --- a/zeroex/ordervalidator/order_validator.go +++ b/zeroex/ordervalidator/order_validator.go @@ -272,8 +272,10 @@ func New(ethClient *ethclient.Client, networkID int, maxRequestContentLength int // BatchValidate retrieves all the information needed to validate the supplied orders. // It splits the orders into chunks of `chunkSize`, and makes no more then `concurrencyLimit` // requests concurrently. If a request fails, re-attempt it up to four times before giving up. -// If it some requests fail, this method still returns whatever order information it was able to -// retrieve. +// If some requests fail, this method still returns whatever order information it was able to +// retrieve up until the failure. +// The `blockNumber` parameter lets the caller specify a specific block height at which to validate +// the orders. This can be set to the `latest` block or any other historical block number. func (o *OrderValidator) BatchValidate(rawSignedOrders []*zeroex.SignedOrder, areNewOrders bool, blockNumber rpc.BlockNumber) *ValidationResults { if len(rawSignedOrders) == 0 { return &ValidationResults{} From 9248d8c62be5b13fc4db33cfc18de2cd9b250b17 Mon Sep 17 00:00:00 2001 From: fabioberger Date: Wed, 2 Oct 2019 17:25:45 +0800 Subject: [PATCH 15/17] Add type for contract event param --- zeroex/order.go | 21 +++++++++++++-------- zeroex/orderwatch/order_watcher.go | 2 +- 2 files changed, 14 insertions(+), 9 deletions(-) diff --git a/zeroex/order.go b/zeroex/order.go index 2aa964410..9ea1958da 100644 --- a/zeroex/order.go +++ b/zeroex/order.go @@ -8,8 +8,8 @@ import ( "strings" "github.com/0xProject/0x-mesh/ethereum" - "github.com/0xProject/0x-mesh/zeroex/orderwatch/decoder" "github.com/0xProject/0x-mesh/ethereum/wrappers" + "github.com/0xProject/0x-mesh/zeroex/orderwatch/decoder" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/math" signer "github.com/ethereum/go-ethereum/signer/core" @@ -75,6 +75,11 @@ const ( OSInvalidTakerAssetData ) +// ContractEventParameters is the parameters of a ContractEvent +type ContractEventParameters interface { + json.Marshaler +} + // ContractEvent is an event emitted by a smart contract type ContractEvent struct { BlockHash common.Hash @@ -84,7 +89,7 @@ type ContractEvent struct { IsRemoved bool Address common.Address Kind string - Parameters interface{} + Parameters ContractEventParameters } // MarshalJSON implements a custom JSON marshaller for the ContractEvent type @@ -140,10 +145,10 @@ func (c ContractEvent) MarshalJSON() ([]byte, error) { // OrderEvent is the order event emitted by Mesh nodes on the "orders" topic // when calling JSON-RPC method `mesh_subscribe` type OrderEvent struct { - OrderHash common.Hash `json:"orderHash"` - SignedOrder *SignedOrder `json:"signedOrder"` - EndState OrderEventEndState `json:"endState"` - FillableTakerAssetAmount *big.Int `json:"fillableTakerAssetAmount"` + OrderHash common.Hash `json:"orderHash"` + SignedOrder *SignedOrder `json:"signedOrder"` + EndState OrderEventEndState `json:"endState"` + FillableTakerAssetAmount *big.Int `json:"fillableTakerAssetAmount"` // All the contract events that triggered this orders re-evaluation. They did not // all necessarily cause the orders state change itself, only it's re-evaluation. // Since it's state _did_ change, at least one of them did cause the actual state change. @@ -153,7 +158,7 @@ type OrderEvent struct { type orderEventJSON struct { OrderHash string `json:"orderHash"` SignedOrder *SignedOrder `json:"signedOrder"` - EndState string `json:"endState"` + EndState string `json:"endState"` FillableTakerAssetAmount string `json:"fillableTakerAssetAmount"` ContractEvents []*ContractEvent `json:"contractEvents"` } @@ -163,7 +168,7 @@ func (o OrderEvent) MarshalJSON() ([]byte, error) { return json.Marshal(map[string]interface{}{ "orderHash": o.OrderHash.Hex(), "signedOrder": o.SignedOrder, - "endState": o.EndState, + "endState": o.EndState, "fillableTakerAssetAmount": o.FillableTakerAssetAmount.String(), "contractEvents": o.ContractEvents, }) diff --git a/zeroex/orderwatch/order_watcher.go b/zeroex/orderwatch/order_watcher.go index d354984e1..4668dce33 100644 --- a/zeroex/orderwatch/order_watcher.go +++ b/zeroex/orderwatch/order_watcher.go @@ -247,7 +247,7 @@ func (w *Watcher) handleBlockEvents(events []*blockwatch.Event) error { for _, event := range events { latestBlockNumber = rpc.BlockNumber(event.BlockHeader.Number.Int64()) for _, log := range event.BlockHeader.Logs { - var parameters interface{} + var parameters zeroex.ContractEventParameters eventType, err := w.eventDecoder.FindEventType(log) if err != nil { switch err.(type) { From c9ae6ae26648fce280a47f2157969a95cc5dcd34 Mon Sep 17 00:00:00 2001 From: fabioberger Date: Thu, 3 Oct 2019 13:14:46 +0800 Subject: [PATCH 16/17] Remove unnecessary switch statements --- zeroex/order.go | 52 +++++++--------------------------------------- zeroex/order_js.go | 37 +-------------------------------- 2 files changed, 9 insertions(+), 80 deletions(-) diff --git a/zeroex/order.go b/zeroex/order.go index 9ea1958da..904b13f74 100644 --- a/zeroex/order.go +++ b/zeroex/order.go @@ -9,7 +9,6 @@ import ( "github.com/0xProject/0x-mesh/ethereum" "github.com/0xProject/0x-mesh/ethereum/wrappers" - "github.com/0xProject/0x-mesh/zeroex/orderwatch/decoder" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/math" signer "github.com/ethereum/go-ethereum/signer/core" @@ -95,50 +94,15 @@ type ContractEvent struct { // MarshalJSON implements a custom JSON marshaller for the ContractEvent type func (c ContractEvent) MarshalJSON() ([]byte, error) { m := map[string]interface{}{ - "blockHash": c.BlockHash.Hex(), - "txHash": c.TxHash.Hex(), - "txIndex": c.TxIndex, - "logIndex": c.LogIndex, - "isRemoved": c.IsRemoved, - "address": c.Address, - "kind": c.Kind, + "blockHash": c.BlockHash.Hex(), + "txHash": c.TxHash.Hex(), + "txIndex": c.TxIndex, + "logIndex": c.LogIndex, + "isRemoved": c.IsRemoved, + "address": c.Address, + "kind": c.Kind, + "parameters": c.Parameters, } - switch c.Kind { - case "ERC20TransferEvent": - m["parameters"] = c.Parameters.(decoder.ERC20TransferEvent) - return json.Marshal(m) - - case "ERC20ApprovalEvent": - m["parameters"] = c.Parameters.(decoder.ERC20ApprovalEvent) - - case "ERC721TransferEvent": - m["parameters"] = c.Parameters.(decoder.ERC721TransferEvent) - - case "ERC721ApprovalEvent": - m["parameters"] = c.Parameters.(decoder.ERC721ApprovalEvent) - - case "ERC721ApprovalForAllEvent": - m["parameters"] = c.Parameters.(decoder.ERC721ApprovalForAllEvent) - - case "WethWithdrawalEvent": - m["parameters"] = c.Parameters.(decoder.WethWithdrawalEvent) - - case "WethDepositEvent": - m["parameters"] = c.Parameters.(decoder.WethDepositEvent) - - case "ExchangeFillEvent": - m["parameters"] = c.Parameters.(decoder.ExchangeFillEvent) - - case "ExchangeCancelEvent": - m["parameters"] = c.Parameters.(decoder.ExchangeCancelEvent) - - case "ExchangeCancelUpToEvent": - m["parameters"] = c.Parameters.(decoder.ExchangeCancelUpToEvent) - - default: - panic(fmt.Sprintf("Unrecognized event encountered: %s", c.Kind)) - } - return json.Marshal(m) } diff --git a/zeroex/order_js.go b/zeroex/order_js.go index d0d3c3fdd..45fc320ca 100644 --- a/zeroex/order_js.go +++ b/zeroex/order_js.go @@ -6,8 +6,6 @@ import ( "fmt" "strings" "syscall/js" - - "github.com/0xProject/0x-mesh/zeroex/orderwatch/decoder" "github.com/ethereum/go-ethereum/common" ) @@ -65,40 +63,7 @@ func (c ContractEvent) JSValue() js.Value { "logIndex": c.LogIndex, "isRemoved": c.IsRemoved, "kind": c.Kind, - } - switch c.Kind { - case "ERC20TransferEvent": - m["parameters"] = c.Parameters.(decoder.ERC20TransferEvent).JSValue() - - case "ERC20ApprovalEvent": - m["parameters"] = c.Parameters.(decoder.ERC20ApprovalEvent).JSValue() - - case "ERC721TransferEvent": - m["parameters"] = c.Parameters.(decoder.ERC721TransferEvent).JSValue() - - case "ERC721ApprovalEvent": - m["parameters"] = c.Parameters.(decoder.ERC721ApprovalEvent).JSValue() - - case "ERC721ApprovalForAllEvent": - m["parameters"] = c.Parameters.(decoder.ERC721ApprovalForAllEvent).JSValue() - - case "WethWithdrawalEvent": - m["parameters"] = c.Parameters.(decoder.WethWithdrawalEvent).JSValue() - - case "WethDepositEvent": - m["parameters"] = c.Parameters.(decoder.WethDepositEvent).JSValue() - - case "ExchangeFillEvent": - m["parameters"] = c.Parameters.(decoder.ExchangeFillEvent).JSValue() - - case "ExchangeCancelEvent": - m["parameters"] = c.Parameters.(decoder.ExchangeCancelEvent).JSValue() - - case "ExchangeCancelUpToEvent": - m["parameters"] = c.Parameters.(decoder.ExchangeCancelUpToEvent).JSValue() - - default: - panic(fmt.Sprintf("Unrecognized event encountered: %s", c.Kind)) + "parameters": c.Parameters.(js.Wrapper).JSValue(), } return js.ValueOf(m) } From fe0cff9b41b2395171025f04c8e468502ddfe5bf Mon Sep 17 00:00:00 2001 From: fabioberger Date: Thu, 3 Oct 2019 14:00:27 +0800 Subject: [PATCH 17/17] Add yarn cache clean step to integration tests --- integration-tests/integration_test.go | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/integration-tests/integration_test.go b/integration-tests/integration_test.go index c821f6eda..8da4c2d27 100644 --- a/integration-tests/integration_test.go +++ b/integration-tests/integration_test.go @@ -196,7 +196,7 @@ func TestBrowserIntegration(t *testing.T) { // Next, wait for the order to be received. expectedOrderEventLog := orderEventLog{ OrderHash: standaloneOrderHash.Hex(), - EndState: "ADDED", + EndState: "ADDED", } _, err = waitForOrderEventLog(ctx, browserLogMessages, expectedOrderEventLog) assert.NoError(t, err, "Browser node did not receive order sent by standalone node") @@ -245,6 +245,12 @@ func buildForTests(t *testing.T, ctx context.Context) { output, err = cmd.CombinedOutput() require.NoError(t, err, "could not build mesh-bootstrap: %s", string(output)) + fmt.Println("Clear yarn cache...") + cmd = exec.CommandContext(ctx, "yarn", "cache", "clean") + cmd.Dir = "../browser" + output, err = cmd.CombinedOutput() + require.NoError(t, err, "could not clean yarn cache: %s", string(output)) + fmt.Println("Installing dependencies for TypeScript bindings...") cmd = exec.CommandContext(ctx, "yarn", "install") cmd.Dir = "../browser" @@ -419,7 +425,7 @@ func waitForReceivedOrderLog(ctx context.Context, logMessages <-chan string, exp // by Mesh. They need to be explicitly logged. type orderEventLog struct { OrderHash string `json:"orderHash"` - EndState string `json:"endState"` + EndState string `json:"endState"` } func waitForOrderEventLog(ctx context.Context, logMessages <-chan string, expectedLog orderEventLog) (string, error) {