Merge branch 'master' into slrm

pull/1780/head
Liana Husikyan 3 years ago committed by GitHub
commit c69dcd2ad5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 2
      apps/remix-ide-e2e/src/commands/clickInstance.ts
  2. 51
      apps/remix-ide-e2e/src/tests/transactionExecution.spec.ts
  3. 3
      apps/remix-ide/src/blockchain/blockchain.js
  4. 2
      apps/remix-ide/src/remixAppManager.js
  5. 2
      libs/remix-core-plugin/src/lib/compiler-artefacts.ts
  6. 97
      libs/remix-lib/src/execution/txExecution.ts
  7. 2
      libs/remix-ui/workspace/src/lib/actions/workspace.ts
  8. 3
      libs/remix-ui/workspace/src/lib/components/file-explorer.tsx

@ -6,7 +6,7 @@ class ClickInstance extends EventEmitter {
index = index + 2 index = index + 2
const selector = '.instance:nth-of-type(' + index + ') > div > button' 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 return this
} }
} }

@ -137,6 +137,7 @@ module.exports = {
.selectAccount('0xCA35b7d915458EF540aDe6068dFe2F44E8fa733c') // this account will be used for this test suite .selectAccount('0xCA35b7d915458EF540aDe6068dFe2F44E8fa733c') // this account will be used for this test suite
.click('#runTabView button[class^="instanceButton"]') .click('#runTabView button[class^="instanceButton"]')
.waitForElementPresent('.instance:nth-of-type(2)') .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) { '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') .clickLaunchIcon('udapp')
.selectAccount('0xCA35b7d915458EF540aDe6068dFe2F44E8fa733c') // this account will be used for this test suite .selectAccount('0xCA35b7d915458EF540aDe6068dFe2F44E8fa733c') // this account will be used for this test suite
.click('#runTabView button[class^="instanceButton"]') .click('#runTabView button[class^="instanceButton"]')
.waitForElementPresent('.instance:nth-of-type(3)') .clickInstance(0)
.click('.instance:nth-of-type(3) > div > button')
.clickFunction('g - transact (not payable)') .clickFunction('g - transact (not payable)')
.pause(5000) .pause(5000)
.journalLastChildIncludes('Error provided by the contract:') .journalLastChildIncludes('Error provided by the contract:')
@ -158,6 +158,7 @@ module.exports = {
.journalLastChildIncludes('"documentation": "param2"') .journalLastChildIncludes('"documentation": "param2"')
.journalLastChildIncludes('"documentation": "param3"') .journalLastChildIncludes('"documentation": "param3"')
.journalLastChildIncludes('Debug the transaction to get more information.') .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) { '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 .click('*[data-id="settingsVMLondonMode"]') // switch to London fork
.selectAccount('0xCA35b7d915458EF540aDe6068dFe2F44E8fa733c') // this account will be used for this test suite .selectAccount('0xCA35b7d915458EF540aDe6068dFe2F44E8fa733c') // this account will be used for this test suite
.click('#runTabView button[class^="instanceButton"]') .click('#runTabView button[class^="instanceButton"]')
.waitForElementPresent('.instance:nth-of-type(2)') .clickInstance(0)
.click('.instance:nth-of-type(2) > div > button')
.clickFunction('g - transact (not payable)') .clickFunction('g - transact (not payable)')
.journalLastChildIncludes('Error provided by the contract:') .journalLastChildIncludes('Error provided by the contract:')
.journalLastChildIncludes('CustomError : error description') .journalLastChildIncludes('CustomError : error description')
@ -178,6 +178,25 @@ module.exports = {
.journalLastChildIncludes('"documentation": "param2"') .journalLastChildIncludes('"documentation": "param2"')
.journalLastChildIncludes('"documentation": "param3"') .journalLastChildIncludes('"documentation": "param3"')
.journalLastChildIncludes('Debug the transaction to get more information.') .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('customErrorLib.sol', sources[5]['customErrorLib.sol'], ['D'])
.clickLaunchIcon('udapp')
.click('#runTabView button[class^="instanceButton"]')
.clickInstance(1)
.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() .end()
} }
} }
@ -281,5 +300,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();
}
}`
}
} }
] ]

@ -524,7 +524,8 @@ class Blockchain extends Plugin {
if (execResult) { 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. // 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') 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) { if (vmError.error) {
return cb(vmError.message) return cb(vmError.message)
} }

@ -11,7 +11,7 @@ const requiredModules = [ // services + layout views + system views
'fileManager', 'contentImport', 'blockchain', 'web3Provider', 'scriptRunner', 'fetchAndCompile', 'mainPanel', 'hiddenPanel', 'sidePanel', 'menuicons', 'fileManager', 'contentImport', 'blockchain', 'web3Provider', 'scriptRunner', 'fetchAndCompile', 'mainPanel', 'hiddenPanel', 'sidePanel', 'menuicons',
'filePanel', 'terminal', 'settings', 'pluginManager', 'tabs', 'udapp', 'dGitProvider', 'solidity-logic'] 'filePanel', 'terminal', 'settings', 'pluginManager', 'tabs', 'udapp', 'dGitProvider', 'solidity-logic']
const dependentModules = ['git', 'hardhat'] // module which shouldn't be manually activated (e.g git is activated by remixd) const dependentModules = ['git', 'hardhat', 'slither'] // module which shouldn't be manually activated (e.g git is activated by remixd)
export function isNative (name) { export function isNative (name) {
const nativePlugins = ['vyper', 'workshops', 'debugger', 'remixd', 'menuicons', 'solidity', 'hardhat-provider', 'solidityStaticAnalysis', 'solidityUnitTesting'] const nativePlugins = ['vyper', 'workshops', 'debugger', 'remixd', 'menuicons', 'solidity', 'hardhat-provider', 'solidityStaticAnalysis', 'solidityUnitTesting']

@ -4,7 +4,7 @@ import { CompilerAbstract } from '@remix-project/remix-solidity'
const profile = { const profile = {
name: 'compilerArtefacts', name: 'compilerArtefacts',
methods: ['get', 'addResolvedContract', 'getCompilerAbstract'], methods: ['get', 'addResolvedContract', 'getCompilerAbstract', 'getAllContractDatas'],
events: [], events: [],
version: '0.0.1' version: '0.0.1'
} }

@ -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
} }
} }
} }

@ -261,7 +261,7 @@ export const uploadFile = async (target, targetFolder: string, cb?: (err: Error,
fileReader.readAsText(file) fileReader.readAsText(file)
cb && cb(null, true) cb && cb(null, true)
} }
const name = `${targetFolder}/${file.name}` const name = targetFolder === '/' ? file.name : `${targetFolder}/${file.name}`
workspaceProvider.exists(name).then(exist => { workspaceProvider.exists(name).then(exist => {
if (!exist) { if (!exist) {

@ -169,9 +169,10 @@ export const FileExplorer = (props: FileExplorerProps) => {
} }
const uploadFile = (target) => { const uploadFile = (target) => {
const parentFolder = getFocusedFolder() let parentFolder = getFocusedFolder()
const expandPath = [...new Set([...props.expandPath, parentFolder])] const expandPath = [...new Set([...props.expandPath, parentFolder])]
parentFolder = parentFolder === name ? '/' : parentFolder
props.dispatchHandleExpandPath(expandPath) props.dispatchHandleExpandPath(expandPath)
props.dispatchUploadFile(target, parentFolder) props.dispatchUploadFile(target, parentFolder)
} }

Loading…
Cancel
Save