Better property access on ABI decoded results (#698).
This commit is contained in:
parent
d0f4642f6d
commit
13f50abd84
@ -9,7 +9,7 @@ import { Logger } from "@ethersproject/logger";
|
|||||||
import { version } from "./_version";
|
import { version } from "./_version";
|
||||||
const logger = new Logger(version);
|
const logger = new Logger(version);
|
||||||
|
|
||||||
import { Coder, Reader, Writer } from "./coders/abstract-coder";
|
import { Coder, Reader, Result, Writer } from "./coders/abstract-coder";
|
||||||
import { AddressCoder } from "./coders/address";
|
import { AddressCoder } from "./coders/address";
|
||||||
import { ArrayCoder } from "./coders/array";
|
import { ArrayCoder } from "./coders/array";
|
||||||
import { BooleanCoder } from "./coders/boolean";
|
import { BooleanCoder } from "./coders/boolean";
|
||||||
@ -99,17 +99,17 @@ export class AbiCoder {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
let coders = types.map((type) => this._getCoder(ParamType.from(type)));
|
const coders = types.map((type) => this._getCoder(ParamType.from(type)));
|
||||||
let coder = (new TupleCoder(coders, "_"));
|
const coder = (new TupleCoder(coders, "_"));
|
||||||
|
|
||||||
let writer = this._getWriter();
|
const writer = this._getWriter();
|
||||||
coder.encode(writer, values);
|
coder.encode(writer, values);
|
||||||
return writer.data;
|
return writer.data;
|
||||||
}
|
}
|
||||||
|
|
||||||
decode(types: Array<string | ParamType>, data: BytesLike): any {
|
decode(types: Array<string | ParamType>, data: BytesLike): Result {
|
||||||
let coders: Array<Coder> = types.map((type) => this._getCoder(ParamType.from(type)));
|
const coders: Array<Coder> = types.map((type) => this._getCoder(ParamType.from(type)));
|
||||||
let coder = new TupleCoder(coders, "_");
|
const coder = new TupleCoder(coders, "_");
|
||||||
return coder.decode(this._getReader(arrayify(data)));
|
return coder.decode(this._getReader(arrayify(data)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,6 +8,10 @@ import { Logger } from "@ethersproject/logger";
|
|||||||
import { version } from "../_version";
|
import { version } from "../_version";
|
||||||
const logger = new Logger(version);
|
const logger = new Logger(version);
|
||||||
|
|
||||||
|
export interface Result extends Array<any> {
|
||||||
|
[key: string]: any;
|
||||||
|
}
|
||||||
|
|
||||||
export type CoerceFunc = (type: string, value: any) => any;
|
export type CoerceFunc = (type: string, value: any) => any;
|
||||||
|
|
||||||
export abstract class Coder {
|
export abstract class Coder {
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
import { ConstructorFragment, EventFragment, FormatTypes, Fragment, FunctionFragment, JsonFragment, JsonFragmentType, ParamType } from "./fragments";
|
import { ConstructorFragment, EventFragment, FormatTypes, Fragment, FunctionFragment, JsonFragment, JsonFragmentType, ParamType } from "./fragments";
|
||||||
import { AbiCoder, CoerceFunc, defaultAbiCoder } from "./abi-coder";
|
import { AbiCoder, CoerceFunc, defaultAbiCoder } from "./abi-coder";
|
||||||
import { Indexed, Interface } from "./interface";
|
import { Indexed, Interface, Result } from "./interface";
|
||||||
|
|
||||||
export {
|
export {
|
||||||
ConstructorFragment,
|
ConstructorFragment,
|
||||||
@ -23,5 +23,7 @@ export {
|
|||||||
|
|
||||||
CoerceFunc,
|
CoerceFunc,
|
||||||
JsonFragment,
|
JsonFragment,
|
||||||
JsonFragmentType
|
JsonFragmentType,
|
||||||
|
|
||||||
|
Result
|
||||||
};
|
};
|
||||||
|
@ -8,43 +8,41 @@ import { keccak256 } from "@ethersproject/keccak256"
|
|||||||
import { defineReadOnly, Description, getStatic } from "@ethersproject/properties";
|
import { defineReadOnly, Description, getStatic } from "@ethersproject/properties";
|
||||||
|
|
||||||
import { AbiCoder, defaultAbiCoder } from "./abi-coder";
|
import { AbiCoder, defaultAbiCoder } from "./abi-coder";
|
||||||
import { ConstructorFragment, EventFragment, Fragment, FunctionFragment, JsonFragment, ParamType } from "./fragments";
|
import { Result } from "./coders/abstract-coder";
|
||||||
|
import { ConstructorFragment, EventFragment, FormatTypes, Fragment, FunctionFragment, JsonFragment, ParamType } from "./fragments";
|
||||||
|
|
||||||
import { Logger } from "@ethersproject/logger";
|
import { Logger } from "@ethersproject/logger";
|
||||||
import { version } from "./_version";
|
import { version } from "./_version";
|
||||||
const logger = new Logger(version);
|
const logger = new Logger(version);
|
||||||
|
|
||||||
export class LogDescription extends Description {
|
export { Result };
|
||||||
|
|
||||||
|
export class LogDescription extends Description<LogDescription> {
|
||||||
readonly eventFragment: EventFragment;
|
readonly eventFragment: EventFragment;
|
||||||
readonly name: string;
|
readonly name: string;
|
||||||
readonly signature: string;
|
readonly signature: string;
|
||||||
readonly topic: string;
|
readonly topic: string;
|
||||||
readonly values: any
|
readonly args: Result
|
||||||
}
|
}
|
||||||
|
|
||||||
export class TransactionDescription extends Description {
|
export class TransactionDescription extends Description<TransactionDescription> {
|
||||||
readonly functionFragment: FunctionFragment;
|
readonly functionFragment: FunctionFragment;
|
||||||
readonly name: string;
|
readonly name: string;
|
||||||
readonly args: Array<any>;
|
readonly args: Result;
|
||||||
readonly signature: string;
|
readonly signature: string;
|
||||||
readonly sighash: string;
|
readonly sighash: string;
|
||||||
readonly value: BigNumber;
|
readonly value: BigNumber;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class Indexed extends Description {
|
export class Indexed extends Description<Indexed> {
|
||||||
readonly hash: string;
|
readonly hash: string;
|
||||||
|
readonly _isIndexed: boolean;
|
||||||
|
|
||||||
static isIndexed(value: any): value is Indexed {
|
static isIndexed(value: any): value is Indexed {
|
||||||
return !!(value && value._isIndexed);
|
return !!(value && value._isIndexed);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class Result {
|
|
||||||
[key: string]: any;
|
|
||||||
[key: number]: any;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
export class Interface {
|
export class Interface {
|
||||||
readonly fragments: Array<Fragment>;
|
readonly fragments: Array<Fragment>;
|
||||||
|
|
||||||
@ -110,29 +108,31 @@ export class Interface {
|
|||||||
bucket[signature] = fragment;
|
bucket[signature] = fragment;
|
||||||
});
|
});
|
||||||
|
|
||||||
// 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) => {
|
|
||||||
let fragment = bucket[signature];
|
|
||||||
if (count[fragment.name] !== 1) {
|
|
||||||
logger.warn("duplicate definition - " + fragment.name);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
bucket[fragment.name] = fragment;
|
|
||||||
});
|
|
||||||
});
|
|
||||||
*/
|
|
||||||
|
|
||||||
// If we do not have a constructor use the default "constructor() payable"
|
// If we do not have a constructor use the default "constructor() payable"
|
||||||
if (!this.deploy) {
|
if (!this.deploy) {
|
||||||
defineReadOnly(this, "deploy", ConstructorFragment.from( { type: "constructor" } ));
|
defineReadOnly(this, "deploy", ConstructorFragment.from({ type: "constructor" }));
|
||||||
}
|
}
|
||||||
|
|
||||||
defineReadOnly(this, "_isInterface", true);
|
defineReadOnly(this, "_isInterface", true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
format(format?: string): string | Array<string> {
|
||||||
|
if (!format) { format = FormatTypes.full; }
|
||||||
|
if (format === FormatTypes.sighash) {
|
||||||
|
logger.throwArgumentError("interface does not support formating sighash", "format", format);
|
||||||
|
}
|
||||||
|
|
||||||
|
const abi = this.fragments.map((fragment) => fragment.format(format));
|
||||||
|
|
||||||
|
// We need to re-bundle the JSON fragments a bit
|
||||||
|
if (format === FormatTypes.json) {
|
||||||
|
return JSON.stringify(abi.map((j) => JSON.parse(j)));
|
||||||
|
}
|
||||||
|
|
||||||
|
return abi;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sub-classes can override these to handle other blockchains
|
||||||
static getAbiCoder(): AbiCoder {
|
static getAbiCoder(): AbiCoder {
|
||||||
return defaultAbiCoder;
|
return defaultAbiCoder;
|
||||||
}
|
}
|
||||||
@ -141,14 +141,15 @@ export class Interface {
|
|||||||
return getAddress(address);
|
return getAddress(address);
|
||||||
}
|
}
|
||||||
|
|
||||||
_sighashify(functionFragment: FunctionFragment): string {
|
static getSighash(functionFragment: FunctionFragment): string {
|
||||||
return hexDataSlice(id(functionFragment.format()), 0, 4);
|
return hexDataSlice(id(functionFragment.format()), 0, 4);
|
||||||
}
|
}
|
||||||
|
|
||||||
_topicify(eventFragment: EventFragment): string {
|
static getTopic(eventFragment: EventFragment): string {
|
||||||
return id(eventFragment.format());
|
return id(eventFragment.format());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Find a function definition by any means necessary (unless it is ambiguous)
|
||||||
getFunction(nameOrSignatureOrSighash: string): FunctionFragment {
|
getFunction(nameOrSignatureOrSighash: string): FunctionFragment {
|
||||||
if (isHexString(nameOrSignatureOrSighash)) {
|
if (isHexString(nameOrSignatureOrSighash)) {
|
||||||
for (const name in this.functions) {
|
for (const name in this.functions) {
|
||||||
@ -180,6 +181,7 @@ export class Interface {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Find an event definition by any means necessary (unless it is ambiguous)
|
||||||
getEvent(nameOrSignatureOrTopic: string): EventFragment {
|
getEvent(nameOrSignatureOrTopic: string): EventFragment {
|
||||||
if (isHexString(nameOrSignatureOrTopic)) {
|
if (isHexString(nameOrSignatureOrTopic)) {
|
||||||
const topichash = nameOrSignatureOrTopic.toLowerCase();
|
const topichash = nameOrSignatureOrTopic.toLowerCase();
|
||||||
@ -212,24 +214,26 @@ export class Interface {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Get the sighash (the bytes4 selector) used by Solidity to identify a function
|
||||||
getSighash(functionFragment: FunctionFragment | string): string {
|
getSighash(functionFragment: FunctionFragment | string): string {
|
||||||
if (typeof(functionFragment) === "string") {
|
if (typeof(functionFragment) === "string") {
|
||||||
functionFragment = this.getFunction(functionFragment);
|
functionFragment = this.getFunction(functionFragment);
|
||||||
}
|
}
|
||||||
|
|
||||||
return this._sighashify(functionFragment);
|
return getStatic<(f: FunctionFragment) => string>(this.constructor, "getSighash")(functionFragment);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Get the topic (the bytes32 hash) used by Solidity to identify an event
|
||||||
getEventTopic(eventFragment: EventFragment | string): string {
|
getEventTopic(eventFragment: EventFragment | string): string {
|
||||||
if (typeof(eventFragment) === "string") {
|
if (typeof(eventFragment) === "string") {
|
||||||
eventFragment = this.getEvent(eventFragment);
|
eventFragment = this.getEvent(eventFragment);
|
||||||
}
|
}
|
||||||
|
|
||||||
return this._topicify(eventFragment);
|
return getStatic<(e: EventFragment) => string>(this.constructor, "getTopic")(eventFragment);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
_decodeParams(params: Array<ParamType>, data: BytesLike): Array<any> {
|
_decodeParams(params: Array<ParamType>, data: BytesLike): Result {
|
||||||
return this._abiCoder.decode(params, data)
|
return this._abiCoder.decode(params, data)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -241,7 +245,8 @@ export class Interface {
|
|||||||
return this._encodeParams(this.deploy.inputs, values || [ ]);
|
return this._encodeParams(this.deploy.inputs, values || [ ]);
|
||||||
}
|
}
|
||||||
|
|
||||||
decodeFunctionData(functionFragment: FunctionFragment | string, data: BytesLike): Array<any> {
|
// Decode the data for a function call (e.g. tx.data)
|
||||||
|
decodeFunctionData(functionFragment: FunctionFragment | string, data: BytesLike): Result {
|
||||||
if (typeof(functionFragment) === "string") {
|
if (typeof(functionFragment) === "string") {
|
||||||
functionFragment = this.getFunction(functionFragment);
|
functionFragment = this.getFunction(functionFragment);
|
||||||
}
|
}
|
||||||
@ -255,6 +260,7 @@ export class Interface {
|
|||||||
return this._decodeParams(functionFragment.inputs, bytes.slice(4));
|
return this._decodeParams(functionFragment.inputs, bytes.slice(4));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Encode the data for a function call (e.g. tx.data)
|
||||||
encodeFunctionData(functionFragment: FunctionFragment | string, values?: Array<any>): string {
|
encodeFunctionData(functionFragment: FunctionFragment | string, values?: Array<any>): string {
|
||||||
if (typeof(functionFragment) === "string") {
|
if (typeof(functionFragment) === "string") {
|
||||||
functionFragment = this.getFunction(functionFragment);
|
functionFragment = this.getFunction(functionFragment);
|
||||||
@ -266,7 +272,8 @@ export class Interface {
|
|||||||
]));
|
]));
|
||||||
}
|
}
|
||||||
|
|
||||||
decodeFunctionResult(functionFragment: FunctionFragment | string, data: BytesLike): Array<any> {
|
// Decode the result from a function call (e.g. from eth_call)
|
||||||
|
decodeFunctionResult(functionFragment: FunctionFragment | string, data: BytesLike): Result {
|
||||||
if (typeof(functionFragment) === "string") {
|
if (typeof(functionFragment) === "string") {
|
||||||
functionFragment = this.getFunction(functionFragment);
|
functionFragment = this.getFunction(functionFragment);
|
||||||
}
|
}
|
||||||
@ -285,7 +292,7 @@ export class Interface {
|
|||||||
case 4:
|
case 4:
|
||||||
if (hexlify(bytes.slice(0, 4)) === "0x08c379a0") {
|
if (hexlify(bytes.slice(0, 4)) === "0x08c379a0") {
|
||||||
errorSignature = "Error(string)";
|
errorSignature = "Error(string)";
|
||||||
reason = this._abiCoder.decode([ "string" ], bytes.slice(4));
|
reason = this._abiCoder.decode([ "string" ], bytes.slice(4))[0];
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -298,6 +305,7 @@ export class Interface {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Encode the result for a function call (e.g. for eth_call)
|
||||||
encodeFunctionResult(functionFragment: FunctionFragment | string, values?: Array<any>): string {
|
encodeFunctionResult(functionFragment: FunctionFragment | string, values?: Array<any>): string {
|
||||||
if (typeof(functionFragment) === "string") {
|
if (typeof(functionFragment) === "string") {
|
||||||
functionFragment = this.getFunction(functionFragment);
|
functionFragment = this.getFunction(functionFragment);
|
||||||
@ -306,6 +314,7 @@ export class Interface {
|
|||||||
return hexlify(this._abiCoder.encode(functionFragment.outputs, values || [ ]));
|
return hexlify(this._abiCoder.encode(functionFragment.outputs, values || [ ]));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Create the filter for the event with search criteria (e.g. for eth_filterLog)
|
||||||
encodeFilterTopics(eventFragment: EventFragment, values: Array<any>): Array<string | Array<string>> {
|
encodeFilterTopics(eventFragment: EventFragment, values: Array<any>): Array<string | Array<string>> {
|
||||||
if (typeof(eventFragment) === "string") {
|
if (typeof(eventFragment) === "string") {
|
||||||
eventFragment = this.getEvent(eventFragment);
|
eventFragment = this.getEvent(eventFragment);
|
||||||
@ -355,7 +364,8 @@ export class Interface {
|
|||||||
return topics;
|
return topics;
|
||||||
}
|
}
|
||||||
|
|
||||||
decodeEventLog(eventFragment: EventFragment | string, data: BytesLike, topics?: Array<string>): Array<any> {
|
// Decode a filter for the event and the search criteria
|
||||||
|
decodeEventLog(eventFragment: EventFragment | string, data: BytesLike, topics?: Array<string>): Result {
|
||||||
if (typeof(eventFragment) === "string") {
|
if (typeof(eventFragment) === "string") {
|
||||||
eventFragment = this.getEvent(eventFragment);
|
eventFragment = this.getEvent(eventFragment);
|
||||||
}
|
}
|
||||||
@ -390,7 +400,7 @@ export class Interface {
|
|||||||
let resultIndexed = (topics != null) ? this._abiCoder.decode(indexed, concat(topics)): null;
|
let resultIndexed = (topics != null) ? this._abiCoder.decode(indexed, concat(topics)): null;
|
||||||
let resultNonIndexed = this._abiCoder.decode(nonIndexed, data);
|
let resultNonIndexed = this._abiCoder.decode(nonIndexed, data);
|
||||||
|
|
||||||
let result: Array<any> = [ ];
|
let result: Result = [ ];
|
||||||
let nonIndexedIndex = 0, indexedIndex = 0;
|
let nonIndexedIndex = 0, indexedIndex = 0;
|
||||||
eventFragment.inputs.forEach((param, index) => {
|
eventFragment.inputs.forEach((param, index) => {
|
||||||
if (param.indexed) {
|
if (param.indexed) {
|
||||||
@ -406,13 +416,14 @@ export class Interface {
|
|||||||
} else {
|
} else {
|
||||||
result[index] = resultNonIndexed[nonIndexedIndex++];
|
result[index] = resultNonIndexed[nonIndexedIndex++];
|
||||||
}
|
}
|
||||||
//if (param.name && result[param.name] == null) { result[param.name] = result[index]; }
|
if (param.name && result[param.name] == null) { result[param.name] = result[index]; }
|
||||||
});
|
});
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Given a transaction, find the matching function fragment (if any) and
|
||||||
|
// determine all its properties and call parameters
|
||||||
parseTransaction(tx: { data: string, value?: BigNumberish }): TransactionDescription {
|
parseTransaction(tx: { data: string, value?: BigNumberish }): TransactionDescription {
|
||||||
let fragment = this.getFunction(tx.data.substring(0, 10).toLowerCase())
|
let fragment = this.getFunction(tx.data.substring(0, 10).toLowerCase())
|
||||||
|
|
||||||
@ -428,12 +439,16 @@ export class Interface {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Given an event log, find the matching event fragment (if any) and
|
||||||
|
// determine all its properties and values
|
||||||
parseLog(log: { topics: Array<string>, data: string}): LogDescription {
|
parseLog(log: { topics: Array<string>, data: string}): LogDescription {
|
||||||
let fragment = this.getEvent(log.topics[0]);
|
let fragment = this.getEvent(log.topics[0]);
|
||||||
|
|
||||||
if (!fragment || fragment.anonymous) { return null; }
|
if (!fragment || fragment.anonymous) { return null; }
|
||||||
|
|
||||||
// @TODO: If anonymous, and the only method, and the input count matches, should we parse?
|
// @TODO: If anonymous, and the only method, and the input count matches, should we parse?
|
||||||
|
// Probably not, because just because it is the only event in the ABI does
|
||||||
|
// not mean we have the full ABI; maybe jsut a fragment?
|
||||||
|
|
||||||
|
|
||||||
return new LogDescription({
|
return new LogDescription({
|
||||||
@ -441,7 +456,7 @@ export class Interface {
|
|||||||
name: fragment.name,
|
name: fragment.name,
|
||||||
signature: fragment.format(),
|
signature: fragment.format(),
|
||||||
topic: this.getEventTopic(fragment),
|
topic: this.getEventTopic(fragment),
|
||||||
values: this.decodeEventLog(fragment, log.data, log.topics)
|
args: this.decodeEventLog(fragment, log.data, log.topics)
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -462,27 +477,4 @@ export class Interface {
|
|||||||
return !!(value && value._isInterface);
|
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; }
|
|
||||||
let fragment = items[signature];
|
|
||||||
if (calcFunc(fragment) === hash) { return fragment; }
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
/*
|
|
||||||
function getNameCount(fragments: { [ signature: string ]: Fragment }): { [ name: string ]: number } {
|
|
||||||
let unique: { [ name: string ]: number } = { };
|
|
||||||
|
|
||||||
// Count each name
|
|
||||||
for (let signature in fragments) {
|
|
||||||
let name = fragments[signature].name;
|
|
||||||
if (!unique[name]) { unique[name] = 0; }
|
|
||||||
unique[name]++;
|
|
||||||
}
|
|
||||||
|
|
||||||
return unique;
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
import { EventFragment, Fragment, Indexed, Interface, JsonFragment, ParamType } from "@ethersproject/abi";
|
import { EventFragment, Fragment, Indexed, Interface, JsonFragment, ParamType, Result } from "@ethersproject/abi";
|
||||||
import { Block, BlockTag, Listener, Log, Provider, TransactionReceipt, TransactionRequest, TransactionResponse } from "@ethersproject/abstract-provider";
|
import { Block, BlockTag, Listener, Log, Provider, TransactionReceipt, TransactionRequest, TransactionResponse } from "@ethersproject/abstract-provider";
|
||||||
import { Signer, VoidSigner } from "@ethersproject/abstract-signer";
|
import { Signer, VoidSigner } from "@ethersproject/abstract-signer";
|
||||||
import { getContractAddress } from "@ethersproject/address";
|
import { getContractAddress } from "@ethersproject/address";
|
||||||
@ -48,7 +48,7 @@ export interface Event extends Log {
|
|||||||
eventSignature?: string;
|
eventSignature?: string;
|
||||||
|
|
||||||
// The parsed arguments to the event
|
// The parsed arguments to the event
|
||||||
values?: Array<any>;
|
args?: Result;
|
||||||
|
|
||||||
// A function that can be used to decode event data and topics
|
// A function that can be used to decode event data and topics
|
||||||
decode?: (data: string, topics?: Array<string>) => any;
|
decode?: (data: string, topics?: Array<string>) => any;
|
||||||
@ -124,7 +124,7 @@ export function _sendTransaction(func: FunctionFragment, args: Array<any>, overr
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
function runMethod(contract: Contract, functionName: string, options: RunOptions): RunFunction {
|
function runMethod(contract: Contract, functionName: string, options: RunOptions): RunFunction {
|
||||||
let method = contract.interface.functions[functionName];
|
const method = contract.interface.functions[functionName];
|
||||||
return function(...params): Promise<any> {
|
return function(...params): Promise<any> {
|
||||||
let tx: any = {}
|
let tx: any = {}
|
||||||
|
|
||||||
@ -233,7 +233,7 @@ function runMethod(contract: Contract, functionName: string, options: RunOptions
|
|||||||
}
|
}
|
||||||
|
|
||||||
return contract.signer.sendTransaction(tx).then((tx) => {
|
return contract.signer.sendTransaction(tx).then((tx) => {
|
||||||
let wait = tx.wait.bind(tx);
|
const wait = tx.wait.bind(tx);
|
||||||
|
|
||||||
tx.wait = (confirmations?: number) => {
|
tx.wait = (confirmations?: number) => {
|
||||||
return wait(confirmations).then((receipt: ContractReceipt) => {
|
return wait(confirmations).then((receipt: ContractReceipt) => {
|
||||||
@ -242,7 +242,7 @@ function runMethod(contract: Contract, functionName: string, options: RunOptions
|
|||||||
|
|
||||||
let parsed = contract.interface.parseLog(log);
|
let parsed = contract.interface.parseLog(log);
|
||||||
if (parsed) {
|
if (parsed) {
|
||||||
event.values = parsed.values;
|
event.args = parsed.args;
|
||||||
event.decode = (data: BytesLike, topics?: Array<any>) => {
|
event.decode = (data: BytesLike, topics?: Array<any>) => {
|
||||||
return this.interface.decodeEventLog(parsed.eventFragment, data, topics);
|
return this.interface.decodeEventLog(parsed.eventFragment, data, topics);
|
||||||
};
|
};
|
||||||
@ -322,10 +322,10 @@ class RunningEvent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
run(args: Array<any>): number {
|
run(args: Array<any>): number {
|
||||||
let listenerCount = this.listenerCount();
|
const listenerCount = this.listenerCount();
|
||||||
this._listeners = this._listeners.filter((item) => {
|
this._listeners = this._listeners.filter((item) => {
|
||||||
|
|
||||||
let argsCopy = args.slice();
|
const argsCopy = args.slice();
|
||||||
|
|
||||||
// Call the callback in the next event loop
|
// Call the callback in the next event loop
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
@ -355,7 +355,7 @@ class FragmentRunningEvent extends RunningEvent {
|
|||||||
readonly fragment: EventFragment;
|
readonly fragment: EventFragment;
|
||||||
|
|
||||||
constructor(address: string, contractInterface: Interface, fragment: EventFragment, topics?: Array<string>) {
|
constructor(address: string, contractInterface: Interface, fragment: EventFragment, topics?: Array<string>) {
|
||||||
let filter: EventFilter = {
|
const filter: EventFilter = {
|
||||||
address: address
|
address: address
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -384,7 +384,7 @@ class FragmentRunningEvent extends RunningEvent {
|
|||||||
return this.interface.decodeEventLog(this.fragment, data, topics);
|
return this.interface.decodeEventLog(this.fragment, data, topics);
|
||||||
};
|
};
|
||||||
|
|
||||||
event.values = this.interface.decodeEventLog(this.fragment, event.data, event.topics);
|
event.args = this.interface.decodeEventLog(this.fragment, event.data, event.topics);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -401,7 +401,7 @@ class WildcardRunningEvent extends RunningEvent {
|
|||||||
prepareEvent(event: Event): void {
|
prepareEvent(event: Event): void {
|
||||||
super.prepareEvent(event);
|
super.prepareEvent(event);
|
||||||
|
|
||||||
let parsed = this.interface.parseLog(event);
|
const parsed = this.interface.parseLog(event);
|
||||||
if (parsed) {
|
if (parsed) {
|
||||||
event.event = parsed.name;
|
event.event = parsed.name;
|
||||||
event.eventSignature = parsed.signature;
|
event.eventSignature = parsed.signature;
|
||||||
@ -410,7 +410,7 @@ class WildcardRunningEvent extends RunningEvent {
|
|||||||
return this.interface.decodeEventLog(parsed.eventFragment, data, topics);
|
return this.interface.decodeEventLog(parsed.eventFragment, data, topics);
|
||||||
};
|
};
|
||||||
|
|
||||||
event.values = parsed.values;
|
event.args = parsed.args;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -476,7 +476,7 @@ export class Contract {
|
|||||||
{
|
{
|
||||||
const uniqueFilters: { [ name: string ]: Array<string> } = { };
|
const uniqueFilters: { [ name: string ]: Array<string> } = { };
|
||||||
Object.keys(this.interface.events).forEach((eventSignature) => {
|
Object.keys(this.interface.events).forEach((eventSignature) => {
|
||||||
let event = this.interface.events[eventSignature];
|
const event = this.interface.events[eventSignature];
|
||||||
defineReadOnly(this.filters, eventSignature, (...args: Array<any>) => {
|
defineReadOnly(this.filters, eventSignature, (...args: Array<any>) => {
|
||||||
return {
|
return {
|
||||||
address: this.address,
|
address: this.address,
|
||||||
@ -523,7 +523,7 @@ export class Contract {
|
|||||||
const fragment = this.interface.functions[name];
|
const fragment = this.interface.functions[name];
|
||||||
// @TODO: This should take in fragment
|
// @TODO: This should take in fragment
|
||||||
|
|
||||||
let run = runMethod(this, name, { });
|
const run = runMethod(this, name, { });
|
||||||
|
|
||||||
if (this[name] == null) {
|
if (this[name] == null) {
|
||||||
defineReadOnly(this, name, run);
|
defineReadOnly(this, name, run);
|
||||||
@ -623,7 +623,7 @@ export class Contract {
|
|||||||
logger.throwError("sending a transactions require a signer", Logger.errors.UNSUPPORTED_OPERATION, { operation: "sendTransaction(fallback)" })
|
logger.throwError("sending a transactions require a signer", Logger.errors.UNSUPPORTED_OPERATION, { operation: "sendTransaction(fallback)" })
|
||||||
}
|
}
|
||||||
|
|
||||||
let tx: TransactionRequest = shallowCopy(overrides || {});
|
const tx: TransactionRequest = shallowCopy(overrides || {});
|
||||||
|
|
||||||
["from", "to"].forEach(function(key) {
|
["from", "to"].forEach(function(key) {
|
||||||
if ((<any>tx)[key] == null) { return; }
|
if ((<any>tx)[key] == null) { return; }
|
||||||
@ -642,7 +642,7 @@ export class Contract {
|
|||||||
signerOrProvider = new VoidSigner(signerOrProvider, this.provider);
|
signerOrProvider = new VoidSigner(signerOrProvider, this.provider);
|
||||||
}
|
}
|
||||||
|
|
||||||
let contract = new (<{ new(...args: any[]): Contract }>(this.constructor))(this.address, this.interface, signerOrProvider);
|
const contract = new (<{ new(...args: any[]): Contract }>(this.constructor))(this.address, this.interface, signerOrProvider);
|
||||||
if (this.deployTransaction) {
|
if (this.deployTransaction) {
|
||||||
defineReadOnly(contract, "deployTransaction", this.deployTransaction);
|
defineReadOnly(contract, "deployTransaction", this.deployTransaction);
|
||||||
}
|
}
|
||||||
@ -681,7 +681,7 @@ export class Contract {
|
|||||||
return this._normalizeRunningEvent(new WildcardRunningEvent(this.address, this.interface));
|
return this._normalizeRunningEvent(new WildcardRunningEvent(this.address, this.interface));
|
||||||
}
|
}
|
||||||
|
|
||||||
let fragment = this.interface.getEvent(eventName)
|
const fragment = this.interface.getEvent(eventName)
|
||||||
if (!fragment) {
|
if (!fragment) {
|
||||||
logger.throwArgumentError("unknown event - " + eventName, "eventName", eventName);
|
logger.throwArgumentError("unknown event - " + eventName, "eventName", eventName);
|
||||||
}
|
}
|
||||||
@ -689,7 +689,7 @@ export class Contract {
|
|||||||
return this._normalizeRunningEvent(new FragmentRunningEvent(this.address, this.interface, fragment));
|
return this._normalizeRunningEvent(new FragmentRunningEvent(this.address, this.interface, fragment));
|
||||||
}
|
}
|
||||||
|
|
||||||
let filter: EventFilter = {
|
const filter: EventFilter = {
|
||||||
address: this.address
|
address: this.address
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -697,7 +697,7 @@ export class Contract {
|
|||||||
// since it may be a filter for an otherwise unknown event
|
// since it may be a filter for an otherwise unknown event
|
||||||
if (eventName.topics) {
|
if (eventName.topics) {
|
||||||
if (eventName.topics[0]) {
|
if (eventName.topics[0]) {
|
||||||
let fragment = this.interface.getEvent(eventName.topics[0]);
|
const fragment = this.interface.getEvent(eventName.topics[0]);
|
||||||
if (fragment) {
|
if (fragment) {
|
||||||
return this._normalizeRunningEvent(new FragmentRunningEvent(this.address, this.interface, fragment, eventName.topics));
|
return this._normalizeRunningEvent(new FragmentRunningEvent(this.address, this.interface, fragment, eventName.topics));
|
||||||
}
|
}
|
||||||
@ -715,7 +715,7 @@ export class Contract {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// If we have a poller for this, remove it
|
// If we have a poller for this, remove it
|
||||||
let emit = this._wrappedEmits[runningEvent.tag];
|
const emit = this._wrappedEmits[runningEvent.tag];
|
||||||
if (emit) {
|
if (emit) {
|
||||||
this.provider.off(runningEvent.filter, emit);
|
this.provider.off(runningEvent.filter, emit);
|
||||||
delete this._wrappedEmits[runningEvent.tag];
|
delete this._wrappedEmits[runningEvent.tag];
|
||||||
@ -723,7 +723,7 @@ export class Contract {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private _wrapEvent(runningEvent: RunningEvent, log: Log, listener: Listener): Event {
|
private _wrapEvent(runningEvent: RunningEvent, log: Log, listener: Listener): Event {
|
||||||
let event = <Event>deepCopy(log);
|
const event = <Event>deepCopy(log);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
runningEvent.prepareEvent(event);
|
runningEvent.prepareEvent(event);
|
||||||
@ -757,11 +757,11 @@ export class Contract {
|
|||||||
|
|
||||||
// If we are not polling the provider, start
|
// If we are not polling the provider, start
|
||||||
if (!this._wrappedEmits[runningEvent.tag]) {
|
if (!this._wrappedEmits[runningEvent.tag]) {
|
||||||
let wrappedEmit = (log: Log) => {
|
const wrappedEmit = (log: Log) => {
|
||||||
let event = this._wrapEvent(runningEvent, log, listener);
|
const event = this._wrapEvent(runningEvent, log, listener);
|
||||||
let values = (event.values || []);
|
const args = (event.args || []);
|
||||||
values.push(event);
|
args.push(event);
|
||||||
this.emit(runningEvent.filter, ...values);
|
this.emit(runningEvent.filter, ...args);
|
||||||
};
|
};
|
||||||
this._wrappedEmits[runningEvent.tag] = wrappedEmit;
|
this._wrappedEmits[runningEvent.tag] = wrappedEmit;
|
||||||
|
|
||||||
@ -773,8 +773,8 @@ export class Contract {
|
|||||||
}
|
}
|
||||||
|
|
||||||
queryFilter(event: EventFilter, fromBlockOrBlockhash?: BlockTag | string, toBlock?: BlockTag): Promise<Array<Event>> {
|
queryFilter(event: EventFilter, fromBlockOrBlockhash?: BlockTag | string, toBlock?: BlockTag): Promise<Array<Event>> {
|
||||||
let runningEvent = this._getRunningEvent(event);
|
const runningEvent = this._getRunningEvent(event);
|
||||||
let filter = shallowCopy(runningEvent.filter);
|
const filter = shallowCopy(runningEvent.filter);
|
||||||
|
|
||||||
if (typeof(fromBlockOrBlockhash) === "string" && isHexString(fromBlockOrBlockhash, 32)) {
|
if (typeof(fromBlockOrBlockhash) === "string" && isHexString(fromBlockOrBlockhash, 32)) {
|
||||||
if (toBlock != null) {
|
if (toBlock != null) {
|
||||||
@ -804,8 +804,8 @@ export class Contract {
|
|||||||
emit(eventName: EventFilter | string, ...args: Array<any>): boolean {
|
emit(eventName: EventFilter | string, ...args: Array<any>): boolean {
|
||||||
if (!this.provider) { return false; }
|
if (!this.provider) { return false; }
|
||||||
|
|
||||||
let runningEvent = this._getRunningEvent(eventName);
|
const runningEvent = this._getRunningEvent(eventName);
|
||||||
let result = (runningEvent.run(args) > 0);
|
const result = (runningEvent.run(args) > 0);
|
||||||
|
|
||||||
// May have drained all the "once" events; check for living events
|
// May have drained all the "once" events; check for living events
|
||||||
this._checkRunningEvents(runningEvent);
|
this._checkRunningEvents(runningEvent);
|
||||||
@ -822,7 +822,7 @@ export class Contract {
|
|||||||
if (!this.provider) { return []; }
|
if (!this.provider) { return []; }
|
||||||
|
|
||||||
if (eventName == null) {
|
if (eventName == null) {
|
||||||
let result: Array<Listener> = [ ];
|
const result: Array<Listener> = [ ];
|
||||||
for (let tag in this._runningEvents) {
|
for (let tag in this._runningEvents) {
|
||||||
this._runningEvents[tag].listeners().forEach((listener) => {
|
this._runningEvents[tag].listeners().forEach((listener) => {
|
||||||
result.push(listener)
|
result.push(listener)
|
||||||
@ -838,8 +838,8 @@ export class Contract {
|
|||||||
if (!this.provider) { return this; }
|
if (!this.provider) { return this; }
|
||||||
|
|
||||||
if (eventName == null) {
|
if (eventName == null) {
|
||||||
for (let tag in this._runningEvents) {
|
for (const tag in this._runningEvents) {
|
||||||
let runningEvent = this._runningEvents[tag];
|
const runningEvent = this._runningEvents[tag];
|
||||||
runningEvent.removeAllListeners();
|
runningEvent.removeAllListeners();
|
||||||
this._checkRunningEvents(runningEvent);
|
this._checkRunningEvents(runningEvent);
|
||||||
}
|
}
|
||||||
@ -847,7 +847,7 @@ export class Contract {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Delete any listeners
|
// Delete any listeners
|
||||||
let runningEvent = this._getRunningEvent(eventName);
|
const runningEvent = this._getRunningEvent(eventName);
|
||||||
runningEvent.removeAllListeners();
|
runningEvent.removeAllListeners();
|
||||||
this._checkRunningEvents(runningEvent);
|
this._checkRunningEvents(runningEvent);
|
||||||
|
|
||||||
@ -856,7 +856,7 @@ export class Contract {
|
|||||||
|
|
||||||
off(eventName: EventFilter | string, listener: Listener): this {
|
off(eventName: EventFilter | string, listener: Listener): this {
|
||||||
if (!this.provider) { return this; }
|
if (!this.provider) { return this; }
|
||||||
let runningEvent = this._getRunningEvent(eventName);
|
const runningEvent = this._getRunningEvent(eventName);
|
||||||
runningEvent.removeListener(listener);
|
runningEvent.removeListener(listener);
|
||||||
this._checkRunningEvents(runningEvent);
|
this._checkRunningEvents(runningEvent);
|
||||||
return this;
|
return this;
|
||||||
@ -915,7 +915,7 @@ export class ContractFactory {
|
|||||||
// If we have 1 additional argument, we allow transaction overrides
|
// If we have 1 additional argument, we allow transaction overrides
|
||||||
if (args.length === this.interface.deploy.inputs.length + 1) {
|
if (args.length === this.interface.deploy.inputs.length + 1) {
|
||||||
tx = shallowCopy(args.pop());
|
tx = shallowCopy(args.pop());
|
||||||
for (let key in tx) {
|
for (const key in tx) {
|
||||||
if (!allowedTransactionKeys[key]) {
|
if (!allowedTransactionKeys[key]) {
|
||||||
throw new Error("unknown transaction override " + key);
|
throw new Error("unknown transaction override " + key);
|
||||||
}
|
}
|
||||||
@ -944,12 +944,12 @@ export class ContractFactory {
|
|||||||
return resolveAddresses(this.signer, args, this.interface.deploy.inputs).then((args) => {
|
return resolveAddresses(this.signer, args, this.interface.deploy.inputs).then((args) => {
|
||||||
|
|
||||||
// Get the deployment transaction (with optional overrides)
|
// Get the deployment transaction (with optional overrides)
|
||||||
let tx = this.getDeployTransaction(...args);
|
const tx = this.getDeployTransaction(...args);
|
||||||
|
|
||||||
// Send the deployment transaction
|
// Send the deployment transaction
|
||||||
return this.signer.sendTransaction(tx).then((tx) => {
|
return this.signer.sendTransaction(tx).then((tx) => {
|
||||||
let address = (<any>(this.constructor)).getContractAddress(tx);
|
const address = (<any>(this.constructor)).getContractAddress(tx);
|
||||||
let contract = (<any>(this.constructor)).getContract(address, this.interface, this.signer);
|
const contract = (<any>(this.constructor)).getContract(address, this.interface, this.signer);
|
||||||
defineReadOnly(contract, "deployTransaction", tx);
|
defineReadOnly(contract, "deployTransaction", tx);
|
||||||
return contract;
|
return contract;
|
||||||
});
|
});
|
||||||
@ -973,7 +973,7 @@ export class ContractFactory {
|
|||||||
compilerOutput = JSON.parse(compilerOutput);
|
compilerOutput = JSON.parse(compilerOutput);
|
||||||
}
|
}
|
||||||
|
|
||||||
let abi = compilerOutput.abi;
|
const abi = compilerOutput.abi;
|
||||||
|
|
||||||
let bytecode: any = null;
|
let bytecode: any = null;
|
||||||
if (compilerOutput.bytecode) {
|
if (compilerOutput.bytecode) {
|
||||||
|
Loading…
Reference in New Issue
Block a user