@0x/utils: Fix nested revert decoding

This commit is contained in:
Lawrence Forman 2020-09-01 16:04:01 -04:00
parent 228246089e
commit f9d02c9e27

View File

@ -7,7 +7,7 @@ import { inspect } from 'util';
import * as AbiEncoder from './abi_encoder'; import * as AbiEncoder from './abi_encoder';
import { BigNumber } from './configured_bignumber'; import { BigNumber } from './configured_bignumber';
// tslint:disable: max-classes-per-file // tslint:disable: max-classes-per-file no-unnecessary-type-assertion
type ArgTypes = type ArgTypes =
| string | string
@ -109,7 +109,10 @@ export abstract class RevertError extends Error {
* @param coerce Whether to coerce unknown selectors into a `RawRevertError` type. * @param coerce Whether to coerce unknown selectors into a `RawRevertError` type.
* @return A RevertError object. * @return A RevertError object.
*/ */
public static decode(bytes: string | Buffer, coerce: boolean = false): RevertError { public static decode(bytes: string | Buffer | RevertError, coerce: boolean = false): RevertError {
if (bytes instanceof RevertError) {
return bytes;
}
const _bytes = bytes instanceof Buffer ? ethUtil.bufferToHex(bytes) : ethUtil.addHexPrefix(bytes); const _bytes = bytes instanceof Buffer ? ethUtil.bufferToHex(bytes) : ethUtil.addHexPrefix(bytes);
// tslint:disable-next-line: custom-no-magic-numbers // tslint:disable-next-line: custom-no-magic-numbers
const selector = _bytes.slice(2, 10); const selector = _bytes.slice(2, 10);
@ -122,21 +125,7 @@ export abstract class RevertError extends Error {
const { type, decoder } = RevertError._typeRegistry[selector]; const { type, decoder } = RevertError._typeRegistry[selector];
const instance = new type(); const instance = new type();
try { try {
const values = decoder(_bytes); Object.assign(instance, { values: decoder(_bytes) });
_.transform(
values,
(result, value, key) => {
const { type: argType } = instance._getArgumentByName(key);
if (argType === 'bytes') {
try {
const nestedRevert = RevertError.decode(value as string, coerce);
result[key] = nestedRevert.toString();
} catch (err) {} // tslint:disable-line:no-empty
}
},
values,
);
_.assign(instance, { values });
instance.message = instance.toString(); instance.message = instance.toString();
return instance; return instance;
} catch (err) { } catch (err) {
@ -306,6 +295,16 @@ export abstract class RevertError extends Error {
return `${this.constructor.name}(${this._raw})`; return `${this.constructor.name}(${this._raw})`;
} }
const values = _.omitBy(this.values, (v: any) => _.isNil(v)); const values = _.omitBy(this.values, (v: any) => _.isNil(v));
// tslint:disable-next-line: forin
for (const k in values) {
const { type: argType } = this._getArgumentByName(k);
if (argType === 'bytes') {
// Try to decode nested revert errors.
try {
values[k] = RevertError.decode(values[k] as any);
} catch (err) {} // tslint:disable-line:no-empty
}
}
const inner = _.isEmpty(values) ? '' : inspect(values); const inner = _.isEmpty(values) ? '' : inspect(values);
return `${this.constructor.name}(${inner})`; return `${this.constructor.name}(${inner})`;
} }
@ -498,6 +497,12 @@ function declarationToAbi(declaration: string): RevertErrorAbi {
} }
function checkArgEquality(type: string, lhs: ArgTypes, rhs: ArgTypes): boolean { function checkArgEquality(type: string, lhs: ArgTypes, rhs: ArgTypes): boolean {
// Try to compare as decoded revert errors first.
try {
return RevertError.decode(lhs as any).equals(RevertError.decode(rhs as any));
} catch (err) {
// no-op
}
if (type === 'address') { if (type === 'address') {
return normalizeAddress(lhs as string) === normalizeAddress(rhs as string); return normalizeAddress(lhs as string) === normalizeAddress(rhs as string);
} else if (type === 'bytes' || /^bytes(\d+)$/.test(type)) { } else if (type === 'bytes' || /^bytes(\d+)$/.test(type)) {