From 25f1b7701fa1c5402e7d6f91cccdbd63bb9a525c Mon Sep 17 00:00:00 2001
From: Alex Browne <stephenalexbrowne@gmail.com>
Date: Tue, 25 Sep 2018 15:03:49 -0700
Subject: Implement scraping and parsing exchange cancel events

---
 .../src/data_types/events/exchange_events.ts       | 41 +++++++++++++++++++---
 .../pipeline/src/entities/ExchangeCancelEvent.ts   | 32 +++++++++++++++++
 .../pipeline/src/entities/ExchangeFillEvent.ts     |  6 ++--
 packages/pipeline/src/index.ts                     | 11 ++++--
 4 files changed, 80 insertions(+), 10 deletions(-)
 create mode 100644 packages/pipeline/src/entities/ExchangeCancelEvent.ts

(limited to 'packages/pipeline/src')

diff --git a/packages/pipeline/src/data_types/events/exchange_events.ts b/packages/pipeline/src/data_types/events/exchange_events.ts
index fb5341b58..3938f3a69 100644
--- a/packages/pipeline/src/data_types/events/exchange_events.ts
+++ b/packages/pipeline/src/data_types/events/exchange_events.ts
@@ -1,4 +1,4 @@
-import { ExchangeEventArgs, ExchangeFillEventArgs } from '@0xproject/contract-wrappers';
+import { ExchangeCancelEventArgs, ExchangeEventArgs, ExchangeFillEventArgs } from '@0xproject/contract-wrappers';
 import { assetDataUtils } from '@0xproject/order-utils';
 import { AssetProxyId, ERC721AssetData } from '@0xproject/types';
 import { BigNumber } from '@0xproject/utils';
@@ -7,12 +7,12 @@ import * as R from 'ramda';
 
 import { artifacts } from '../../artifacts';
 import { EventsResponse } from '../../data_sources/etherscan';
+import { ExchangeCancelEvent } from '../../entities/ExchangeCancelEvent';
 import { ExchangeFillEvent } from '../../entities/ExchangeFillEvent';
 
 import { convertResponseToLogEntry, decodeLogEntry } from './event_utils';
 
-// TODO(albrow): Union with other exchange event entity types
-export type ExchangeEventEntity = ExchangeFillEvent;
+export type ExchangeEventEntity = ExchangeFillEvent | ExchangeCancelEvent;
 
 const exchangeContractAbi = artifacts.Exchange.compilerOutput.abi;
 
@@ -22,7 +22,7 @@ export function parseExchangeEvents(rawEventsResponse: EventsResponse): Exchange
         eventResponse => decodeLogEntry<ExchangeEventArgs>(exchangeContractAbi, eventResponse),
         logEntries,
     );
-    const filteredLogEntries = R.filter(logEntry => R.contains(logEntry.event, ['Fill']), decodedLogEntries);
+    const filteredLogEntries = R.filter(logEntry => R.contains(logEntry.event, ['Fill', 'Cancel']), decodedLogEntries);
     return R.map(_convertToEntity, filteredLogEntries);
 }
 
