@ -57,7 +57,7 @@ export function callFunction (from, to, data, value, gasLimit, funAbi, txRunner,
* @param { Object } execResult - execution result given by the VM
* @param { Object } execResult - execution result given by the VM
* @return { Object } - { error : true / false , message : DOMNode }
* @return { Object } - { error : true / false , message : DOMNode }
* /
* /
export function checkVMError ( execResult , abi , contract ) {
export function checkVMError ( execResult , compiledContracts ) {
const errorCode = {
const errorCode = {
OUT_OF_GAS : 'out of gas' ,
OUT_OF_GAS : 'out of gas' ,
STACK_UNDERFLOW : 'stack underflow' ,
STACK_UNDERFLOW : 'stack underflow' ,
@ -91,55 +91,60 @@ export function checkVMError (execResult, abi, contract) {
const returnData = execResult . returnValue
const returnData = execResult . returnValue
const returnDataHex = returnData . slice ( 0 , 4 ) . toString ( 'hex' )
const returnDataHex = returnData . slice ( 0 , 4 ) . toString ( 'hex' )
let customError
let customError
if ( abi ) {
if ( compiledContracts ) {
let decodedCustomErrorInputsClean
let decodedCustomErrorInputsClean
for ( const item of abi ) {
for ( const file of Object . keys ( compiledContracts ) ) {
if ( item . type === 'error' ) {
for ( const contractName of Object . keys ( compiledContracts [ file ] ) ) {
// ethers doesn't crash anymore if "error" type is specified, but it doesn't extract the errors. see:
const contract = compiledContracts [ file ] [ contractName ]
// https://github.com/ethers-io/ethers.js/commit/bd05aed070ac9e1421a3e2bff2ceea150bedf9b7
for ( const item of contract . abi ) {
// we need here to fake the type, so the "getSighash" function works properly
if ( item . type === 'error' ) {
const fn = getFunctionFragment ( { . . . item , type : 'function' , stateMutability : 'nonpayable' } )
// ethers doesn't crash anymore if "error" type is specified, but it doesn't extract the errors. see:
if ( ! fn ) continue
// https://github.com/ethers-io/ethers.js/commit/bd05aed070ac9e1421a3e2bff2ceea150bedf9b7
const sign = fn . getSighash ( item . name )
// we need here to fake the type, so the "getSighash" function works properly
if ( ! sign ) continue
const fn = getFunctionFragment ( { . . . item , type : 'function' , stateMutability : 'nonpayable' } )
if ( returnDataHex === sign . replace ( '0x' , '' ) ) {
if ( ! fn ) continue
customError = item . name
const sign = fn . getSighash ( item . name )
const functionDesc = fn . getFunction ( item . name )
if ( ! sign ) continue
// decoding error parameters
if ( returnDataHex === sign . replace ( '0x' , '' ) ) {
const decodedCustomErrorInputs = fn . decodeFunctionData ( functionDesc , returnData )
customError = item . name
decodedCustomErrorInputsClean = { }
const functionDesc = fn . getFunction ( item . name )
let devdoc = { }
// decoding error parameters
// "contract" reprensents the compilation result containing the NATSPEC documentation
const decodedCustomErrorInputs = fn . decodeFunctionData ( functionDesc , returnData )
if ( contract && fn . functions && Object . keys ( fn . functions ) . length ) {
decodedCustomErrorInputsClean = { }
const functionSignature = Object . keys ( fn . functions ) [ 0 ]
let devdoc = { }
// we check in the 'devdoc' if there's a developer documentation for this error
// "contract" reprensents the compilation result containing the NATSPEC documentation
try {
if ( contract && fn . functions && Object . keys ( fn . functions ) . length ) {
devdoc = ( contract . object . devdoc . errors && contract . object . devdoc . errors [ functionSignature ] [ 0 ] ) || { }
const functionSignature = Object . keys ( fn . functions ) [ 0 ]
} catch ( e ) {
// we check in the 'devdoc' if there's a developer documentation for this error
console . error ( e . message )
try {
}
devdoc = ( contract . devdoc . errors && contract . devdoc . errors [ functionSignature ] [ 0 ] ) || { }
// we check in the 'userdoc' if there's an user documentation for this error
} catch ( e ) {
try {
console . error ( e . message )
const userdoc = ( contract . object . userdoc . errors && contract . object . userdoc . errors [ functionSignature ] [ 0 ] ) || { }
}
if ( userdoc && ( userdoc as any ) . notice ) customError += ' : ' + ( userdoc as any ) . notice // we append the user doc if any
// we check in the 'userdoc' if there's an user documentation for this error
} catch ( e ) {
try {
console . error ( e . message )
const userdoc = ( contract . userdoc . errors && contract . userdoc . errors [ functionSignature ] [ 0 ] ) || { }
}
if ( userdoc && ( userdoc as any ) . notice ) customError += ' : ' + ( userdoc as any ) . notice // we append the user doc if any
}
} catch ( e ) {
let inputIndex = 0
console . error ( e . message )
for ( const input of functionDesc . inputs ) {
}
const inputKey = input . name || inputIndex
}
const v = decodedCustomErrorInputs [ inputKey ]
let inputIndex = 0
for ( const input of functionDesc . inputs ) {
const inputKey = input . name || inputIndex
const v = decodedCustomErrorInputs [ inputKey ]
decodedCustomErrorInputsClean [ inputKey ] = {
decodedCustomErrorInputsClean [ inputKey ] = {
value : v.toString ? v . toString ( ) : v
value : v.toString ? v . toString ( ) : v
}
}
if ( devdoc && ( devdoc as any ) . params ) {
if ( devdoc && ( devdoc as any ) . params ) {
decodedCustomErrorInputsClean [ input . name ] . documentation = ( devdoc as any ) . params [ inputKey ] // we add the developer documentation for this input parameter if any
decodedCustomErrorInputsClean [ input . name ] . documentation = ( devdoc as any ) . params [ inputKey ] // we add the developer documentation for this input parameter if any
}
inputIndex ++
}
break
}
}
inputIndex ++
}
}
break
}
}
}
}
}
}