From d1061b4c9617aba709bbda085576eee6d638d3e6 Mon Sep 17 00:00:00 2001 From: yann300 Date: Wed, 17 Nov 2021 14:31:29 +0100 Subject: [PATCH 1/4] fix cumstom error --- .../src/tests/transactionExecution.spec.ts | 47 +++++++- apps/remix-ide/src/blockchain/blockchain.js | 3 +- .../src/lib/compiler-artefacts.ts | 2 +- libs/remix-lib/src/execution/txExecution.ts | 101 +++++++++--------- 4 files changed, 102 insertions(+), 51 deletions(-) diff --git a/apps/remix-ide-e2e/src/tests/transactionExecution.spec.ts b/apps/remix-ide-e2e/src/tests/transactionExecution.spec.ts index 55f2eadacb..6c1e832869 100644 --- a/apps/remix-ide-e2e/src/tests/transactionExecution.spec.ts +++ b/apps/remix-ide-e2e/src/tests/transactionExecution.spec.ts @@ -178,8 +178,29 @@ module.exports = { .journalLastChildIncludes('"documentation": "param2"') .journalLastChildIncludes('"documentation": "param3"') .journalLastChildIncludes('Debug the transaction to get more information.') + }, + + 'Should Compile and Deploy a contract which define a custom error in a library, the error should be logged in the terminal': function (browser: NightwatchBrowser) { + browser.testContracts('customError.sol', sources[5]['customErrorLib.sol'], ['D']) + .clickLaunchIcon('udapp') + .selectAccount('0xCA35b7d915458EF540aDe6068dFe2F44E8fa733c') // this account will be used for this test suite + .click('#runTabView button[class^="instanceButton"]') + .waitForElementPresent('.instance:nth-of-type(3)') + .click('.instance:nth-of-type(3) > div > button') + .clickFunction('h - transact (not payable)') + .pause(5000) + .journalLastChildIncludes('Error provided by the contract:') + .journalLastChildIncludes('CustomError : error description from library') + .journalLastChildIncludes('Parameters:') + .journalLastChildIncludes('"value": "48"') + .journalLastChildIncludes('"value": "46"') + .journalLastChildIncludes('"value": "error_string_from_library"') + .journalLastChildIncludes('"documentation": "param1 from library"') + .journalLastChildIncludes('"documentation": "param2 from library"') + .journalLastChildIncludes('"documentation": "param3 from library"') + .journalLastChildIncludes('Debug the transaction to get more information.') .end() - } + }, } // @TODO test: bytes8[3][] type as input @@ -281,5 +302,29 @@ contract C { } }` } + }, + { + 'customErrorLib.sol': { + content: `// SPDX-License-Identifier: GPL-3.0 + + pragma solidity ^0.8.7; + + library lib { + /// error description from library + /// @param a param1 from library + /// @param b param2 from library + /// @param c param3 from library + error CustomError(uint a, uint b, string c); + function set() public { + revert CustomError(48, 46, "error_string_from_library"); + } + } + + contract D { + function h() public { + lib.set(); + } + }` + } } ] diff --git a/apps/remix-ide/src/blockchain/blockchain.js b/apps/remix-ide/src/blockchain/blockchain.js index f0daf8d880..f280e47d8b 100644 --- a/apps/remix-ide/src/blockchain/blockchain.js +++ b/apps/remix-ide/src/blockchain/blockchain.js @@ -524,7 +524,8 @@ class Blockchain extends Plugin { if (execResult) { // if it's not the VM, we don't have return value. We only have the transaction, and it does not contain the return value. returnValue = execResult ? execResult.returnValue : toBuffer(addHexPrefix(txResult.result) || '0x0000000000000000000000000000000000000000000000000000000000000000') - const vmError = txExecution.checkVMError(execResult, args.data.contractABI, args.data.contract) + const compiledContracts = await this.call('compilerArtefacts', 'getAllContractDatas') + const vmError = txExecution.checkVMError(execResult, compiledContracts) if (vmError.error) { return cb(vmError.message) } diff --git a/libs/remix-core-plugin/src/lib/compiler-artefacts.ts b/libs/remix-core-plugin/src/lib/compiler-artefacts.ts index 52e1c7730b..b7f907f270 100644 --- a/libs/remix-core-plugin/src/lib/compiler-artefacts.ts +++ b/libs/remix-core-plugin/src/lib/compiler-artefacts.ts @@ -4,7 +4,7 @@ import { CompilerAbstract } from '@remix-project/remix-solidity' const profile = { name: 'compilerArtefacts', - methods: ['get', 'addResolvedContract', 'getCompilerAbstract'], + methods: ['get', 'addResolvedContract', 'getCompilerAbstract' , 'getAllContractDatas'], events: [], version: '0.0.1' } diff --git a/libs/remix-lib/src/execution/txExecution.ts b/libs/remix-lib/src/execution/txExecution.ts index f7e34333ee..5171c7c4e2 100644 --- a/libs/remix-lib/src/execution/txExecution.ts +++ b/libs/remix-lib/src/execution/txExecution.ts @@ -57,7 +57,7 @@ export function callFunction (from, to, data, value, gasLimit, funAbi, txRunner, * @param {Object} execResult - execution result given by the VM * @return {Object} - { error: true/false, message: DOMNode } */ -export function checkVMError (execResult, abi, contract) { +export function checkVMError (execResult, compiledContracts) { const errorCode = { OUT_OF_GAS: 'out of gas', STACK_UNDERFLOW: 'stack underflow', @@ -91,56 +91,61 @@ export function checkVMError (execResult, abi, contract) { const returnData = execResult.returnValue const returnDataHex = returnData.slice(0, 4).toString('hex') let customError - if (abi) { + if (compiledContracts) { let decodedCustomErrorInputsClean - for (const item of abi) { - if (item.type === 'error') { - // ethers doesn't crash anymore if "error" type is specified, but it doesn't extract the errors. see: - // https://github.com/ethers-io/ethers.js/commit/bd05aed070ac9e1421a3e2bff2ceea150bedf9b7 - // we need here to fake the type, so the "getSighash" function works properly - const fn = getFunctionFragment({ ...item, type: 'function', stateMutability: 'nonpayable' }) - if (!fn) continue - const sign = fn.getSighash(item.name) - if (!sign) continue - if (returnDataHex === sign.replace('0x', '')) { - customError = item.name - const functionDesc = fn.getFunction(item.name) - // decoding error parameters - const decodedCustomErrorInputs = fn.decodeFunctionData(functionDesc, returnData) - decodedCustomErrorInputsClean = {} - let devdoc = {} - // "contract" reprensents the compilation result containing the NATSPEC documentation - if (contract && fn.functions && Object.keys(fn.functions).length) { - const functionSignature = Object.keys(fn.functions)[0] - // we check in the 'devdoc' if there's a developer documentation for this error - try { - devdoc = (contract.object.devdoc.errors && contract.object.devdoc.errors[functionSignature][0]) || {} - } catch (e) { - console.error(e.message) + for (const file of Object.keys(compiledContracts)) { + for (const contractName of Object.keys(compiledContracts[file])) { + const contract = compiledContracts[file][contractName] + for (const item of contract.abi) { + if (item.type === 'error') { + // ethers doesn't crash anymore if "error" type is specified, but it doesn't extract the errors. see: + // https://github.com/ethers-io/ethers.js/commit/bd05aed070ac9e1421a3e2bff2ceea150bedf9b7 + // we need here to fake the type, so the "getSighash" function works properly + const fn = getFunctionFragment({ ...item, type: 'function', stateMutability: 'nonpayable' }) + if (!fn) continue + const sign = fn.getSighash(item.name) + if (!sign) continue + if (returnDataHex === sign.replace('0x', '')) { + customError = item.name + const functionDesc = fn.getFunction(item.name) + // decoding error parameters + const decodedCustomErrorInputs = fn.decodeFunctionData(functionDesc, returnData) + decodedCustomErrorInputsClean = {} + let devdoc = {} + // "contract" reprensents the compilation result containing the NATSPEC documentation + if (contract && fn.functions && Object.keys(fn.functions).length) { + const functionSignature = Object.keys(fn.functions)[0] + // we check in the 'devdoc' if there's a developer documentation for this error + try { + devdoc = (contract.devdoc.errors && contract.devdoc.errors[functionSignature][0]) || {} + } catch (e) { + console.error(e.message) + } + // we check in the 'userdoc' if there's an user documentation for this error + try { + 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) { + console.error(e.message) + } + } + let inputIndex = 0 + for (const input of functionDesc.inputs) { + const inputKey = input.name || inputIndex + const v = decodedCustomErrorInputs[inputKey] + + decodedCustomErrorInputsClean[inputKey] = { + value: v.toString ? v.toString() : v + } + 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 + } + inputIndex++ + } + break } - // we check in the 'userdoc' if there's an user documentation for this error - try { - 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 - } catch (e) { - console.error(e.message) - } - } - let inputIndex = 0 - for (const input of functionDesc.inputs) { - const inputKey = input.name || inputIndex - const v = decodedCustomErrorInputs[inputKey] - - decodedCustomErrorInputsClean[inputKey] = { - value: v.toString ? v.toString() : v - } - 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 - } - inputIndex++ } - break - } + } } } if (decodedCustomErrorInputsClean) { From 1eaa56b142e882a8a6b444d1adcde51bd0db00fd Mon Sep 17 00:00:00 2001 From: yann300 Date: Wed, 17 Nov 2021 15:47:35 +0100 Subject: [PATCH 2/4] fix linting --- apps/remix-ide-e2e/src/tests/transactionExecution.spec.ts | 2 +- libs/remix-core-plugin/src/lib/compiler-artefacts.ts | 2 +- libs/remix-lib/src/execution/txExecution.ts | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/apps/remix-ide-e2e/src/tests/transactionExecution.spec.ts b/apps/remix-ide-e2e/src/tests/transactionExecution.spec.ts index 6c1e832869..cfeae8987d 100644 --- a/apps/remix-ide-e2e/src/tests/transactionExecution.spec.ts +++ b/apps/remix-ide-e2e/src/tests/transactionExecution.spec.ts @@ -200,7 +200,7 @@ module.exports = { .journalLastChildIncludes('"documentation": "param3 from library"') .journalLastChildIncludes('Debug the transaction to get more information.') .end() - }, + } } // @TODO test: bytes8[3][] type as input diff --git a/libs/remix-core-plugin/src/lib/compiler-artefacts.ts b/libs/remix-core-plugin/src/lib/compiler-artefacts.ts index b7f907f270..148aa292c7 100644 --- a/libs/remix-core-plugin/src/lib/compiler-artefacts.ts +++ b/libs/remix-core-plugin/src/lib/compiler-artefacts.ts @@ -4,7 +4,7 @@ import { CompilerAbstract } from '@remix-project/remix-solidity' const profile = { name: 'compilerArtefacts', - methods: ['get', 'addResolvedContract', 'getCompilerAbstract' , 'getAllContractDatas'], + methods: ['get', 'addResolvedContract', 'getCompilerAbstract', 'getAllContractDatas'], events: [], version: '0.0.1' } diff --git a/libs/remix-lib/src/execution/txExecution.ts b/libs/remix-lib/src/execution/txExecution.ts index 5171c7c4e2..da9274e89c 100644 --- a/libs/remix-lib/src/execution/txExecution.ts +++ b/libs/remix-lib/src/execution/txExecution.ts @@ -133,7 +133,7 @@ export function checkVMError (execResult, compiledContracts) { for (const input of functionDesc.inputs) { const inputKey = input.name || inputIndex const v = decodedCustomErrorInputs[inputKey] - + decodedCustomErrorInputsClean[inputKey] = { value: v.toString ? v.toString() : v } @@ -145,7 +145,7 @@ export function checkVMError (execResult, compiledContracts) { break } } - } + } } } if (decodedCustomErrorInputsClean) { From 75acfcd3a7bf79d2f42f13776addac8b5476607b Mon Sep 17 00:00:00 2001 From: yann300 Date: Wed, 17 Nov 2021 17:25:34 +0100 Subject: [PATCH 3/4] fix e2e --- apps/remix-ide-e2e/src/tests/transactionExecution.spec.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/apps/remix-ide-e2e/src/tests/transactionExecution.spec.ts b/apps/remix-ide-e2e/src/tests/transactionExecution.spec.ts index cfeae8987d..a0cafb1f56 100644 --- a/apps/remix-ide-e2e/src/tests/transactionExecution.spec.ts +++ b/apps/remix-ide-e2e/src/tests/transactionExecution.spec.ts @@ -181,9 +181,8 @@ module.exports = { }, 'Should Compile and Deploy a contract which define a custom error in a library, the error should be logged in the terminal': function (browser: NightwatchBrowser) { - browser.testContracts('customError.sol', sources[5]['customErrorLib.sol'], ['D']) + browser.testContracts('customErrorLib.sol', sources[5]['customErrorLib.sol'], ['D']) .clickLaunchIcon('udapp') - .selectAccount('0xCA35b7d915458EF540aDe6068dFe2F44E8fa733c') // this account will be used for this test suite .click('#runTabView button[class^="instanceButton"]') .waitForElementPresent('.instance:nth-of-type(3)') .click('.instance:nth-of-type(3) > div > button') From 047dceeb42c890f2fc71ec6fb6246bc511d07f17 Mon Sep 17 00:00:00 2001 From: yann300 Date: Mon, 22 Nov 2021 10:32:58 +0100 Subject: [PATCH 4/4] fix e2e --- apps/remix-ide-e2e/src/commands/clickInstance.ts | 2 +- .../src/tests/transactionExecution.spec.ts | 11 +++++------ 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/apps/remix-ide-e2e/src/commands/clickInstance.ts b/apps/remix-ide-e2e/src/commands/clickInstance.ts index 091d66ea94..f9d5ee8e6f 100644 --- a/apps/remix-ide-e2e/src/commands/clickInstance.ts +++ b/apps/remix-ide-e2e/src/commands/clickInstance.ts @@ -6,7 +6,7 @@ class ClickInstance extends EventEmitter { index = index + 2 const selector = '.instance:nth-of-type(' + index + ') > div > button' - this.api.waitForElementContainsText(selector, '', 60000).scrollAndClick(selector).perform(() => { this.emit('complete') }) + this.api.waitForElementPresent(selector).waitForElementContainsText(selector, '', 60000).scrollAndClick(selector).perform(() => { this.emit('complete') }) return this } } diff --git a/apps/remix-ide-e2e/src/tests/transactionExecution.spec.ts b/apps/remix-ide-e2e/src/tests/transactionExecution.spec.ts index a0cafb1f56..7f6124ccc4 100644 --- a/apps/remix-ide-e2e/src/tests/transactionExecution.spec.ts +++ b/apps/remix-ide-e2e/src/tests/transactionExecution.spec.ts @@ -137,6 +137,7 @@ module.exports = { .selectAccount('0xCA35b7d915458EF540aDe6068dFe2F44E8fa733c') // this account will be used for this test suite .click('#runTabView button[class^="instanceButton"]') .waitForElementPresent('.instance:nth-of-type(2)') + .click('*[data-id="deployAndRunClearInstances"]') }, 'Should Compile and Deploy a contract which define a custom error, the error should be logged in the terminal': function (browser: NightwatchBrowser) { @@ -144,8 +145,7 @@ module.exports = { .clickLaunchIcon('udapp') .selectAccount('0xCA35b7d915458EF540aDe6068dFe2F44E8fa733c') // this account will be used for this test suite .click('#runTabView button[class^="instanceButton"]') - .waitForElementPresent('.instance:nth-of-type(3)') - .click('.instance:nth-of-type(3) > div > button') + .clickInstance(0) .clickFunction('g - transact (not payable)') .pause(5000) .journalLastChildIncludes('Error provided by the contract:') @@ -158,6 +158,7 @@ module.exports = { .journalLastChildIncludes('"documentation": "param2"') .journalLastChildIncludes('"documentation": "param3"') .journalLastChildIncludes('Debug the transaction to get more information.') + .click('*[data-id="deployAndRunClearInstances"]') }, 'Should Compile and Deploy a contract which define a custom error, the error should be logged in the terminal , using London VM Fork': function (browser: NightwatchBrowser) { @@ -165,8 +166,7 @@ module.exports = { .click('*[data-id="settingsVMLondonMode"]') // switch to London fork .selectAccount('0xCA35b7d915458EF540aDe6068dFe2F44E8fa733c') // this account will be used for this test suite .click('#runTabView button[class^="instanceButton"]') - .waitForElementPresent('.instance:nth-of-type(2)') - .click('.instance:nth-of-type(2) > div > button') + .clickInstance(0) .clickFunction('g - transact (not payable)') .journalLastChildIncludes('Error provided by the contract:') .journalLastChildIncludes('CustomError : error description') @@ -184,8 +184,7 @@ module.exports = { browser.testContracts('customErrorLib.sol', sources[5]['customErrorLib.sol'], ['D']) .clickLaunchIcon('udapp') .click('#runTabView button[class^="instanceButton"]') - .waitForElementPresent('.instance:nth-of-type(3)') - .click('.instance:nth-of-type(3) > div > button') + .clickInstance(1) .clickFunction('h - transact (not payable)') .pause(5000) .journalLastChildIncludes('Error provided by the contract:')