Make event parsing more functional and less class-based

This commit is contained in:
Alex Browne
2018-09-25 14:41:45 -07:00
committed by Fred Carlsen
parent c99b71f354
commit 7d9afce13b
3 changed files with 21 additions and 65 deletions

View File

@@ -1,34 +0,0 @@
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;
}
}

View File

@@ -1,23 +1,29 @@
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 { BigNumber } from '@0xproject/utils';
import { LogWithDecodedArgs } from 'ethereum-types';
import * as R from 'ramda';
import { ExchangeFillEvent } from '../../../entities/ExchangeFillEvent';
import { decodeLogEntry } from '../event_utils';
import { artifacts } from '../../artifacts';
import { EventsResponse } from '../../data_sources/etherscan';
import { ExchangeFillEvent } from '../../entities/ExchangeFillEvent';
import { BaseEventHandler } from './base_event_handler';
import { convertResponseToLogEntry, decodeLogEntry } from './event_utils';
// 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);
}
const exchangeContractAbi = artifacts.Exchange.compilerOutput.abi;
export function parseExchangeEvents(rawEventsResponse: EventsResponse): ExchangeEventEntity[] {
const logEntries = R.map(convertResponseToLogEntry, rawEventsResponse.result);
const decodedLogEntries = R.map(
eventResponse => decodeLogEntry<ExchangeEventArgs>(exchangeContractAbi, eventResponse),
logEntries,
);
const filteredLogEntries = R.filter(logEntry => R.contains(logEntry.event, ['Fill']), decodedLogEntries);
return R.map(_convertToEntity, filteredLogEntries);
}
export function _convertToEntity(eventLog: LogWithDecodedArgs<ExchangeEventArgs>): ExchangeEventEntity {
@@ -25,8 +31,7 @@ export function _convertToEntity(eventLog: LogWithDecodedArgs<ExchangeEventArgs>
case 'Fill':
return _convertToExchangeFillEvent(eventLog as LogWithDecodedArgs<ExchangeFillEventArgs>);
default:
return new ExchangeFillEvent();
// throw new Error('unexpected eventLog.event type: ' + eventLog.event);
throw new Error('unexpected eventLog.event type: ' + eventLog.event);
}
}
@@ -68,9 +73,3 @@ function bigNumbertoStringOrNull(n: BigNumber): string | null {
}
return n.toString();
}
function filterEventLogs(
eventLogs: Array<LogWithDecodedArgs<ExchangeEventArgs>>,
): Array<LogWithDecodedArgs<ExchangeEventArgs>> {
return R.filter(eventLog => eventLog.event === 'Fill', eventLogs);
}

View File

@@ -1,12 +1,11 @@
import 'reflect-metadata';
import { createConnection } from 'typeorm';
import { artifacts } from './artifacts';
import { Etherscan } from './data_sources/etherscan';
import { ExchangeFillEvent } from './entities/ExchangeFillEvent';
import { config } from './ormconfig';
import { ExchangeEventHandler } from './data_types/events/event_handlers/exchange_event_handler';
import { parseExchangeEvents } from './data_types/events/exchange_events';
const etherscan = new Etherscan(process.env.ETHERSCAN_API_KEY as string);
const EXCHANGE_ADDRESS = '0x4f833a24e1f95d70f028921e27040ca56e09ab0b';
@@ -15,18 +14,10 @@ const EXCHANGE_ADDRESS = '0x4f833a24e1f95d70f028921e27040ca56e09ab0b';
const connection = await createConnection(config);
const repository = connection.getRepository(ExchangeFillEvent);
console.log(`found ${await repository.count()} existing fill events`);
const exchangeEventHandler = new ExchangeEventHandler(
artifacts.Exchange.compilerOutput.abi,
EXCHANGE_ADDRESS,
etherscan,
);
const events = await exchangeEventHandler.getEventsAsync();
console.log(JSON.stringify(events, null, 2));
const rawEvents = await etherscan.getContractEventsAsync(EXCHANGE_ADDRESS);
const events = parseExchangeEvents(rawEvents);
for (const event of events) {
// TODO(albrow): remove this check once we can parse all Exchange events
if (event.address != null) {
await event.save();
}
await event.save();
}
console.log(`now ${await repository.count()} total fill events`);
})();