diff --git a/packages/utils/CHANGELOG.json b/packages/utils/CHANGELOG.json index 501498f118..fce2f78e86 100644 --- a/packages/utils/CHANGELOG.json +++ b/packages/utils/CHANGELOG.json @@ -5,6 +5,10 @@ { "note": "Removed exports AuthorizableRevertErrors, LibAddressArrayRevertErrors, LibBytesRevertErrors, OwnableRevertErrors, ReentrancyGuardRevertErrors and SafeMathRevertErrors", "pr": 2321 + }, + { + "note": "Decode `Parity` revert errors", + "pr": 2341 } ] }, diff --git a/packages/utils/src/revert_error.ts b/packages/utils/src/revert_error.ts index 515950850c..329614f83c 100644 --- a/packages/utils/src/revert_error.ts +++ b/packages/utils/src/revert_error.ts @@ -314,6 +314,7 @@ export abstract class RevertError extends Error { } } +const PARITY_TRANSACTION_REVERT_ERROR_MESSAGE = /^VM execution error/; const GANACHE_TRANSACTION_REVERT_ERROR_MESSAGE = /^VM Exception while processing transaction: revert/; const GETH_TRANSACTION_REVERT_ERROR_MESSAGE = /always failing transaction$/; @@ -329,10 +330,18 @@ interface GanacheTransactionRevertError extends Error { hashes: string[]; } +interface ParityTransactionRevertError extends Error { + code: number; + data: string; + message: string; +} + /** * Try to extract the ecnoded revert error bytes from a thrown `Error`. */ -export function getThrownErrorRevertErrorBytes(error: Error | GanacheTransactionRevertError): string { +export function getThrownErrorRevertErrorBytes( + error: Error | GanacheTransactionRevertError | ParityTransactionRevertError, +): string { // Handle ganache transaction reverts. if (isGanacheTransactionRevertError(error)) { // Grab the first result attached. @@ -344,6 +353,13 @@ export function getThrownErrorRevertErrorBytes(error: Error | GanacheTransaction if (result.return !== undefined && result.return !== '0x') { return result.return; } + } else if (isParityTransactionRevertError(error)) { + // Parity returns { data: 'Reverted 0xa6bcde47...', ... } + const { data } = error; + const hexDataIndex = data.indexOf('0x'); + if (hexDataIndex !== -1) { + return data.slice(hexDataIndex); + } } else { // Handle geth transaction reverts. if (isGethTransactionRevertError(error)) { @@ -354,6 +370,15 @@ export function getThrownErrorRevertErrorBytes(error: Error | GanacheTransaction throw new Error(`Cannot decode thrown Error "${error.message}" as a RevertError`); } +function isParityTransactionRevertError( + error: Error | ParityTransactionRevertError, +): error is ParityTransactionRevertError { + if (PARITY_TRANSACTION_REVERT_ERROR_MESSAGE.test(error.message) && 'code' in error && 'data' in error) { + return true; + } + return false; +} + function isGanacheTransactionRevertError( error: Error | GanacheTransactionRevertError, ): error is GanacheTransactionRevertError { diff --git a/packages/utils/test/revert_error_test.ts b/packages/utils/test/revert_error_test.ts index efb86453de..2a2f47e37b 100644 --- a/packages/utils/test/revert_error_test.ts +++ b/packages/utils/test/revert_error_test.ts @@ -1,7 +1,13 @@ import * as chai from 'chai'; import * as _ from 'lodash'; -import { AnyRevertError, RawRevertError, RevertError, StringRevertError } from '../src/revert_error'; +import { + AnyRevertError, + getThrownErrorRevertErrorBytes, + RawRevertError, + RevertError, + StringRevertError, +} from '../src/revert_error'; import { chaiSetup } from './utils/chai_setup'; @@ -154,6 +160,14 @@ describe('RevertError', () => { expect(decode).to.throw(); }); }); + describe('getThrownErrorRevertErrorBytes', () => { + it('should decode Parity revert errors', () => { + const revertAbi = '0x1234'; + const parityError = { code: 1234, message: 'VM execution error.', data: `Reverted ${revertAbi}`, name: '' }; + const revertError = getThrownErrorRevertErrorBytes(parityError); + expect(revertError).to.be.eq(revertAbi); + }); + }); describe('encoding', () => { const message = 'foobar'; it('should be able to encode', () => {