From da8ca2e8bc982fc3ea0343bb3c593a485ca1fef0 Mon Sep 17 00:00:00 2001 From: Richard Moore Date: Tue, 19 Nov 2019 18:13:58 +0900 Subject: [PATCH] Moved bare ABI named functions and events from Interface into Contracts to simplify other consumers of Interface. --- packages/abi/src.ts/interface.ts | 57 ++++++++++++++++++++++----- packages/contracts/src.ts/index.ts | 62 ++++++++++++++++++++++++++---- 2 files changed, 103 insertions(+), 16 deletions(-) diff --git a/packages/abi/src.ts/interface.ts b/packages/abi/src.ts/interface.ts index 33d40b8a14..bf4d760a24 100644 --- a/packages/abi/src.ts/interface.ts +++ b/packages/abi/src.ts/interface.ts @@ -111,6 +111,7 @@ export class Interface { }); // Add any fragments with a unique name by its name (sans signature parameters) + /* [this.events, this.functions].forEach((bucket) => { let count = getNameCount(bucket); Object.keys(bucket).forEach((signature) => { @@ -122,6 +123,7 @@ export class Interface { bucket[fragment.name] = fragment; }); }); + */ // If we do not have a constructor use the default "constructor() payable" if (!this.deploy) { @@ -149,32 +151,67 @@ export class Interface { getFunction(nameOrSignatureOrSighash: string): FunctionFragment { if (isHexString(nameOrSignatureOrSighash)) { - return getFragment(nameOrSignatureOrSighash, this.getSighash.bind(this), this.functions); + for (const name in this.functions) { + if (nameOrSignatureOrSighash === this.getSighash(name)) { + return this.functions[name]; + } + } + logger.throwArgumentError("no matching function", "sighash", nameOrSignatureOrSighash); } // It is a bare name, look up the function (will return null if ambiguous) if (nameOrSignatureOrSighash.indexOf("(") === -1) { - return (this.functions[nameOrSignatureOrSighash.trim()] || null); + const name = nameOrSignatureOrSighash.trim(); + const matching = Object.keys(this.functions).filter((f) => (f.split("("/* fix:) */)[0] === name)); + if (matching.length === 0) { + logger.throwArgumentError("no matching function", "name", name); + } else if (matching.length > 1) { + logger.throwArgumentError("multiple matching functions", "name", name); + } + + return this.functions[matching[0]]; } // Normlize the signature and lookup the function - return this.functions[FunctionFragment.fromString(nameOrSignatureOrSighash).format()]; + const result = this.functions[FunctionFragment.fromString(nameOrSignatureOrSighash).format()]; + if (!result) { + logger.throwArgumentError("no matching function", "signature", nameOrSignatureOrSighash); + } + return result; } getEvent(nameOrSignatureOrTopic: string): EventFragment { if (isHexString(nameOrSignatureOrTopic)) { - return getFragment(nameOrSignatureOrTopic, this.getEventTopic.bind(this), this.events); + const topichash = nameOrSignatureOrTopic.toLowerCase(); + for (const name in this.events) { + if (topichash === this.getEventTopic(name)) { + return this.events[name]; + } + } + logger.throwArgumentError("no matching event", "topichash", topichash); } // It is a bare name, look up the function (will return null if ambiguous) if (nameOrSignatureOrTopic.indexOf("(") === -1) { - return this.events[nameOrSignatureOrTopic]; + const name = nameOrSignatureOrTopic.trim(); + const matching = Object.keys(this.events).filter((f) => (f.split("("/* fix:) */)[0] === name)); + if (matching.length === 0) { + logger.throwArgumentError("no matching event", "name", name); + } else if (matching.length > 1) { + logger.throwArgumentError("multiple matching events", "name", name); + } + + return this.events[matching[0]]; } - return this.events[EventFragment.fromString(nameOrSignatureOrTopic).format()]; + // Normlize the signature and lookup the function + const result = this.events[EventFragment.fromString(nameOrSignatureOrTopic).format()]; + if (!result) { + logger.throwArgumentError("no matching event", "signature", nameOrSignatureOrTopic); + } + return result; } - getSighash(functionFragment: FunctionFragment | string): string { if (typeof(functionFragment) === "string") { functionFragment = this.getFunction(functionFragment); @@ -425,7 +462,7 @@ export class Interface { return !!(value && value._isInterface); } } - +/* function getFragment(hash: string, calcFunc: (f: Fragment) => string, items: { [ sig: string ]: Fragment } ) { for (let signature in items) { if (signature.indexOf("(") === -1) { continue; } @@ -434,7 +471,8 @@ function getFragment(hash: string, calcFunc: (f: Fragment) => string, items: { [ } return null; } - +*/ +/* function getNameCount(fragments: { [ signature: string ]: Fragment }): { [ name: string ]: number } { let unique: { [ name: string ]: number } = { }; @@ -447,3 +485,4 @@ function getNameCount(fragments: { [ signature: string ]: Fragment }): { [ name: return unique; } +*/ diff --git a/packages/contracts/src.ts/index.ts b/packages/contracts/src.ts/index.ts index cf46a50e4a..5103b7fdad 100644 --- a/packages/contracts/src.ts/index.ts +++ b/packages/contracts/src.ts/index.ts @@ -113,6 +113,16 @@ type RunOptions = { transaction?: boolean; }; +/* +export function _populateTransaction(func: FunctionFragment, args: Array, overrides?: any): Promise { + return null; +} + +export function _sendTransaction(func: FunctionFragment, args: Array, overrides?: any): Promise { + return null; +} +*/ + function runMethod(contract: Contract, functionName: string, options: RunOptions): RunFunction { let method = contract.interface.functions[functionName]; return function(...params): Promise { @@ -463,15 +473,29 @@ export class Contract { defineReadOnly(this, "filters", { }); - Object.keys(this.interface.events).forEach((eventName) => { - let event = this.interface.events[eventName]; - defineReadOnly(this.filters, eventName, (...args: Array) => { - return { - address: this.address, - topics: this.interface.encodeFilterTopics(event, args) + { + const uniqueFilters: { [ name: string ]: Array } = { }; + Object.keys(this.interface.events).forEach((eventSignature) => { + let event = this.interface.events[eventSignature]; + defineReadOnly(this.filters, eventSignature, (...args: Array) => { + return { + address: this.address, + topics: this.interface.encodeFilterTopics(event, args) + } + }); + if (!uniqueFilters[event.name]) { uniqueFilters[event.name] = [ ]; } + uniqueFilters[event.name].push(eventSignature); + }); + + Object.keys(uniqueFilters).forEach((name) => { + const filters = uniqueFilters[name]; + if (filters.length === 1) { + defineReadOnly(this.filters, name, this.filters[filters[0]]); + } else { + logger.warn(`Duplicate definition of ${ name } (${ filters.join(", ")})`); } }); - }); + } defineReadOnly(this, "_runningEvents", { }); defineReadOnly(this, "_wrappedEmits", { }); @@ -494,7 +518,11 @@ export class Contract { } } + const uniqueFunctions: { [ name: string ]: Array } = { }; Object.keys(this.interface.functions).forEach((name) => { + const fragment = this.interface.functions[name]; + // @TODO: This should take in fragment + let run = runMethod(this, name, { }); if (this[name] == null) { @@ -516,6 +544,26 @@ export class Contract { if (this.estimate[name] == null) { defineReadOnly(this.estimate, name, runMethod(this, name, { estimate: true })); } + + if (!uniqueFunctions[fragment.name]) { uniqueFunctions[fragment.name] = [ ]; } + uniqueFunctions[fragment.name].push(name); + }); + + Object.keys(uniqueFunctions).forEach((name) => { + const signatures = uniqueFunctions[name]; + if (signatures.length > 1) { + logger.warn(`Duplicate definition of ${ name } (${ signatures.join(", ")})`); + return; + } + + if (this[name] == null) { + defineReadOnly(this, name, this[signatures[0]]); + } + + defineReadOnly(this.functions, name, this.functions[signatures[0]]); + defineReadOnly(this.callStatic, name, this.callStatic[signatures[0]]); + defineReadOnly(this.populateTransaction, name, this.populateTransaction[signatures[0]]); + defineReadOnly(this.estimate, name, this.estimate[signatures[0]]); }); }