aboutsummaryrefslogtreecommitdiffstats
path: root/packages/pipeline/src/data_types/events
diff options
context:
space:
mode:
Diffstat (limited to 'packages/pipeline/src/data_types/events')
-rw-r--r--packages/pipeline/src/data_types/events/event_handlers/base_event_handler.ts34
-rw-r--r--packages/pipeline/src/data_types/events/event_handlers/exchange_event_handler.ts76
-rw-r--r--packages/pipeline/src/data_types/events/event_utils.ts35
3 files changed, 145 insertions, 0 deletions
diff --git a/packages/pipeline/src/data_types/events/event_handlers/base_event_handler.ts b/packages/pipeline/src/data_types/events/event_handlers/base_event_handler.ts
new file mode 100644
index 000000000..59331fc4f
--- /dev/null
+++ b/packages/pipeline/src/data_types/events/event_handlers/base_event_handler.ts
@@ -0,0 +1,34 @@
+import { AbiDefinition, BlockParam, BlockParamLiteral, LogEntry } from 'ethereum-types';
+import * as R from 'ramda';
+import { BaseEntity } from 'typeorm';
+
+import { Etherscan } from '../../../data_sources/etherscan';
+import { convertResponseToLogEntry } from '../event_utils';
+
+export abstract class BaseEventHandler<EntityType extends BaseEntity> {
+ protected _abi: AbiDefinition[];
+ protected _address: string;
+ protected _etherscan: Etherscan;
+ constructor(abi: AbiDefinition[], address: string, etherscan: Etherscan) {
+ this._abi = abi;
+ this._address = address;
+ this._etherscan = etherscan;
+ }
+ public abstract convertLogEntryToEventEntity(logEntry: LogEntry): EntityType;
+
+ public async getEventsAsync(
+ fromBlock: BlockParam = BlockParamLiteral.Earliest,
+ toBlock: BlockParam = BlockParamLiteral.Latest,
+ ): Promise<EntityType[]> {
+ const rawEventsResponse = await this._etherscan.getContractEventsAsync(this._address, fromBlock, toBlock);
+ const logEntries = R.map(convertResponseToLogEntry, rawEventsResponse.result);
+ // Note(albrow): Imperative for loop is required here because we can't
+ // bind convertLogEntryToEventEntity without having a specific instance
+ // of a sub-class.
+ const result = [];
+ for (const logEntry of logEntries) {
+ result.push(this.convertLogEntryToEventEntity(logEntry));
+ }
+ return result;
+ }
+}
diff --git a/packages/pipeline/src/data_types/events/event_handlers/exchange_event_handler.ts b/packages/pipeline/src/data_types/events/event_handlers/exchange_event_handler.ts
new file mode 100644
index 000000000..38ff20595
--- /dev/null
+++ b/packages/pipeline/src/data_types/events/event_handlers/exchange_event_handler.ts
@@ -0,0 +1,76 @@
+import { ExchangeEventArgs, ExchangeFillEventArgs } from '@0xproject/contract-wrappers';
+import { assetDataUtils } from '@0xproject/order-utils';
+import { AssetProxyId, ERC721AssetData } from '@0xproject/types';
+import { AbiDecoder, BigNumber } from '@0xproject/utils';
+import { AbiDefinition, LogEntry, LogWithDecodedArgs } from 'ethereum-types';
+import * as R from 'ramda';
+
+import { ExchangeFillEvent } from '../../../entities/ExchangeFillEvent';
+import { decodeLogEntry } from '../event_utils';
+
+import { BaseEventHandler } from './base_event_handler';
+
+// TODO(albrow): Union with other exchange event entity types
+export type ExchangeEventEntity = ExchangeFillEvent;
+
+export class ExchangeEventHandler extends BaseEventHandler<ExchangeEventEntity> {
+ public convertLogEntryToEventEntity(logEntry: LogEntry): ExchangeEventEntity {
+ const decodedLogEntry = decodeLogEntry<ExchangeEventArgs>(this._abi, logEntry);
+ return _convertToEntity(decodedLogEntry);
+ }
+}
+
+export function _convertToEntity(eventLog: LogWithDecodedArgs<ExchangeEventArgs>): ExchangeEventEntity {
+ switch (eventLog.event) {
+ case 'Fill':
+ return _convertToExchangeFillEvent(eventLog as LogWithDecodedArgs<ExchangeFillEventArgs>);
+ default:
+ return new ExchangeFillEvent();
+ // throw new Error('unexpected eventLog.event type: ' + eventLog.event);
+ }
+}
+
+export function _convertToExchangeFillEvent(eventLog: LogWithDecodedArgs<ExchangeFillEventArgs>): ExchangeFillEvent {
+ 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 exchangeFillEvent = new ExchangeFillEvent();
+ exchangeFillEvent.logIndex = eventLog.logIndex as number;
+ exchangeFillEvent.address = eventLog.address as string;
+ exchangeFillEvent.rawData = eventLog.data as string;
+ exchangeFillEvent.blockNumber = eventLog.blockNumber as number;
+ exchangeFillEvent.makerAddress = eventLog.args.makerAddress.toString();
+ exchangeFillEvent.takerAddress = eventLog.args.takerAddress.toString();
+ exchangeFillEvent.feeRecepientAddress = eventLog.args.feeRecipientAddress;
+ exchangeFillEvent.senderAddress = eventLog.args.senderAddress;
+ exchangeFillEvent.makerAssetFilledAmount = eventLog.args.makerAssetFilledAmount.toString();
+ exchangeFillEvent.takerAssetFilledAmount = eventLog.args.takerAssetFilledAmount.toString();
+ exchangeFillEvent.makerFeePaid = eventLog.args.makerFeePaid.toString();
+ exchangeFillEvent.takerFeePaid = eventLog.args.takerFeePaid.toString();
+ exchangeFillEvent.orderHash = eventLog.args.orderHash;
+ exchangeFillEvent.rawMakerAssetData = eventLog.args.makerAssetData;
+ exchangeFillEvent.makerAssetType = makerAssetType;
+ exchangeFillEvent.makerAssetProxyId = makerAssetData.assetProxyId;
+ exchangeFillEvent.makerTokenAddress = makerAssetData.tokenAddress;
+ exchangeFillEvent.makerTokenId = bigNumbertoStringOrNull((makerAssetData as ERC721AssetData).tokenId);
+ exchangeFillEvent.rawTakerAssetData = eventLog.args.takerAssetData;
+ exchangeFillEvent.takerAssetType = takerAssetType;
+ exchangeFillEvent.takerAssetProxyId = takerAssetData.assetProxyId;
+ exchangeFillEvent.takerTokenAddress = takerAssetData.tokenAddress;
+ exchangeFillEvent.takerTokenId = bigNumbertoStringOrNull((takerAssetData as ERC721AssetData).tokenId);
+ return exchangeFillEvent;
+}
+
+function bigNumbertoStringOrNull(n: BigNumber): string | null {
+ if (n == null) {
+ return null;
+ }
+ return n.toString();
+}
+
+function filterEventLogs(
+ eventLogs: Array<LogWithDecodedArgs<ExchangeEventArgs>>,
+): Array<LogWithDecodedArgs<ExchangeEventArgs>> {
+ return R.filter(eventLog => eventLog.event === 'Fill', eventLogs);
+}
diff --git a/packages/pipeline/src/data_types/events/event_utils.ts b/packages/pipeline/src/data_types/events/event_utils.ts
new file mode 100644
index 000000000..6be964807
--- /dev/null
+++ b/packages/pipeline/src/data_types/events/event_utils.ts
@@ -0,0 +1,35 @@
+import { AbiDecoder } from '@0xproject/utils';
+import { AbiDefinition, LogEntry, LogWithDecodedArgs } from 'ethereum-types';
+
+import { EventsResponseResult } from '../../data_sources/etherscan';
+
+const hexRadix = 16;
+
+function hexToInt(hex: string): number {
+ return parseInt(hex.replace('0x', ''), hexRadix);
+}
+
+// Converts a raw event response to a LogEntry
+export function convertResponseToLogEntry(result: EventsResponseResult): LogEntry {
+ return {
+ logIndex: hexToInt(result.logIndex),
+ transactionIndex: hexToInt(result.transactionIndex),
+ transactionHash: result.transactionHash,
+ blockHash: '',
+ blockNumber: hexToInt(result.blockNumber),
+ address: result.address,
+ data: result.data,
+ topics: result.topics,
+ };
+}
+
+// Decodes a LogEntry into a LogWithDecodedArgs
+export function decodeLogEntry<EventArgsType>(
+ contractAbi: AbiDefinition[],
+ log: LogEntry,
+): LogWithDecodedArgs<EventArgsType> {
+ const abiDecoder = new AbiDecoder([contractAbi]);
+ const logWithDecodedArgs = abiDecoder.tryToDecodeLogOrNoop<EventArgsType>(log);
+ // tslint:disable-next-line:no-unnecessary-type-assertion
+ return logWithDecodedArgs as LogWithDecodedArgs<EventArgsType>;
+}