@@ -30,6 +30,8 @@ export function _convertToEntity(eventLog: LogWithDecodedArgs<ExchangeEventArgs>
     switch (eventLog.event) {
         case 'Fill':
             return _convertToExchangeFillEvent(eventLog as LogWithDecodedArgs<ExchangeFillEventArgs>);
+        case 'Cancel':
+            return _convertToExchangeCancelEvent(eventLog as LogWithDecodedArgs<ExchangeCancelEventArgs>);
         default:
             throw new Error('unexpected eventLog.event type: ' + eventLog.event);
     }
@@ -67,6 +69,37 @@ export function _convertToExchangeFillEvent(eventLog: LogWithDecodedArgs<Exchang
     return exchangeFillEvent;
 }
 
+export function _convertToExchangeCancelEvent(
+    eventLog: LogWithDecodedArgs<ExchangeCancelEventArgs>,
+): ExchangeCancelEvent {
+    const makerAssetData = assetDataUtils.decodeAssetDataOrThrow(eventLog.args.makerAssetData);
+    const makerAssetType = makerAssetData.assetProxyId === AssetProxyId.ERC20 ? 'erc20' : 'erc721';
+    const takerAssetData = assetDataUtils.decodeAssetDataOrThrow(eventLog.args.takerAssetData);
+    const takerAssetType = takerAssetData.assetProxyId === AssetProxyId.ERC20 ? 'erc20' : 'erc721';
+    const exchangeCancelEvent = new ExchangeCancelEvent();
+    exchangeCancelEvent.logIndex = eventLog.logIndex as number;
+    exchangeCancelEvent.address = eventLog.address as string;
+    exchangeCancelEvent.rawData = eventLog.data as string;
+    exchangeCancelEvent.blockNumber = eventLog.blockNumber as number;
+    exchangeCancelEvent.makerAddress = eventLog.args.makerAddress.toString();
+    exchangeCancelEvent.takerAddress =
+        eventLog.args.takerAddress == null ? null : eventLog.args.takerAddress.toString();
+    exchangeCancelEvent.feeRecepientAddress = eventLog.args.feeRecipientAddress;
+    exchangeCancelEvent.senderAddress = eventLog.args.senderAddress;
+    exchangeCancelEvent.orderHash = eventLog.args.orderHash;
+    exchangeCancelEvent.rawMakerAssetData = eventLog.args.makerAssetData;
+    exchangeCancelEvent.makerAssetType = makerAssetType;
+    exchangeCancelEvent.makerAssetProxyId = makerAssetData.assetProxyId;
+    exchangeCancelEvent.makerTokenAddress = makerAssetData.tokenAddress;
+    exchangeCancelEvent.makerTokenId = bigNumbertoStringOrNull((makerAssetData as ERC721AssetData).tokenId);
+    exchangeCancelEvent.rawTakerAssetData = eventLog.args.takerAssetData;
+    exchangeCancelEvent.takerAssetType = takerAssetType;
+    exchangeCancelEvent.takerAssetProxyId = takerAssetData.assetProxyId;
+    exchangeCancelEvent.takerTokenAddress = takerAssetData.tokenAddress;
+    exchangeCancelEvent.takerTokenId = bigNumbertoStringOrNull((takerAssetData as ERC721AssetData).tokenId);
+    return exchangeCancelEvent;
+}
+
 function bigNumbertoStringOrNull(n: BigNumber): string | null {
     if (n == null) {
         return null;
diff --git a/packages/pipeline/src/entities/ExchangeCancelEvent.ts b/packages/pipeline/src/entities/ExchangeCancelEvent.ts
new file mode 100644
index 000000000..8e21518d3
--- /dev/null
+++ b/packages/pipeline/src/entities/ExchangeCancelEvent.ts
@@ -0,0 +1,32 @@
+import { BaseEntity, Column, Entity, PrimaryColumn } from 'typeorm';
+
+import { AssetType } from '../types';
+
+@Entity()
+export class ExchangeCancelEvent extends BaseEntity {
+    @PrimaryColumn() public logIndex!: number;
+
+    @Column() public address!: string;
+    @Column() public rawData!: string;
+    @Column() public blockNumber!: number;
+
+    @Column() public makerAddress!: string;
+    @Column({ nullable: true, type: String })
+    public takerAddress!: string;
+    @Column() public feeRecepientAddress!: string;
+    @Column() public senderAddress!: string;
+    @Column() public orderHash!: string;
+    @Column() public rawMakerAssetData!: string;
+    @Column() public makerAssetType!: AssetType;
+    @Column() public makerAssetProxyId!: string;
+    @Column() public makerTokenAddress!: string;
+    @Column({ nullable: true, type: String })
+    public makerTokenId!: string | null;
+    @Column() public rawTakerAssetData!: string;
+    @Column() public takerAssetType!: AssetType;
+    @Column() public takerAssetProxyId!: string;
+    @Column() public takerTokenAddress!: string;
+    @Column({ nullable: true, type: String })
+    public takerTokenId!: string | null;
+    // TODO(albrow): Include topics?
+}
diff --git a/packages/pipeline/src/entities/ExchangeFillEvent.ts b/packages/pipeline/src/entities/ExchangeFillEvent.ts
index a7e817240..e66bd64e3 100644
--- a/packages/pipeline/src/entities/ExchangeFillEvent.ts
+++ b/packages/pipeline/src/entities/ExchangeFillEvent.ts
@@ -1,6 +1,6 @@
 import { BaseEntity, Column, Entity, PrimaryColumn } from 'typeorm';
 
-export type ExchangeFillEventAssetType = 'erc20' | 'erc721';
+import { AssetType } from '../types';
 
 @Entity()
 export class ExchangeFillEvent extends BaseEntity {
@@ -20,13 +20,13 @@ export class ExchangeFillEvent extends BaseEntity {
     @Column() public takerFeePaid!: string;
     @Column() public orderHash!: string;
     @Column() public rawMakerAssetData!: string;
-    @Column() public makerAssetType!: ExchangeFillEventAssetType;
+    @Column() public makerAssetType!: AssetType;
     @Column() public makerAssetProxyId!: string;
     @Column() public makerTokenAddress!: string;
     @Column({ nullable: true, type: String })
     public makerTokenId!: string | null;
     @Column() public rawTakerAssetData!: string;
-    @Column() public takerAssetType!: ExchangeFillEventAssetType;
+    @Column() public takerAssetType!: AssetType;
     @Column() public takerAssetProxyId!: string;
     @Column() public takerTokenAddress!: string;
     @Column({ nullable: true, type: String })
diff --git a/packages/pipeline/src/index.ts b/packages/pipeline/src/index.ts
index c01ff57b4..c68df95bf 100644
--- a/packages/pipeline/src/index.ts
+++ b/packages/pipeline/src/index.ts
@@ -2,6 +2,7 @@ import 'reflect-metadata';
 import { createConnection } from 'typeorm';
 
 import { Etherscan } from './data_sources/etherscan';
+import { ExchangeCancelEvent } from './entities/ExchangeCancelEvent';
 import { ExchangeFillEvent } from './entities/ExchangeFillEvent';
 import { config } from './ormconfig';
 
@@ -12,12 +13,16 @@ const EXCHANGE_ADDRESS = '0x4f833a24e1f95d70f028921e27040ca56e09ab0b';
 
 (async () => {
     const connection = await createConnection(config);
-    const repository = connection.getRepository(ExchangeFillEvent);
-    console.log(`found ${await repository.count()} existing fill events`);
+    const fillRepository = connection.getRepository(ExchangeFillEvent);
+    const cancelRepository = connection.getRepository(ExchangeCancelEvent);
+    console.log(`found ${await fillRepository.count()} existing fill events`);
+    console.log(`found ${await cancelRepository.count()} existing cancel events`);
     const rawEvents = await etherscan.getContractEventsAsync(EXCHANGE_ADDRESS);
     const events = parseExchangeEvents(rawEvents);
+    console.log(`got ${events.length} parsed events`);
     for (const event of events) {
         await event.save();
     }
-    console.log(`now ${await repository.count()} total fill events`);
+    console.log(`now ${await fillRepository.count()} total fill events`);
+    console.log(`now ${await cancelRepository.count()} total cancel events`);
 })();
-- 
cgit v1.2.3