Skip to content

Commit 720bde7

Browse files
committed
Added error utilities to Interface.
1 parent 319987e commit 720bde7

File tree

1 file changed

+60
-4
lines changed

1 file changed

+60
-4
lines changed

packages/abi/src.ts/interface.ts

Lines changed: 60 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,14 @@ export class TransactionDescription extends Description<TransactionDescription>
3434
readonly value: BigNumber;
3535
}
3636

37+
export class ErrorDescription extends Description<ErrorDescription> {
38+
readonly errorFragment: ErrorFragment;
39+
readonly name: string;
40+
readonly args: Result;
41+
readonly signature: string;
42+
readonly sighash: string;
43+
}
44+
3745
export class Indexed extends Description<Indexed> {
3846
readonly hash: string;
3947
readonly _isIndexed: boolean;
@@ -283,12 +291,20 @@ export class Interface {
283291
}
284292

285293
// Get the sighash (the bytes4 selector) used by Solidity to identify a function
286-
getSighash(functionFragment: FunctionFragment | string): string {
287-
if (typeof(functionFragment) === "string") {
288-
functionFragment = this.getFunction(functionFragment);
294+
getSighash(fragment: ErrorFragment | FunctionFragment | string): string {
295+
if (typeof(fragment) === "string") {
296+
try {
297+
fragment = this.getFunction(fragment);
298+
} catch (error) {
299+
try {
300+
fragment = this.getError(<string>fragment);
301+
} catch (_) {
302+
throw error;
303+
}
304+
}
289305
}
290306

291-
return getStatic<(f: FunctionFragment) => string>(this.constructor, "getSighash")(functionFragment);
307+
return getStatic<(f: ErrorFragment | FunctionFragment) => string>(this.constructor, "getSighash")(fragment);
292308
}
293309

294310
// Get the topic (the bytes32 hash) used by Solidity to identify an event
@@ -313,6 +329,31 @@ export class Interface {
313329
return this._encodeParams(this.deploy.inputs, values || [ ]);
314330
}
315331

332+
decodeErrorData(fragment: ErrorFragment | string, data: BytesLike): Result {
333+
if (typeof(fragment) === "string") {
334+
fragment = this.getError(fragment);
335+
}
336+
337+
const bytes = arrayify(data);
338+
339+
if (hexlify(bytes.slice(0, 4)) !== this.getSighash(fragment)) {
340+
logger.throwArgumentError(`data signature does not match error ${ fragment.name }.`, "data", hexlify(bytes));
341+
}
342+
343+
return this._decodeParams(fragment.inputs, bytes.slice(4));
344+
}
345+
346+
encodeErrorData(fragment: ErrorFragment | string, values?: ReadonlyArray<any>): string {
347+
if (typeof(fragment) === "string") {
348+
fragment = this.getError(fragment);
349+
}
350+
351+
return hexlify(concat([
352+
this.getSighash(fragment),
353+
this._encodeParams(fragment.inputs, values || [ ])
354+
]));
355+
}
356+
316357
// Decode the data for a function call (e.g. tx.data)
317358
decodeFunctionData(functionFragment: FunctionFragment | string, data: BytesLike): Result {
318359
if (typeof(functionFragment) === "string") {
@@ -627,6 +668,21 @@ export class Interface {
627668
});
628669
}
629670

671+
parseError(data: BytesLike): ErrorDescription {
672+
const hexData = hexlify(data);
673+
let fragment = this.getError(hexData.substring(0, 10).toLowerCase())
674+
675+
if (!fragment) { return null; }
676+
677+
return new ErrorDescription({
678+
args: this._abiCoder.decode(fragment.inputs, "0x" + hexData.substring(10)),
679+
errorFragment: fragment,
680+
name: fragment.name,
681+
signature: fragment.format(),
682+
sighash: this.getSighash(fragment),
683+
});
684+
}
685+
630686

631687
/*
632688
static from(value: Array<Fragment | string | JsonAbi> | string | Interface) {

0 commit comments

Comments
 (0)