Added initial support for recoverable coding erros (#800).
This commit is contained in:
parent
14e6811bf7
commit
bda6623091
@ -12,6 +12,29 @@ export interface Result extends ReadonlyArray<any> {
|
|||||||
readonly [key: string]: any;
|
readonly [key: string]: any;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function checkResultErrors(result: Result): Array<{ path: Array<string | number>, error: Error }> {
|
||||||
|
// Find the first error (if any)
|
||||||
|
const errors: Array<{ path: Array<string | number>, error: Error }> = [ ];
|
||||||
|
|
||||||
|
const checkErrors = function(path: Array<string | number>, object: any): void {
|
||||||
|
if (!Array.isArray(object)) { return; }
|
||||||
|
for (let key in object) {
|
||||||
|
const childPath = path.slice();
|
||||||
|
childPath.push(key);
|
||||||
|
|
||||||
|
try {
|
||||||
|
checkErrors(childPath, object[key]);
|
||||||
|
} catch (error) {
|
||||||
|
errors.push({ path: childPath, error: error });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
checkErrors([ ], result);
|
||||||
|
|
||||||
|
return errors;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
export type CoerceFunc = (type: string, value: any) => any;
|
export type CoerceFunc = (type: string, value: any) => any;
|
||||||
|
|
||||||
export abstract class Coder {
|
export abstract class Coder {
|
||||||
@ -34,6 +57,7 @@ export abstract class Coder {
|
|||||||
readonly dynamic: boolean;
|
readonly dynamic: boolean;
|
||||||
|
|
||||||
constructor(name: string, type: string, localName: string, dynamic: boolean) {
|
constructor(name: string, type: string, localName: string, dynamic: boolean) {
|
||||||
|
// @TODO: defineReadOnly these
|
||||||
this.name = name;
|
this.name = name;
|
||||||
this.type = type;
|
this.type = type;
|
||||||
this.localName = localName;
|
this.localName = localName;
|
||||||
|
@ -75,10 +75,29 @@ export function unpack(reader: Reader, coders: Array<Coder>): Array<any> {
|
|||||||
if (coder.dynamic) {
|
if (coder.dynamic) {
|
||||||
let offset = reader.readValue();
|
let offset = reader.readValue();
|
||||||
let offsetReader = baseReader.subReader(offset.toNumber());
|
let offsetReader = baseReader.subReader(offset.toNumber());
|
||||||
value = coder.decode(offsetReader);
|
try {
|
||||||
|
value = coder.decode(offsetReader);
|
||||||
|
} catch (error) {
|
||||||
|
// Cannot recover from this
|
||||||
|
if (error.code === Logger.errors.BUFFER_OVERRUN) { throw error; }
|
||||||
|
value = error;
|
||||||
|
value.baseType = coder.name;
|
||||||
|
value.name = coder.localName;
|
||||||
|
value.type = coder.type;
|
||||||
|
}
|
||||||
dynamicLength += offsetReader.consumed;
|
dynamicLength += offsetReader.consumed;
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
value = coder.decode(reader);
|
try {
|
||||||
|
value = coder.decode(reader);
|
||||||
|
} catch (error) {
|
||||||
|
// Cannot recover from this
|
||||||
|
if (error.code === Logger.errors.BUFFER_OVERRUN) { throw error; }
|
||||||
|
value = error;
|
||||||
|
value.baseType = coder.name;
|
||||||
|
value.name = coder.localName;
|
||||||
|
value.type = coder.type;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (value != undefined) {
|
if (value != undefined) {
|
||||||
@ -99,9 +118,26 @@ export function unpack(reader: Reader, coders: Array<Coder>): Array<any> {
|
|||||||
|
|
||||||
if (values[name] != null) { return; }
|
if (values[name] != null) { return; }
|
||||||
|
|
||||||
values[name] = values[index];
|
const value = values[index];
|
||||||
|
|
||||||
|
if (value instanceof Error) {
|
||||||
|
Object.defineProperty(values, name, {
|
||||||
|
get: () => { throw value; }
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
values[name] = value;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
for (let i = 0; i < values.length; i++) {
|
||||||
|
const value = values[i];
|
||||||
|
if (value instanceof Error) {
|
||||||
|
Object.defineProperty(values, i, {
|
||||||
|
get: () => { throw value; }
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return Object.freeze(values);
|
return Object.freeze(values);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -126,7 +162,6 @@ export class ArrayCoder extends Coder {
|
|||||||
|
|
||||||
let count = this.length;
|
let count = this.length;
|
||||||
|
|
||||||
//let result = new Uint8Array(0);
|
|
||||||
if (count === -1) {
|
if (count === -1) {
|
||||||
count = value.length;
|
count = value.length;
|
||||||
writer.writeValue(value.length);
|
writer.writeValue(value.length);
|
||||||
|
@ -8,12 +8,12 @@ export class TupleCoder extends Coder {
|
|||||||
|
|
||||||
constructor(coders: Array<Coder>, localName: string) {
|
constructor(coders: Array<Coder>, localName: string) {
|
||||||
let dynamic = false;
|
let dynamic = false;
|
||||||
let types: Array<string> = [];
|
const types: Array<string> = [];
|
||||||
coders.forEach((coder) => {
|
coders.forEach((coder) => {
|
||||||
if (coder.dynamic) { dynamic = true; }
|
if (coder.dynamic) { dynamic = true; }
|
||||||
types.push(coder.type);
|
types.push(coder.type);
|
||||||
});
|
});
|
||||||
let type = ("tuple(" + types.join(",") + ")");
|
const type = ("tuple(" + types.join(",") + ")");
|
||||||
|
|
||||||
super("tuple", type, localName, dynamic);
|
super("tuple", type, localName, dynamic);
|
||||||
this.coders = coders;
|
this.coders = coders;
|
||||||
|
@ -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, LogDescription, Result, TransactionDescription } from "./interface";
|
import { checkResultErrors, Indexed, Interface, LogDescription, Result, TransactionDescription } from "./interface";
|
||||||
|
|
||||||
export {
|
export {
|
||||||
ConstructorFragment,
|
ConstructorFragment,
|
||||||
@ -26,6 +26,7 @@ export {
|
|||||||
JsonFragmentType,
|
JsonFragmentType,
|
||||||
|
|
||||||
Result,
|
Result,
|
||||||
|
checkResultErrors,
|
||||||
|
|
||||||
LogDescription,
|
LogDescription,
|
||||||
TransactionDescription
|
TransactionDescription
|
||||||
|
@ -8,14 +8,14 @@ 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 { Result } from "./coders/abstract-coder";
|
import { checkResultErrors, Result } from "./coders/abstract-coder";
|
||||||
import { ConstructorFragment, EventFragment, FormatTypes, Fragment, FunctionFragment, JsonFragment, ParamType } from "./fragments";
|
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 { Result };
|
export { checkResultErrors, Result };
|
||||||
|
|
||||||
export class LogDescription extends Description<LogDescription> {
|
export class LogDescription extends Description<LogDescription> {
|
||||||
readonly eventFragment: EventFragment;
|
readonly eventFragment: EventFragment;
|
||||||
@ -43,6 +43,24 @@ export class Indexed extends Description<Indexed> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function wrapAccessError(property: string, error: Error): Error {
|
||||||
|
const wrap = new Error(`deferred error during ABI decoding triggered accessing ${ property }`);
|
||||||
|
(<any>wrap).error = error;
|
||||||
|
return wrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
function checkNames(fragment: Fragment, type: "input" | "output", params: Array<ParamType>): void {
|
||||||
|
params.reduce((accum, param) => {
|
||||||
|
if (param.name) {
|
||||||
|
if (accum[param.name]) {
|
||||||
|
logger.throwArgumentError(`duplicate ${ type } parameter ${ JSON.stringify(param.name) } in ${ fragment.format("full") }`, "fragment", fragment);
|
||||||
|
}
|
||||||
|
accum[param.name] = true;
|
||||||
|
}
|
||||||
|
return accum;
|
||||||
|
}, <{ [ name: string ]: boolean }>{ });
|
||||||
|
}
|
||||||
|
|
||||||
export class Interface {
|
export class Interface {
|
||||||
readonly fragments: Array<Fragment>;
|
readonly fragments: Array<Fragment>;
|
||||||
|
|
||||||
@ -87,12 +105,16 @@ export class Interface {
|
|||||||
logger.warn("duplicate definition - constructor");
|
logger.warn("duplicate definition - constructor");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
checkNames(fragment, "input", fragment.inputs);
|
||||||
defineReadOnly(this, "deploy", <ConstructorFragment>fragment);
|
defineReadOnly(this, "deploy", <ConstructorFragment>fragment);
|
||||||
return;
|
return;
|
||||||
case "function":
|
case "function":
|
||||||
|
checkNames(fragment, "input", fragment.inputs);
|
||||||
|
checkNames(fragment, "output", (<FunctionFragment>fragment).outputs);
|
||||||
bucket = this.functions;
|
bucket = this.functions;
|
||||||
break;
|
break;
|
||||||
case "event":
|
case "event":
|
||||||
|
checkNames(fragment, "input", fragment.inputs);
|
||||||
bucket = this.events;
|
bucket = this.events;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
@ -367,6 +389,49 @@ export class Interface {
|
|||||||
return topics;
|
return topics;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
encodeEventLog(eventFragment: EventFragment, values: Array<any>): { data: string, topics: Array<string> } {
|
||||||
|
if (typeof(eventFragment) === "string") {
|
||||||
|
eventFragment = this.getEvent(eventFragment);
|
||||||
|
}
|
||||||
|
|
||||||
|
const topics: Array<string> = [ ];
|
||||||
|
|
||||||
|
const dataTypes: Array<ParamType> = [ ];
|
||||||
|
const dataValues: Array<string> = [ ];
|
||||||
|
|
||||||
|
if (!eventFragment.anonymous) {
|
||||||
|
topics.push(this.getEventTopic(eventFragment));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (values.length !== eventFragment.inputs.length) {
|
||||||
|
logger.throwArgumentError("event arguments/values mismatch", "values", values);
|
||||||
|
}
|
||||||
|
|
||||||
|
eventFragment.inputs.forEach((param, index) => {
|
||||||
|
const value = values[index];
|
||||||
|
if (param.indexed) {
|
||||||
|
if (param.type === "string") {
|
||||||
|
topics.push(id(value))
|
||||||
|
} else if (param.type === "bytes") {
|
||||||
|
topics.push(keccak256(value))
|
||||||
|
} else if (param.baseType === "tuple" || param.baseType === "array") {
|
||||||
|
// @TOOD
|
||||||
|
throw new Error("not implemented");
|
||||||
|
} else {
|
||||||
|
topics.push(this._abiCoder.encode([ param.type] , [ value ]));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
dataTypes.push(param);
|
||||||
|
dataValues.push(value);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
data: this._abiCoder.encode(dataTypes , dataValues),
|
||||||
|
topics: topics
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
// Decode a filter for the event and the search criteria
|
// Decode a filter for the event and the search criteria
|
||||||
decodeEventLog(eventFragment: EventFragment | string, data: BytesLike, topics?: Array<string>): Result {
|
decodeEventLog(eventFragment: EventFragment | string, data: BytesLike, topics?: Array<string>): Result {
|
||||||
if (typeof(eventFragment) === "string") {
|
if (typeof(eventFragment) === "string") {
|
||||||
@ -414,15 +479,45 @@ export class Interface {
|
|||||||
result[index] = new Indexed({ _isIndexed: true, hash: resultIndexed[indexedIndex++] });
|
result[index] = new Indexed({ _isIndexed: true, hash: resultIndexed[indexedIndex++] });
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
result[index] = resultIndexed[indexedIndex++];
|
try {
|
||||||
|
result[index] = resultIndexed[indexedIndex++];
|
||||||
|
} catch (error) {
|
||||||
|
result[index] = error;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
result[index] = resultNonIndexed[nonIndexedIndex++];
|
try {
|
||||||
|
result[index] = resultNonIndexed[nonIndexedIndex++];
|
||||||
|
} catch (error) {
|
||||||
|
result[index] = error;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (param.name && result[param.name] == null) { result[param.name] = result[index]; }
|
// Add the keyword argument if named and safe
|
||||||
|
if (param.name && result[param.name] == null) {
|
||||||
|
const value = result[index];
|
||||||
|
|
||||||
|
// Make error named values throw on access
|
||||||
|
if (value instanceof Error) {
|
||||||
|
Object.defineProperty(result, param.name, {
|
||||||
|
get: () => { throw wrapAccessError(`property ${ JSON.stringify(param.name) }`, value); }
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
result[param.name] = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Make all error indexed values throw on access
|
||||||
|
for (let i = 0; i < result.length; i++) {
|
||||||
|
const value = result[i];
|
||||||
|
if (value instanceof Error) {
|
||||||
|
Object.defineProperty(result, i, {
|
||||||
|
get: () => { throw wrapAccessError(`index ${ i }`, value); }
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return Object.freeze(result);
|
return Object.freeze(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
import { EventFragment, Fragment, Indexed, Interface, JsonFragment, LogDescription, ParamType, Result } from "@ethersproject/abi";
|
import { checkResultErrors, EventFragment, Fragment, Indexed, Interface, JsonFragment, LogDescription, ParamType, Result } from "@ethersproject/abi";
|
||||||
import { Block, BlockTag, Filter, FilterByBlockHash, Listener, Log, Provider, TransactionReceipt, TransactionRequest, TransactionResponse } from "@ethersproject/abstract-provider";
|
import { Block, BlockTag, Filter, FilterByBlockHash, 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";
|
||||||
@ -80,6 +80,7 @@ const allowedTransactionKeys: { [ key: string ]: boolean } = {
|
|||||||
chainId: true, data: true, from: true, gasLimit: true, gasPrice:true, nonce: true, to: true, value: true
|
chainId: true, data: true, from: true, gasLimit: true, gasPrice:true, nonce: true, to: true, value: true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Recursively replaces ENS names with promises to resolve the name and resolves all properties
|
// Recursively replaces ENS names with promises to resolve the name and resolves all properties
|
||||||
function resolveAddresses(signerOrProvider: Signer | Provider, value: any, paramType: ParamType | Array<ParamType>): Promise<any> {
|
function resolveAddresses(signerOrProvider: Signer | Provider, value: any, paramType: ParamType | Array<ParamType>): Promise<any> {
|
||||||
if (Array.isArray(paramType)) {
|
if (Array.isArray(paramType)) {
|
||||||
@ -147,7 +148,7 @@ function runMethod(contract: Contract, functionName: string, options: RunOptions
|
|||||||
// Check for unexpected keys (e.g. using "gas" instead of "gasLimit")
|
// Check for unexpected keys (e.g. using "gas" instead of "gasLimit")
|
||||||
for (let key in tx) {
|
for (let key in tx) {
|
||||||
if (!allowedTransactionKeys[key]) {
|
if (!allowedTransactionKeys[key]) {
|
||||||
logger.throwError(("unknown transaction override - " + key), "overrides", tx);
|
logger.throwArgumentError(("unknown transaction override - " + key), "overrides", tx);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -362,6 +363,7 @@ class ErrorRunningEvent extends RunningEvent {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// @TODO Fragment should inherit Wildcard? and just override getEmit?
|
// @TODO Fragment should inherit Wildcard? and just override getEmit?
|
||||||
// or have a common abstract super class, with enough constructor
|
// or have a common abstract super class, with enough constructor
|
||||||
// options to configure both.
|
// options to configure both.
|
||||||
@ -408,11 +410,13 @@ class FragmentRunningEvent extends RunningEvent {
|
|||||||
} catch (error) {
|
} catch (error) {
|
||||||
event.args = null;
|
event.args = null;
|
||||||
event.decodeError = error;
|
event.decodeError = error;
|
||||||
throw error;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
getEmit(event: Event): Array<any> {
|
getEmit(event: Event): Array<any> {
|
||||||
|
const errors = checkResultErrors(event.args);
|
||||||
|
if (errors.length) { throw errors[0].error; }
|
||||||
|
|
||||||
const args = (event.args || []).slice();
|
const args = (event.args || []).slice();
|
||||||
args.push(event);
|
args.push(event);
|
||||||
return args;
|
return args;
|
||||||
@ -713,6 +717,11 @@ export class Contract {
|
|||||||
return this._normalizeRunningEvent(new ErrorRunningEvent());
|
return this._normalizeRunningEvent(new ErrorRunningEvent());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Listen for any event that is registered
|
||||||
|
if (eventName === "event") {
|
||||||
|
return this._normalizeRunningEvent(new RunningEvent("event", null));
|
||||||
|
}
|
||||||
|
|
||||||
// Listen for any event
|
// Listen for any event
|
||||||
if (eventName === "*") {
|
if (eventName === "*") {
|
||||||
return this._normalizeRunningEvent(new WildcardRunningEvent(this.address, this.interface));
|
return this._normalizeRunningEvent(new WildcardRunningEvent(this.address, this.interface));
|
||||||
@ -791,16 +800,27 @@ export class Contract {
|
|||||||
// If we are not polling the provider, start polling
|
// If we are not polling the provider, start polling
|
||||||
if (!this._wrappedEmits[runningEvent.tag]) {
|
if (!this._wrappedEmits[runningEvent.tag]) {
|
||||||
const wrappedEmit = (log: Log) => {
|
const wrappedEmit = (log: Log) => {
|
||||||
let event = null;
|
let event = this._wrapEvent(runningEvent, log, listener);
|
||||||
try {
|
|
||||||
event = this._wrapEvent(runningEvent, log, listener);
|
// Try to emit the result for the parameterized event...
|
||||||
} catch (error) {
|
if (event.decodeError == null) {
|
||||||
// There was an error decoding the data and topics
|
try {
|
||||||
this.emit("error", error, event);
|
const args = runningEvent.getEmit(event);
|
||||||
return;
|
this.emit(runningEvent.filter, ...args);
|
||||||
|
} catch (error) {
|
||||||
|
event.decodeError = error.error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Always emit "event" for fragment-base events
|
||||||
|
if (runningEvent.filter != null) {
|
||||||
|
this.emit("event", event);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Emit "error" if there was an error
|
||||||
|
if (event.decodeError != null) {
|
||||||
|
this.emit("error", event.decodeError, event);
|
||||||
}
|
}
|
||||||
const args = runningEvent.getEmit(event);
|
|
||||||
this.emit(runningEvent.filter, ...args);
|
|
||||||
};
|
};
|
||||||
this._wrappedEmits[runningEvent.tag] = wrappedEmit;
|
this._wrappedEmits[runningEvent.tag] = wrappedEmit;
|
||||||
|
|
||||||
|
@ -16,9 +16,7 @@ import { Wordlist, wordlists} from "@ethersproject/wordlists";
|
|||||||
|
|
||||||
import * as utils from "./utils";
|
import * as utils from "./utils";
|
||||||
|
|
||||||
import { Logger } from "@ethersproject/logger";
|
import { ErrorCode as errors, Logger } from "@ethersproject/logger";
|
||||||
|
|
||||||
const errors: { [ name: string ]: string } = Logger.errors;
|
|
||||||
|
|
||||||
////////////////////////
|
////////////////////////
|
||||||
// Types
|
// Types
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
import { AbiCoder, defaultAbiCoder, EventFragment, FormatTypes, Fragment, FunctionFragment, Indexed, Interface, ParamType } from "@ethersproject/abi";
|
import { AbiCoder, checkResultErrors, defaultAbiCoder, EventFragment, FormatTypes, Fragment, FunctionFragment, Indexed, Interface, ParamType, Result } from "@ethersproject/abi";
|
||||||
import { getAddress, getCreate2Address, getContractAddress, getIcapAddress, isAddress } from "@ethersproject/address";
|
import { getAddress, getCreate2Address, getContractAddress, getIcapAddress, isAddress } from "@ethersproject/address";
|
||||||
import * as base64 from "@ethersproject/base64";
|
import * as base64 from "@ethersproject/base64";
|
||||||
import { arrayify, concat, hexDataSlice, hexDataLength, hexlify, hexStripZeros, hexValue, hexZeroPad, isBytes, isBytesLike, isHexString, joinSignature, zeroPad, splitSignature, stripZeros } from "@ethersproject/bytes";
|
import { arrayify, concat, hexDataSlice, hexDataLength, hexlify, hexStripZeros, hexValue, hexZeroPad, isBytes, isBytesLike, isHexString, joinSignature, zeroPad, splitSignature, stripZeros } from "@ethersproject/bytes";
|
||||||
@ -51,6 +51,9 @@ export {
|
|||||||
ParamType,
|
ParamType,
|
||||||
FormatTypes,
|
FormatTypes,
|
||||||
|
|
||||||
|
checkResultErrors,
|
||||||
|
Result,
|
||||||
|
|
||||||
Logger,
|
Logger,
|
||||||
|
|
||||||
RLP,
|
RLP,
|
||||||
|
Loading…
Reference in New Issue
Block a user