diff --git a/packages/abi/src.ts/interface.ts b/packages/abi/src.ts/interface.ts index 5307819116..c2ff373813 100644 --- a/packages/abi/src.ts/interface.ts +++ b/packages/abi/src.ts/interface.ts @@ -34,6 +34,14 @@ export class TransactionDescription extends Description readonly value: BigNumber; } +export class ErrorDescription extends Description { + readonly errorFragment: ErrorFragment; + readonly name: string; + readonly args: Result; + readonly signature: string; + readonly sighash: string; +} + export class Indexed extends Description { readonly hash: string; readonly _isIndexed: boolean; @@ -283,12 +291,20 @@ export class Interface { } // Get the sighash (the bytes4 selector) used by Solidity to identify a function - getSighash(functionFragment: FunctionFragment | string): string { - if (typeof(functionFragment) === "string") { - functionFragment = this.getFunction(functionFragment); + getSighash(fragment: ErrorFragment | FunctionFragment | string): string { + if (typeof(fragment) === "string") { + try { + fragment = this.getFunction(fragment); + } catch (error) { + try { + fragment = this.getError(fragment); + } catch (_) { + throw error; + } + } } - return getStatic<(f: FunctionFragment) => string>(this.constructor, "getSighash")(functionFragment); + return getStatic<(f: ErrorFragment | FunctionFragment) => string>(this.constructor, "getSighash")(fragment); } // Get the topic (the bytes32 hash) used by Solidity to identify an event @@ -313,6 +329,31 @@ export class Interface { return this._encodeParams(this.deploy.inputs, values || [ ]); } + decodeErrorData(fragment: ErrorFragment | string, data: BytesLike): Result { + if (typeof(fragment) === "string") { + fragment = this.getError(fragment); + } + + const bytes = arrayify(data); + + if (hexlify(bytes.slice(0, 4)) !== this.getSighash(fragment)) { + logger.throwArgumentError(`data signature does not match error ${ fragment.name }.`, "data", hexlify(bytes)); + } + + return this._decodeParams(fragment.inputs, bytes.slice(4)); + } + + encodeErrorData(fragment: ErrorFragment | string, values?: ReadonlyArray): string { + if (typeof(fragment) === "string") { + fragment = this.getError(fragment); + } + + return hexlify(concat([ + this.getSighash(fragment), + this._encodeParams(fragment.inputs, values || [ ]) + ])); + } + // Decode the data for a function call (e.g. tx.data) decodeFunctionData(functionFragment: FunctionFragment | string, data: BytesLike): Result { if (typeof(functionFragment) === "string") { @@ -627,6 +668,21 @@ export class Interface { }); } + parseError(data: BytesLike): ErrorDescription { + const hexData = hexlify(data); + let fragment = this.getError(hexData.substring(0, 10).toLowerCase()) + + if (!fragment) { return null; } + + return new ErrorDescription({ + args: this._abiCoder.decode(fragment.inputs, "0x" + hexData.substring(10)), + errorFragment: fragment, + name: fragment.name, + signature: fragment.format(), + sighash: this.getSighash(fragment), + }); + } + /* static from(value: Array | string | Interface) {