aboutsummaryrefslogtreecommitdiffstats
path: root/packages/contract-wrappers/src/utils/filter_utils.ts
diff options
context:
space:
mode:
Diffstat (limited to 'packages/contract-wrappers/src/utils/filter_utils.ts')
-rw-r--r--packages/contract-wrappers/src/utils/filter_utils.ts95
1 files changed, 95 insertions, 0 deletions
diff --git a/packages/contract-wrappers/src/utils/filter_utils.ts b/packages/contract-wrappers/src/utils/filter_utils.ts
new file mode 100644
index 000000000..c5df7321e
--- /dev/null
+++ b/packages/contract-wrappers/src/utils/filter_utils.ts
@@ -0,0 +1,95 @@
+import {
+ ConstructorAbi,
+ ContractAbi,
+ EventAbi,
+ FallbackAbi,
+ FilterObject,
+ LogEntry,
+ MethodAbi,
+} from '@0xproject/types';
+import * as ethUtil from 'ethereumjs-util';
+import * as jsSHA3 from 'js-sha3';
+import * as _ from 'lodash';
+import * as uuid from 'uuid/v4';
+
+import { BlockRange, ContractEvents, IndexedFilterValues } from '../types';
+
+const TOPIC_LENGTH = 32;
+
+export const filterUtils = {
+ generateUUID(): string {
+ return uuid();
+ },
+ getFilter(
+ address: string,
+ eventName: ContractEvents,
+ indexFilterValues: IndexedFilterValues,
+ abi: ContractAbi,
+ blockRange?: BlockRange,
+ ): FilterObject {
+ const eventAbi = _.find(abi, { name: eventName }) as EventAbi;
+ const eventSignature = filterUtils.getEventSignatureFromAbiByName(eventAbi, eventName);
+ const topicForEventSignature = ethUtil.addHexPrefix(jsSHA3.keccak256(eventSignature));
+ const topicsForIndexedArgs = filterUtils.getTopicsForIndexedArgs(eventAbi, indexFilterValues);
+ const topics = [topicForEventSignature, ...topicsForIndexedArgs];
+ let filter: FilterObject = {
+ address,
+ topics,
+ };
+ if (!_.isUndefined(blockRange)) {
+ filter = {
+ ...blockRange,
+ ...filter,
+ };
+ }
+ return filter;
+ },
+ getEventSignatureFromAbiByName(eventAbi: EventAbi, eventName: ContractEvents): string {
+ const types = _.map(eventAbi.inputs, 'type');
+ const signature = `${eventAbi.name}(${types.join(',')})`;
+ return signature;
+ },
+ getTopicsForIndexedArgs(abi: EventAbi, indexFilterValues: IndexedFilterValues): Array<string | null> {
+ const topics: Array<string | null> = [];
+ for (const eventInput of abi.inputs) {
+ if (!eventInput.indexed) {
+ continue;
+ }
+ if (_.isUndefined(indexFilterValues[eventInput.name])) {
+ // Null is a wildcard topic in a JSON-RPC call
+ topics.push(null);
+ } else {
+ const value = indexFilterValues[eventInput.name] as string;
+ const buffer = ethUtil.toBuffer(value);
+ const paddedBuffer = ethUtil.setLengthLeft(buffer, TOPIC_LENGTH);
+ const topic = ethUtil.bufferToHex(paddedBuffer);
+ topics.push(topic);
+ }
+ }
+ return topics;
+ },
+ matchesFilter(log: LogEntry, filter: FilterObject): boolean {
+ if (!_.isUndefined(filter.address) && log.address !== filter.address) {
+ return false;
+ }
+ if (!_.isUndefined(filter.topics)) {
+ return filterUtils.matchesTopics(log.topics, filter.topics);
+ }
+ return true;
+ },
+ matchesTopics(logTopics: string[], filterTopics: Array<string[] | string | null>): boolean {
+ const matchesTopic = _.zipWith(logTopics, filterTopics, filterUtils.matchesTopic.bind(filterUtils));
+ const matchesTopics = _.every(matchesTopic);
+ return matchesTopics;
+ },
+ matchesTopic(logTopic: string, filterTopic: string[] | string | null): boolean {
+ if (_.isArray(filterTopic)) {
+ return _.includes(filterTopic, logTopic);
+ }
+ if (_.isString(filterTopic)) {
+ return filterTopic === logTopic;
+ }
+ // null topic is a wildcard
+ return true;
+ },
+};