Merge branch 'master' into mg-compile-btn-text

pull/2257/head
Miladinho 3 years ago committed by GitHub
commit 5d73d7c2b2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 4
      apps/remix-ide-e2e/src/tests/debugger.test.ts
  2. 2
      apps/remix-ide-e2e/src/tests/solidityUnittests.test.ts
  3. 124
      apps/remix-ide-e2e/src/tests/terminal.test.ts
  4. 7
      apps/remix-ide/src/app.js
  5. 62
      apps/remix-ide/src/app/tabs/intelligent-script-executor.ts
  6. 9
      apps/remix-ide/src/app/tabs/web3-provider.js
  7. 10
      apps/remix-ide/src/app/udapp/run-tab.js
  8. 10
      apps/remix-ide/src/blockchain/blockchain.js
  9. 2
      apps/remix-ide/src/remixAppManager.js
  10. 2
      apps/solidity-compiler/src/app/compiler-api.ts
  11. 35
      libs/remix-core-plugin/src/lib/compiler-artefacts.ts
  12. 3
      libs/remix-core-plugin/src/lib/compiler-metadata.ts
  13. 9
      libs/remix-lib/src/execution/txListener.ts
  14. 7
      libs/remix-lib/src/execution/txRunnerVM.ts
  15. 52
      libs/remix-lib/src/execution/txRunnerWeb3.ts
  16. 51
      libs/remix-simulator/src/methods/blocks.ts
  17. 27
      libs/remix-simulator/src/methods/transactions.ts
  18. 2
      libs/remix-tests/src/compiler.ts
  19. 3
      libs/remix-tests/src/deployer.ts
  20. 56
      libs/remix-ui/home-tab/src/lib/remix-ui-home-tab.tsx
  21. 10
      libs/remix-ui/run-tab/src/lib/actions/index.ts
  22. 2
      libs/remix-ui/terminal/src/lib/remix-ui-terminal.tsx
  23. 59
      libs/remix-ui/workspace/src/lib/actions/index.ts
  24. 4
      libs/remix-ui/workspace/src/lib/contexts/index.ts
  25. 3
      libs/remix-ui/workspace/src/lib/css/remix-ui-workspace.css
  26. 14
      libs/remix-ui/workspace/src/lib/providers/FileSystemProvider.tsx
  27. 39
      libs/remix-ui/workspace/src/lib/remix-ui-workspace.tsx
  28. 5
      libs/remixd/src/services/slitherClient.ts

@ -477,7 +477,7 @@ const localVariable_step717_ABIEncoder = { // eslint-disable-line
const jsGetTrace = `(async () => { const jsGetTrace = `(async () => {
try { try {
const result = await remix.call('debugger', 'getTrace', '0x16be5c31014a7e1552d136f7ed7bc7788f3bb9e45e31b059df253173f2df31e7') const result = await remix.call('debugger', 'getTrace', '0x65f0813753462414f9a91f0aabea946188327995f54b893b63a8d7ff186cfca3')
console.log('result ', result) console.log('result ', result)
} catch (e) { } catch (e) {
console.log(e.message) console.log(e.message)
@ -486,7 +486,7 @@ const jsGetTrace = `(async () => {
const jsDebug = `(async () => { const jsDebug = `(async () => {
try { try {
const result = await remix.call('debugger', 'debug', '0x16be5c31014a7e1552d136f7ed7bc7788f3bb9e45e31b059df253173f2df31e7') const result = await remix.call('debugger', 'debug', '0x65f0813753462414f9a91f0aabea946188327995f54b893b63a8d7ff186cfca3')
console.log('result ', result) console.log('result ', result)
} catch (e) { } catch (e) {
console.log(e.message) console.log(e.message)

@ -132,7 +132,7 @@ module.exports = {
.click('*[data-id="testTabCheckAllTests"]') .click('*[data-id="testTabCheckAllTests"]')
.clickElementAtPosition('.singleTestLabel', 1) .clickElementAtPosition('.singleTestLabel', 1)
.scrollAndClick('*[data-id="testTabRunTestsTabRunAction"]') .scrollAndClick('*[data-id="testTabRunTestsTabRunAction"]')
.waitForElementContainsText('*[data-id="testTabSolidityUnitTestsOutput"]', 'contract deployment failed after trying twice', 120000) .waitForElementContainsText('*[data-id="testTabSolidityUnitTestsOutput"]', 'revert Deploy Failed', 120000)
}, },
'Should fail when parameters are passed to method in test contract #group3': function (browser: NightwatchBrowser) { 'Should fail when parameters are passed to method in test contract #group3': function (browser: NightwatchBrowser) {

@ -190,7 +190,17 @@ module.exports = {
.waitForElementVisible('*[data-id="terminalCli"]') .waitForElementVisible('*[data-id="terminalCli"]')
.click('*[data-id="terminalCli"]') .click('*[data-id="terminalCli"]')
.sendKeys('*[data-id="terminalCliInput"]', 'remix.') .sendKeys('*[data-id="terminalCliInput"]', 'remix.')
.assert.visible('*[data-id="autoCompletePopUpAutoCompleteItem"]').end() .assert.visible('*[data-id="autoCompletePopUpAutoCompleteItem"]')
},
'Should run a script right after compilation #group6': function (browser: NightwatchBrowser) {
browser
.addFile('contracts/storage.sol', { content: scriptAutoExec.contract } )
.addFile('scripts/deploy_storage.js', { content: scriptAutoExec.script } )
.openFile('contracts/storage.sol')
.sendKeys('body', [browser.Keys.CONTROL, browser.Keys.SHIFT, 's'])
.pause(5000)
.journalLastChildIncludes('147')
} }
} }
@ -484,3 +494,115 @@ contract OwnerTest {
return owner; return owner;
} }
}` }`
const scriptAutoExec = {
contract: `// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.7.0 <0.9.0;
library lib {
function test () public view returns (uint) {
return 147;
}
}
/**
* @title Storage
* @dev Store & retrieve value inr a variable
* @custom:dev-run-script ./scripts/deploy_storage.js
*/
contract Storage {
uint256 number;
/**
* @dev Store valrue in variable
* @param num value to store
*/
function store(uint256 num) public {
number = num;
}
/**
* @dev Return value
* @return value of 'number'
*/
function retrieve() public view returns (uint256){
return number;
}
function getFromLib() public view returns (uint) {
return lib.test();
}
}
`,
script: `
// Right click on the script name and hit "Run" to execute
const { expect } = require("chai");
const { ethers } = require("hardhat");
(async () => {
try {
// function getContractFactoryFromArtifact(artifact: Artifact, signer?: ethers.Signer): Promise<ethers.ContractFactory>;
// function getContractFactoryFromArtifact(artifact: Artifact, factoryOptions: FactoryOptions): Promise<ethers.ContractFactory>;
const metadataLib = JSON.parse(await remix.call('fileManager', 'readFile', 'contracts/artifacts/lib.json'))
console.log('deploying lib:')
const artifactLib = {
contractName: 'Lib',
sourceName: 'contracts/1_Storage.sol',
abi: metadataLib.abi,
bytecode: '0x' + metadataLib.data.bytecode.object,
deployedBytecode: '0x' + metadataLib.data.deployedBytecode.object,
linkReferences: metadataLib.data.bytecode.linkReferences,
deployedLinkReferences: metadataLib.data.deployedBytecode.linkReferences,
}
const optionsLib = {}
const factoryLib = await ethers.getContractFactoryFromArtifact(artifactLib, optionsLib)
const lib = await factoryLib.deploy();
await lib.deployed()
console.log('lib deployed', lib.address)
const metadata = JSON.parse(await remix.call('fileManager', 'readFile', 'contracts/artifacts/Storage.json'))
const artifact = {
contractName: 'Storage',
sourceName: 'contracts/1_Storage.sol',
abi: metadata.abi,
bytecode: '0x' + metadata.data.bytecode.object,
deployedBytecode: '0x' + metadata.data.deployedBytecode.object,
linkReferences: metadata.data.bytecode.linkReferences,
deployedLinkReferences: metadata.data.deployedBytecode.linkReferences,
}
const options = {
libraries: {
'lib': lib.address
}
}
const factory = await ethers.getContractFactoryFromArtifact(artifact, options)
const storage = await factory.deploy();
await storage.deployed()
const storeValue = await storage.store(333);
// wait until the transaction is mined
await storeValue.wait();
console.log((await storage.getFromLib()).toString())
// expect((await storage.getFromLib()).toString()).to.equal('34');
} catch (e) {
console.error(e.message)
}
})()
`
}

@ -5,6 +5,7 @@ import { RemixAppManager } from './remixAppManager'
import { ThemeModule } from './app/tabs/theme-module' import { ThemeModule } from './app/tabs/theme-module'
import { NetworkModule } from './app/tabs/network-module' import { NetworkModule } from './app/tabs/network-module'
import { Web3ProviderModule } from './app/tabs/web3-provider' import { Web3ProviderModule } from './app/tabs/web3-provider'
import { IntelligentScriptExecutor } from './app/tabs/intelligent-script-executor'
import { SidePanel } from './app/components/side-panel' import { SidePanel } from './app/components/side-panel'
import { HiddenPanel } from './app/components/hidden-panel' import { HiddenPanel } from './app/components/hidden-panel'
import { VerticalIcons } from './app/components/vertical-icons' import { VerticalIcons } from './app/components/vertical-icons'
@ -179,7 +180,8 @@ class AppComponent {
api: offsetToLineColumnConverter, api: offsetToLineColumnConverter,
name: 'offsettolinecolumnconverter' name: 'offsettolinecolumnconverter'
}) })
// ----------------- run script after each compilation results -----------
const intelligentScriptExecutor = new IntelligentScriptExecutor()
// -------------------Terminal---------------------------------------- // -------------------Terminal----------------------------------------
makeUdapp(blockchain, compilersArtefacts, domEl => terminal.logHtml(domEl)) makeUdapp(blockchain, compilersArtefacts, domEl => terminal.logHtml(domEl))
const terminal = new Terminal( const terminal = new Terminal(
@ -222,6 +224,7 @@ class AppComponent {
contextualListener, contextualListener,
terminal, terminal,
web3Provider, web3Provider,
intelligentScriptExecutor,
fetchAndCompile, fetchAndCompile,
dGitProvider, dGitProvider,
storagePlugin, storagePlugin,
@ -343,7 +346,7 @@ class AppComponent {
await this.appManager.activatePlugin(['settings', 'config']) await this.appManager.activatePlugin(['settings', 'config'])
await this.appManager.activatePlugin(['hiddenPanel', 'pluginManager', 'contextualListener', 'terminal', 'blockchain', 'fetchAndCompile', 'contentImport', 'gistHandler']) await this.appManager.activatePlugin(['hiddenPanel', 'pluginManager', 'contextualListener', 'terminal', 'blockchain', 'fetchAndCompile', 'contentImport', 'gistHandler'])
await this.appManager.activatePlugin(['settings']) await this.appManager.activatePlugin(['settings'])
await this.appManager.activatePlugin(['walkthrough','storage']) await this.appManager.activatePlugin(['walkthrough','storage', 'intelligentScriptExecutor'])
this.appManager.on( this.appManager.on(
'filePanel', 'filePanel',

@ -0,0 +1,62 @@
import { Plugin } from '@remixproject/engine'
import * as packageJson from '../../../../../package.json'
export const profile = {
name: 'intelligentScriptExecutor',
displayName: 'Intelligent Script Executor',
description: 'after each compilation, run the script defined in Natspec.',
methods: [],
version: packageJson.version,
kind: 'none'
}
type listener = (event: KeyboardEvent) => void
export class IntelligentScriptExecutor extends Plugin {
executionListener: listener
targetFileName: string
constructor () {
super(profile)
this.executionListener = async (e) => {
// ctrl+e or command+e
const file = await this.call('fileManager', 'file')
if ((e.metaKey || e.ctrlKey) && e.shiftKey && e.keyCode === 83 && file !== '') {
if (file.endsWith('.sol')) {
e.preventDefault()
this.targetFileName = file
await this.call('solidity', 'compile', file)
} else if (file.endsWith('.js') || file.endsWith('.ts')) {
e.preventDefault()
this.runScript(file, false)
}
}
}
}
async runScript (fileName, clearAllInstances) {
await this.call('terminal', 'log', `running ${fileName} ...`)
const content = await this.call('fileManager', 'readFile', fileName)
if (clearAllInstances) {
await this.call('udapp', 'clearAllInstances')
}
await this.call('scriptRunner', 'execute', content)
}
onActivation () {
window.document.addEventListener('keydown', this.executionListener)
this.on('compilerMetadata', 'artefactsUpdated', async (fileName, contract) => {
if (this.targetFileName === contract.file && contract.object && contract.object.devdoc['custom:dev-run-script']) {
this.targetFileName = null
const file = contract.object.devdoc['custom:dev-run-script']
if (file) this.runScript(file, true)
}
})
}
onDeactivation () {
window.document.removeEventListener('keydown', this.executionListener)
this.off('compilerMetadata', 'artefactsUpdated')
}
}

@ -24,8 +24,15 @@ export class Web3ProviderModule extends Plugin {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
const provider = this.blockchain.web3().currentProvider const provider = this.blockchain.web3().currentProvider
// see https://github.com/ethereum/web3.js/pull/1018/files#diff-d25786686c1053b786cc2626dc6e048675050593c0ebaafbf0814e1996f22022R129 // see https://github.com/ethereum/web3.js/pull/1018/files#diff-d25786686c1053b786cc2626dc6e048675050593c0ebaafbf0814e1996f22022R129
provider[provider.sendAsync ? 'sendAsync' : 'send'](payload, (error, message) => { provider[provider.sendAsync ? 'sendAsync' : 'send'](payload, async (error, message) => {
if (error) return reject(error) if (error) return reject(error)
if (payload.method === 'eth_sendTransaction') {
if (payload.params.length && !payload.params[0].to && message.result) {
const receipt = await this.call('blockchain', 'getTransactionReceipt', message.result)
const contractData = await this.call('compilerArtefacts', 'getContractDataFromAddress', receipt.contractAddress)
if (contractData) this.call('udapp', 'addInstance', receipt.contractAddress, contractData.contract.abi, contractData.name)
}
}
resolve(message) resolve(message)
}) })
}) })

@ -18,7 +18,7 @@ const profile = {
version: packageJson.version, version: packageJson.version,
permission: true, permission: true,
events: ['newTransaction'], events: ['newTransaction'],
methods: ['createVMAccount', 'sendTransaction', 'getAccounts', 'pendingTransactionsCount', 'getSettings', 'setEnvironmentMode'] methods: ['createVMAccount', 'sendTransaction', 'getAccounts', 'pendingTransactionsCount', 'getSettings', 'setEnvironmentMode', 'clearAllInstances', 'addInstance']
} }
export class RunTab extends ViewPlugin { export class RunTab extends ViewPlugin {
@ -64,6 +64,14 @@ export class RunTab extends ViewPlugin {
} }
} }
clearAllInstances () {
this.emit('clearAllInstancesReducer')
}
addInstance (address, abi, name) {
this.emit('addInstanceReducer', address, abi, name)
}
createVMAccount (newAccount) { createVMAccount (newAccount) {
return this.blockchain.createVMAccount(newAccount) return this.blockchain.createVMAccount(newAccount)
} }

@ -22,7 +22,7 @@ const profile = {
name: 'blockchain', name: 'blockchain',
displayName: 'Blockchain', displayName: 'Blockchain',
description: 'Blockchain - Logic', description: 'Blockchain - Logic',
methods: [], methods: ['getCode', 'getTransactionReceipt'],
version: packageJson.version version: packageJson.version
} }
@ -392,6 +392,14 @@ export class Blockchain extends Plugin {
return Object.keys(this.txRunner.pendingTxs).length return Object.keys(this.txRunner.pendingTxs).length
} }
async getCode(address) {
return await this.web3().eth.getCode(address)
}
async getTransactionReceipt (hash) {
return await this.web3().eth.getTransactionReceipt(hash)
}
/** /**
* This function send a tx only to javascript VM or testnet, will return an error for the mainnet * This function send a tx only to javascript VM or testnet, will return an error for the mainnet
* SHOULD BE TAKEN CAREFULLY! * SHOULD BE TAKEN CAREFULLY!

@ -8,7 +8,7 @@ const requiredModules = [ // services + layout views + system views
'manager', 'config', 'compilerArtefacts', 'compilerMetadata', 'contextualListener', 'editor', 'offsetToLineColumnConverter', 'network', 'theme', 'manager', 'config', 'compilerArtefacts', 'compilerMetadata', 'contextualListener', 'editor', 'offsetToLineColumnConverter', 'network', 'theme',
'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', 'gistHandler', 'layout', 'filePanel', 'terminal', 'settings', 'pluginManager', 'tabs', 'udapp', 'dGitProvider', 'solidity-logic', 'gistHandler', 'layout',
'notification', 'permissionhandler', 'walkthrough', 'storage', 'restorebackupzip', 'link-libraries', 'deploy-libraries'] 'notification', 'permissionhandler', 'walkthrough', 'storage', 'restorebackupzip', 'link-libraries', 'deploy-libraries', 'intelligentScriptExecutor']
const dependentModules = ['git', 'hardhat', 'slither'] // 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)

@ -321,7 +321,7 @@ export const CompilerApiMixin = (Base) => class extends Base {
// Run the compiler instead of trying to save the website // Run the compiler instead of trying to save the website
this.data.eventHandlers.onKeyDown = async (e) => { this.data.eventHandlers.onKeyDown = async (e) => {
// ctrl+s or command+s // ctrl+s or command+s
if ((e.metaKey || e.ctrlKey) && e.keyCode === 83 && this.currentFile !== '') { if ((e.metaKey || e.ctrlKey) && !e.shiftKey && e.keyCode === 83 && this.currentFile !== '') {
e.preventDefault() e.preventDefault()
this.compileTabLogic.runCompiler(await this.getAppParameter('hardhat-compilation')) this.compileTabLogic.runCompiler(await this.getAppParameter('hardhat-compilation'))
} }

@ -1,10 +1,11 @@
'use strict' 'use strict'
import { Plugin } from '@remixproject/engine' import { Plugin } from '@remixproject/engine'
import { util } from '@remix-project/remix-lib'
import { CompilerAbstract } from '@remix-project/remix-solidity' import { CompilerAbstract } from '@remix-project/remix-solidity'
const profile = { const profile = {
name: 'compilerArtefacts', name: 'compilerArtefacts',
methods: ['get', 'addResolvedContract', 'getCompilerAbstract', 'getAllContractDatas', 'getLastCompilationResult', 'getArtefactsByContractName'], methods: ['get', 'addResolvedContract', 'getCompilerAbstract', 'getAllContractDatas', 'getLastCompilationResult', 'getArtefactsByContractName', 'getContractDataFromAddress'],
events: [], events: [],
version: '0.0.1' version: '0.0.1'
} }
@ -72,15 +73,27 @@ export class CompilerArtefacts extends Plugin {
* @returns compilatin output * @returns compilatin output
*/ */
getAllContractDatas () { getAllContractDatas () {
return this.filterAllContractDatas(() => true)
}
/**
* filter compilation output for contracts compiled during a session of Remix IDE
* @returns compilatin output
*/
filterAllContractDatas (filter) {
const contractsData = {} const contractsData = {}
Object.keys(this.compilersArtefactsPerFile).map((targetFile) => { Object.keys(this.compilersArtefactsPerFile).map((targetFile) => {
const contracts = this.compilersArtefactsPerFile[targetFile].getContracts() const contracts = this.compilersArtefactsPerFile[targetFile].getContracts()
Object.keys(contracts).map((file) => { contractsData[file] = contracts[file] }) Object.keys(contracts).map((file) => {
if (filter(file, contracts[file])) contractsData[file] = contracts[file]
})
}) })
// making sure we save last compilation result in there // making sure we save last compilation result in there
if (this.compilersArtefacts.__last) { if (this.compilersArtefacts.__last) {
const contracts = this.compilersArtefacts.__last.getContracts() const contracts = this.compilersArtefacts.__last.getContracts()
Object.keys(contracts).map((file) => { contractsData[file] = contracts[file] }) Object.keys(contracts).map((file) => {
if (filter(file, contracts[file])) contractsData[file] = contracts[file]
})
} }
return contractsData return contractsData
} }
@ -182,4 +195,20 @@ export class CompilerArtefacts extends Plugin {
get (key) { get (key) {
return this.compilersArtefacts[key] return this.compilersArtefacts[key]
} }
async getContractDataFromAddress (address) {
const code = await this.call('blockchain', 'getCode', address)
let found
this.filterAllContractDatas((file, contractsData) => {
for (const name of Object.keys(contractsData)) {
const contract = contractsData[name]
if (util.compareByteCode(code, '0x' + contract.evm.deployedBytecode.object)) {
found = { name, contract }
return true
}
}
return true
})
return found
}
} }

@ -103,7 +103,8 @@ export class CompilerMetadata extends Plugin {
}, },
abi: contract.object.abi abi: contract.object.abi
} }
await this.call('fileManager', 'writeFile', fileName, JSON.stringify(data, null, '\t')) await this.call('fileManager', 'writeFile', fileName, JSON.stringify(data, null, '\t'))
this.emit('artefactsUpdated', fileName, contract)
} }
_syncContext (contract, metadata) { _syncContext (contract, metadata) {

@ -60,7 +60,7 @@ export class TxListener {
// in VM mode // in VM mode
// in web3 mode && listen remix txs only // in web3 mode && listen remix txs only
if (!this._isListening) return // we don't listen if (!this._isListening) return // we don't listen
if (this._loopId && this.executionContext.getProvider() !== 'vm') return // we seems to already listen on a "web3" network if (this._loopId) return // we seems to already listen on a "web3" network
let returnValue let returnValue
let execResult let execResult
@ -95,7 +95,7 @@ export class TxListener {
// in VM mode // in VM mode
// in web3 mode && listen remix txs only // in web3 mode && listen remix txs only
if (!this._isListening) return // we don't listen if (!this._isListening) return // we don't listen
if (this._loopId && this.executionContext.getProvider() !== 'vm') return // we seems to already listen on a "web3" network if (this._loopId) return // we seems to already listen on a "web3" network
this.executionContext.web3().eth.getTransaction(txResult.transactionHash, async (error, tx) => { this.executionContext.web3().eth.getTransaction(txResult.transactionHash, async (error, tx) => {
if (error) return console.log(error) if (error) return console.log(error)
@ -133,7 +133,7 @@ export class TxListener {
*/ */
init () { init () {
this.blocks = [] this.blocks = []
this.lastBlock = null this.lastBlock = -1
} }
/** /**
@ -170,8 +170,7 @@ export class TxListener {
this.executionContext.web3().eth.getBlockNumber((error, blockNumber) => { this.executionContext.web3().eth.getBlockNumber((error, blockNumber) => {
if (this._loopId === null) return if (this._loopId === null) return
if (error) return console.log(error) if (error) return console.log(error)
if (currentLoopId === this._loopId && (!this.lastBlock || blockNumber > this.lastBlock)) { if (currentLoopId === this._loopId && blockNumber > this.lastBlock) {
if (!this.lastBlock) this.lastBlock = blockNumber - 1
let current = this.lastBlock + 1 let current = this.lastBlock + 1
this.lastBlock = blockNumber this.lastBlock = blockNumber
while (blockNumber >= current) { while (blockNumber >= current) {

@ -56,7 +56,12 @@ export class TxRunnerVM {
runInVm (from, to, data, value, gasLimit, useCall, timestamp, callback) { runInVm (from, to, data, value, gasLimit, useCall, timestamp, callback) {
const self = this const self = this
const account = self.vmaccounts[from] let account
if (!from && useCall && Object.keys(self.vmaccounts).length) {
from = Object.keys(self.vmaccounts)[0]
account = self.vmaccounts[from]
} else account = self.vmaccounts[from]
if (!account) { if (!account) {
return callback('Invalid account selected') return callback('Invalid account selected')
} }

@ -101,16 +101,16 @@ export class TxRunnerWeb3 {
// // @todo(#378) this should be removed when https://github.com/WalletConnect/walletconnect-monorepo/issues/334 is fixed // // @todo(#378) this should be removed when https://github.com/WalletConnect/walletconnect-monorepo/issues/334 is fixed
callback(new Error('Gas estimation failed because of an unknown internal error. This may indicated that the transaction will fail.')) callback(new Error('Gas estimation failed because of an unknown internal error. This may indicated that the transaction will fail.'))
} }
gasEstimationForceSend(err, () => { this._api.detectNetwork((errNetWork, network) => {
// callback is called whenever no error if (errNetWork) {
tx['gas'] = !gasEstimation ? gasLimit : gasEstimation console.log(errNetWork)
return
this._api.detectNetwork((err, network) => { }
if (err) { err = network.name === 'VM' ? null : err // just send the tx if "VM"
console.log(err) gasEstimationForceSend(err, () => {
return // callback is called whenever no error
} tx['gas'] = !gasEstimation ? gasLimit : gasEstimation
if (this._api.config.getUnpersistedProperty('doNotShowTransactionConfirmationAgain')) { if (this._api.config.getUnpersistedProperty('doNotShowTransactionConfirmationAgain')) {
return this._executeTx(tx, network, null, this._api, promptCb, callback) return this._executeTx(tx, network, null, this._api, promptCb, callback)
} }
@ -120,23 +120,23 @@ export class TxRunnerWeb3 {
}, (error) => { }, (error) => {
callback(error) callback(error)
}) })
}, () => {
const blockGasLimit = this.currentblockGasLimit()
// NOTE: estimateGas very likely will return a large limit if execution of the code failed
// we want to be able to run the code in order to debug and find the cause for the failure
if (err) return callback(err)
let warnEstimation = ' An important gas estimation might also be the sign of a problem in the contract code. Please check loops and be sure you did not sent value to a non payable function (that\'s also the reason of strong gas estimation). '
warnEstimation += ' ' + err
if (gasEstimation > gasLimit) {
return callback('Gas required exceeds limit: ' + gasLimit + '. ' + warnEstimation)
}
if (gasEstimation > blockGasLimit) {
return callback('Gas required exceeds block gas limit: ' + gasLimit + '. ' + warnEstimation)
}
}) })
}, () => { })
const blockGasLimit = this.currentblockGasLimit()
// NOTE: estimateGas very likely will return a large limit if execution of the code failed
// we want to be able to run the code in order to debug and find the cause for the failure
if (err) return callback(err)
let warnEstimation = ' An important gas estimation might also be the sign of a problem in the contract code. Please check loops and be sure you did not sent value to a non payable function (that\'s also the reason of strong gas estimation). '
warnEstimation += ' ' + err
if (gasEstimation > gasLimit) {
return callback('Gas required exceeds limit: ' + gasLimit + '. ' + warnEstimation)
}
if (gasEstimation > blockGasLimit) {
return callback('Gas required exceeds block gas limit: ' + gasLimit + '. ' + warnEstimation)
}
})
}) })
} }
} }

@ -1,6 +1,9 @@
import Web3 from 'web3'
export class Blocks { export class Blocks {
vmContext vmContext
coinbase: string coinbase: string
TX_INDEX = '0x0' // currently there's always only 1 tx per block, so the transaction index will always be 0x0
constructor (vmContext, _options) { constructor (vmContext, _options) {
this.vmContext = vmContext this.vmContext = vmContext
const options = _options || {} const options = _options || {}
@ -37,6 +40,28 @@ export class Blocks {
return cb(new Error('block not found')) return cb(new Error('block not found'))
} }
console.log(block.transactions)
const transactions = block.transactions.map((t) => {
const hash = '0x' + t.hash().toString('hex')
const tx = this.vmContext.txByHash[hash]
const receipt = this.vmContext.currentVm.web3vm.txsReceipt[hash]
if (receipt) {
return {
blockHash: '0x' + block.hash().toString('hex'),
blockNumber: '0x' + block.header.number.toString('hex'),
from: receipt.from,
gas: Web3.utils.toHex(receipt.gas),
chainId: '0xd05',
gasPrice: '0x4a817c800', // 20000000000
hash: receipt.transactionHash,
input: receipt.input,
nonce: '0x' + tx.nonce.toString('hex'),
transactionIndex: this.TX_INDEX,
value: receipt.value === '0x' ? '0x0' : receipt.value,
to: receipt.to ? receipt.to : null
}
}
})
const b = { const b = {
baseFeePerGas: '0x01', baseFeePerGas: '0x01',
number: this.toHex(block.header.number), number: this.toHex(block.header.number),
@ -55,7 +80,7 @@ export class Blocks {
gasLimit: this.toHex(block.header.gasLimit), gasLimit: this.toHex(block.header.gasLimit),
gasUsed: this.toHex(block.header.gasUsed), gasUsed: this.toHex(block.header.gasUsed),
timestamp: this.toHex(block.header.timestamp), timestamp: this.toHex(block.header.timestamp),
transactions: block.transactions.map((t) => '0x' + t.hash().toString('hex')), transactions,
uncles: [] uncles: []
} }
cb(null, b) cb(null, b)
@ -70,6 +95,28 @@ export class Blocks {
eth_getBlockByHash (payload, cb) { eth_getBlockByHash (payload, cb) {
const block = this.vmContext.blocks[payload.params[0]] const block = this.vmContext.blocks[payload.params[0]]
console.log(block.transactions)
const transactions = block.transactions.map((t) => {
const hash = '0x' + t.hash().toString('hex')
const tx = this.vmContext.txByHash[hash]
const receipt = this.vmContext.currentVm.web3vm.txsReceipt[hash]
if (receipt) {
return {
blockHash: '0x' + block.hash().toString('hex'),
blockNumber: '0x' + block.header.number.toString('hex'),
from: receipt.from,
gas: Web3.utils.toHex(receipt.gas),
chainId: '0xd05',
gasPrice: '0x4a817c800', // 20000000000
hash: receipt.transactionHash,
input: receipt.input,
nonce: '0x' + tx.nonce.toString('hex'),
transactionIndex: this.TX_INDEX,
value: receipt.value === '0x' ? '0x0' : receipt.value,
to: receipt.to ? receipt.to : null
}
}
})
const b = { const b = {
baseFeePerGas: '0x01', baseFeePerGas: '0x01',
number: this.toHex(block.header.number), number: this.toHex(block.header.number),
@ -88,7 +135,7 @@ export class Blocks {
gasLimit: this.toHex(block.header.gasLimit), gasLimit: this.toHex(block.header.gasLimit),
gasUsed: this.toHex(block.header.gasUsed), gasUsed: this.toHex(block.header.gasUsed),
timestamp: this.toHex(block.header.timestamp), timestamp: this.toHex(block.header.timestamp),
transactions: block.transactions.map((t) => '0x' + t.hash().toString('hex')), transactions,
uncles: [] uncles: []
} }

@ -2,6 +2,7 @@ import Web3 from 'web3'
import { toChecksumAddress, BN, Address } from 'ethereumjs-util' import { toChecksumAddress, BN, Address } from 'ethereumjs-util'
import { processTx } from './txProcess' import { processTx } from './txProcess'
import { execution } from '@remix-project/remix-lib' import { execution } from '@remix-project/remix-lib'
import { ethers } from 'ethers'
const TxRunnerVM = execution.TxRunnerVM const TxRunnerVM = execution.TxRunnerVM
const TxRunner = execution.TxRunner const TxRunner = execution.TxRunner
@ -122,7 +123,31 @@ export class Transactions {
} }
eth_estimateGas (payload, cb) { eth_estimateGas (payload, cb) {
cb(null, 10000000 * 8) // from might be lowercased address (web3)
if (payload.params && payload.params.length > 0 && payload.params[0].from) {
payload.params[0].from = toChecksumAddress(payload.params[0].from)
}
if (payload.params && payload.params.length > 0 && payload.params[0].to) {
payload.params[0].to = toChecksumAddress(payload.params[0].to)
}
payload.params[0].gas = 10000000 * 10
processTx(this.txRunnerInstance, payload, true, (error, result) => {
if (error) return cb(error)
if (result.result.status === '0x0') {
try {
const msg = result.result.execResult.returnValue
const abiCoder = new ethers.utils.AbiCoder()
const reason = abiCoder.decode(['string'], msg.slice(4))[0]
return cb('revert ' + reason)
} catch (e) {
return cb(e.message)
}
}
const gasUsed = result.result.execResult.gasUsed.toNumber()
cb(null, Math.ceil(gasUsed + (15 * gasUsed) / 100))
})
} }
eth_getCode (payload, cb) { eth_getCode (payload, cb) {

@ -183,7 +183,7 @@ export function compileContractSources (sources: SrcIfc, newCompConfig: any, imp
async.waterfall([ async.waterfall([
(next) => { (next) => {
if (!deepequal(UTRunner.compilerConfig, newCompConfig)) { if (!compiler || !deepequal(UTRunner.compilerConfig, newCompConfig)) {
UTRunner.compilerConfig = newCompConfig UTRunner.compilerConfig = newCompConfig
const { currentCompilerUrl, evmVersion, optimize, runs, usingWorker } = newCompConfig const { currentCompilerUrl, evmVersion, optimize, runs, usingWorker } = newCompConfig
compiler = new RemixCompiler(importFileCb) compiler = new RemixCompiler(importFileCb)

@ -79,6 +79,9 @@ export function deployAll (compileResult: compilationInterface, web3: Web3, test
console.error(err) console.error(err)
callback(err) callback(err)
}) })
}).catch((err) => {
console.error(err)
callback(err)
}) })
} }

@ -1,7 +1,6 @@
import React, { useState, useRef, useEffect, useReducer } from 'react' // eslint-disable-line import React, { useState, useRef, useEffect, useReducer } from 'react' // eslint-disable-line
import './remix-ui-home-tab.css' import './remix-ui-home-tab.css'
import JSZip from 'jszip'
import { ModalDialog } from '@remix-ui/modal-dialog' // eslint-disable-line import { ModalDialog } from '@remix-ui/modal-dialog' // eslint-disable-line
import { Toaster } from '@remix-ui/toaster' // eslint-disable-line import { Toaster } from '@remix-ui/toaster' // eslint-disable-line
import PluginButton from './components/pluginButton' // eslint-disable-line import PluginButton from './components/pluginButton' // eslint-disable-line
@ -176,53 +175,6 @@ export const RemixUiHomeTab = (props: RemixUiHomeTabProps) => {
const startPluginManager = async () => { const startPluginManager = async () => {
plugin.verticalIcons.select('pluginManager') plugin.verticalIcons.select('pluginManager')
} }
const saveAs = (blob, name) => {
const node = document.createElement('a')
node.download = name
node.rel = 'noopener'
node.href = URL.createObjectURL(blob)
setTimeout(function () { URL.revokeObjectURL(node.href) }, 4E4) // 40s
setTimeout(function () {
try {
node.dispatchEvent(new MouseEvent('click'))
} catch (e) {
const evt = document.createEvent('MouseEvents')
evt.initMouseEvent('click', true, true, window, 0, 0, 0, 80,
20, false, false, false, false, 0, null)
node.dispatchEvent(evt)
}
}, 0) // 40s
}
const downloadFiles = async () => {
try {
plugin.call('notification', 'toast', 'preparing files for download, please wait..')
const zip = new JSZip()
zip.file("readme.txt", "This is a Remix backup file.\nThis zip should be used by the restore backup tool in Remix.\nThe .workspaces directory contains your workspaces.")
const browserProvider = fileManager.getProvider('browser')
await browserProvider.copyFolderToJson('/', ({ path, content }) => {
zip.file(path, content)
})
zip.generateAsync({ type: 'blob' }).then(function (blob) {
const today = new Date()
const date = today.getFullYear() + '-' + (today.getMonth() + 1) + '-' + today.getDate()
const time = today.getHours() + 'h' + today.getMinutes() + 'min'
saveAs(blob, `remix-backup-at-${time}-${date}.zip`)
_paq.push(['trackEvent', 'Backup', 'download', 'home'])
}).catch((e) => {
_paq.push(['trackEvent', 'Backup', 'error', e.message])
plugin.call('notification', 'toast', e.message)
})
} catch (e) {
plugin.call('notification', 'toast', e.message)
}
}
const restoreBackupZip = async () => {
await plugin.appManager.activatePlugin(['restorebackupzip'])
await plugin.call('mainPanel', 'showContent', 'restorebackupzip')
plugin.verticalIcons.select('restorebackupzip')
_paq.push(['trackEvent', 'pluginManager', 'userActivate', 'restorebackupzip'])
}
const showFullMessage = (title: string, loadItem: string, examples: Array<string>) => { const showFullMessage = (title: string, loadItem: string, examples: Array<string>) => {
setState(prevState => { setState(prevState => {
@ -336,14 +288,6 @@ export const RemixUiHomeTab = (props: RemixUiHomeTabProps) => {
<i className="mr-1 far fa-hdd"></i> <i className="mr-1 far fa-hdd"></i>
<label className="ml-1 remixui_home_text" onClick={() => connectToLocalhost()}>Connect to Localhost</label> <label className="ml-1 remixui_home_text" onClick={() => connectToLocalhost()}>Connect to Localhost</label>
</p> </p>
<p className="mb-1">
<i className="mr-1 far fa-download"></i>
<label className="ml-1 remixui_home_text" onClick={() => downloadFiles()}>Download Backup</label>
</p>
<p className="mb-1">
<i className="mr-1 far fa-upload"></i>
<label className="ml-1 remixui_home_text" onClick={() => restoreBackupZip()}>Restore Backup</label>
</p>
<p className="mt-3 mb-0"><label>LOAD FROM:</label></p> <p className="mt-3 mb-0"><label>LOAD FROM:</label></p>
<div className="btn-group"> <div className="btn-group">
<button className="btn mr-1 btn-secondary" data-id="landingPageImportFromGistButton" onClick={() => importFromGist()}>Gist</button> <button className="btn mr-1 btn-secondary" data-id="landingPageImportFromGistButton" onClick={() => importFromGist()}>Gist</button>

@ -83,6 +83,14 @@ const setupEvents = () => {
setExecutionContext(env) setExecutionContext(env)
}) })
plugin.on('udapp', 'clearAllInstancesReducer', () => {
dispatch(clearAllInstances())
})
plugin.on('udapp', 'addInstanceReducer', (address, abi, name) => {
addInstance({ abi, address, name })
})
plugin.on('filePanel', 'setWorkspace', () => { plugin.on('filePanel', 'setWorkspace', () => {
dispatch(resetUdapp()) dispatch(resetUdapp())
resetAndInit() resetAndInit()
@ -549,7 +557,7 @@ export const updateTxFeeContent = (content: string) => {
dispatch(setTxFeeContent(content)) dispatch(setTxFeeContent(content))
} }
const addInstance = (instance: { contractData?: ContractData, address: string, name: string, abi?: any, decodedResponse?: Record<number, any> }) => { export const addInstance = (instance: { contractData?: ContractData, address: string, name: string, abi?: any, decodedResponse?: Record<number, any> }) => {
instance.decodedResponse = {} instance.decodedResponse = {}
dispatch(addNewInstance(instance)) dispatch(addNewInstance(instance))
} }

@ -462,7 +462,7 @@ export const RemixUiTerminal = (props: RemixUiTerminalProps) => {
title="If checked Remix will listen on all transactions mined in the current environment and not only transactions created by you" title="If checked Remix will listen on all transactions mined in the current environment and not only transactions created by you"
htmlFor="listenNetworkCheck" htmlFor="listenNetworkCheck"
> >
listen on network listen on all transactions
</label> </label>
</div> </div>
<div className="remix_ui_terminal_search d-flex align-items-center h-100"> <div className="remix_ui_terminal_search d-flex align-items-center h-100">

@ -5,13 +5,14 @@ import { customAction } from '@remixproject/plugin-api/lib/file-system/file-pane
import { displayNotification, displayPopUp, fetchDirectoryError, fetchDirectoryRequest, fetchDirectorySuccess, focusElement, fsInitializationCompleted, hidePopUp, removeInputFieldSuccess, setCurrentWorkspace, setExpandPath, setMode, setWorkspaces } from './payload' import { displayNotification, displayPopUp, fetchDirectoryError, fetchDirectoryRequest, fetchDirectorySuccess, focusElement, fsInitializationCompleted, hidePopUp, removeInputFieldSuccess, setCurrentWorkspace, setExpandPath, setMode, setWorkspaces } from './payload'
import { listenOnPluginEvents, listenOnProviderEvents } from './events' import { listenOnPluginEvents, listenOnProviderEvents } from './events'
import { createWorkspaceTemplate, getWorkspaces, loadWorkspacePreset, setPlugin } from './workspace' import { createWorkspaceTemplate, getWorkspaces, loadWorkspacePreset, setPlugin } from './workspace'
import { QueryParams } from '@remix-project/remix-lib'
import JSZip from 'jszip'
export * from './events' export * from './events'
export * from './workspace' export * from './workspace'
import { QueryParams } from '@remix-project/remix-lib'
const queryParams = new QueryParams() const queryParams = new QueryParams()
const _paq = window._paq = window._paq || []
let plugin, dispatch: React.Dispatch<any> let plugin, dispatch: React.Dispatch<any>
@ -269,6 +270,40 @@ export const handleExpandPath = (paths: string[]) => {
dispatch(setExpandPath(paths)) dispatch(setExpandPath(paths))
} }
export const handleDownloadFiles = async () => {
try {
plugin.call('notification', 'toast', 'preparing files for download, please wait..')
const zip = new JSZip()
zip.file("readme.txt", "This is a Remix backup file.\nThis zip should be used by the restore backup tool in Remix.\nThe .workspaces directory contains your workspaces.")
const browserProvider = plugin.fileManager.getProvider('browser')
await browserProvider.copyFolderToJson('/', ({ path, content }) => {
zip.file(path, content)
})
zip.generateAsync({ type: 'blob' }).then(function (blob) {
const today = new Date()
const date = today.getFullYear() + '-' + (today.getMonth() + 1) + '-' + today.getDate()
const time = today.getHours() + 'h' + today.getMinutes() + 'min'
saveAs(blob, `remix-backup-at-${time}-${date}.zip`)
_paq.push(['trackEvent', 'Backup', 'download', 'home'])
}).catch((e) => {
_paq.push(['trackEvent', 'Backup', 'error', e.message])
plugin.call('notification', 'toast', e.message)
})
} catch (e) {
plugin.call('notification', 'toast', e.message)
}
}
export const restoreBackupZip = async () => {
await plugin.appManager.activatePlugin(['restorebackupzip'])
await plugin.call('mainPanel', 'showContent', 'restorebackupzip')
plugin.verticalIcons.select('restorebackupzip')
_paq.push(['trackEvent', 'pluginManager', 'userActivate', 'restorebackupzip'])
}
const packageGistFiles = async (directory) => { const packageGistFiles = async (directory) => {
const workspaceProvider = plugin.fileProviders.workspace const workspaceProvider = plugin.fileProviders.workspace
const isFile = await workspaceProvider.isFile(directory) const isFile = await workspaceProvider.isFile(directory)
@ -344,3 +379,23 @@ const getOriginalFiles = async (id) => {
const data = await res.json() const data = await res.json()
return data.files || [] return data.files || []
} }
const saveAs = (blob, name) => {
const node = document.createElement('a')
node.download = name
node.rel = 'noopener'
node.href = URL.createObjectURL(blob)
setTimeout(function () { URL.revokeObjectURL(node.href) }, 4E4) // 40s
setTimeout(function () {
try {
node.dispatchEvent(new MouseEvent('click'))
} catch (e) {
const evt = document.createEvent('MouseEvents')
evt.initMouseEvent('click', true, true, window, 0, 0, 0, 80,
20, false, false, false, false, 0, null)
node.dispatchEvent(evt)
}
}, 0) // 40s
}

@ -27,5 +27,7 @@ export const FileSystemContext = createContext<{
dispatchRunScript: (path: string) => Promise<void>, dispatchRunScript: (path: string) => Promise<void>,
dispatchEmitContextMenuEvent: (cmd: customAction) => Promise<void>, dispatchEmitContextMenuEvent: (cmd: customAction) => Promise<void>,
dispatchHandleClickFile: (path: string, type: 'file' | 'folder' | 'gist') => Promise<void> dispatchHandleClickFile: (path: string, type: 'file' | 'folder' | 'gist') => Promise<void>
dispatchHandleExpandPath: (paths: string[]) => Promise<void> dispatchHandleExpandPath: (paths: string[]) => Promise<void>,
dispatchHandleDownloadFiles: () => Promise<void>,
dispatchHandleRestoreBackup: () => Promise<void>
}>(null) }>(null)

@ -58,4 +58,7 @@
.remixui_menuicon { .remixui_menuicon {
padding-right : 10px; padding-right : 10px;
} }
.remixui_menuicon:hover {
transform: scale(1.3);
}

@ -5,7 +5,7 @@ import { Toaster } from '@remix-ui/toaster' // eslint-disable-line
// eslint-disable-next-line @typescript-eslint/no-unused-vars // eslint-disable-next-line @typescript-eslint/no-unused-vars
import { FileSystemContext } from '../contexts' import { FileSystemContext } from '../contexts'
import { browserReducer, browserInitialState } from '../reducers/workspace' import { browserReducer, browserInitialState } from '../reducers/workspace'
import { initWorkspace, fetchDirectory, removeInputField, deleteWorkspace, clearPopUp, publishToGist, createNewFile, setFocusElement, createNewFolder, deletePath, renamePath, copyFile, copyFolder, runScript, emitContextMenuEvent, handleClickFile, handleExpandPath, addInputField, createWorkspace, fetchWorkspaceDirectory, renameWorkspace, switchToWorkspace, uploadFile } from '../actions' import { initWorkspace, fetchDirectory, removeInputField, deleteWorkspace, clearPopUp, publishToGist, createNewFile, setFocusElement, createNewFolder, deletePath, renamePath, copyFile, copyFolder, runScript, emitContextMenuEvent, handleClickFile, handleExpandPath, addInputField, createWorkspace, fetchWorkspaceDirectory, renameWorkspace, switchToWorkspace, uploadFile, handleDownloadFiles, restoreBackupZip } from '../actions'
import { Modal, WorkspaceProps } from '../types' import { Modal, WorkspaceProps } from '../types'
// eslint-disable-next-line @typescript-eslint/no-unused-vars // eslint-disable-next-line @typescript-eslint/no-unused-vars
import { Workspace } from '../remix-ui-workspace' import { Workspace } from '../remix-ui-workspace'
@ -115,6 +115,14 @@ export const FileSystemProvider = (props: WorkspaceProps) => {
await handleExpandPath(paths) await handleExpandPath(paths)
} }
const dispatchHandleDownloadFiles = async () => {
await handleDownloadFiles()
}
const dispatchHandleRestoreBackup = async () => {
await restoreBackupZip()
}
useEffect(() => { useEffect(() => {
dispatchInitWorkspace() dispatchInitWorkspace()
}, []) }, [])
@ -214,7 +222,9 @@ export const FileSystemProvider = (props: WorkspaceProps) => {
dispatchRunScript, dispatchRunScript,
dispatchEmitContextMenuEvent, dispatchEmitContextMenuEvent,
dispatchHandleClickFile, dispatchHandleClickFile,
dispatchHandleExpandPath dispatchHandleExpandPath,
dispatchHandleDownloadFiles,
dispatchHandleRestoreBackup
} }
return ( return (
<FileSystemContext.Provider value={value}> <FileSystemContext.Provider value={value}>

@ -50,6 +50,22 @@ export function Workspace () {
global.modal('Delete Current Workspace', 'Are you sure to delete the current workspace?', 'OK', onFinishDeleteWorkspace, '') global.modal('Delete Current Workspace', 'Are you sure to delete the current workspace?', 'OK', onFinishDeleteWorkspace, '')
} }
const downloadWorkspaces = async () => {
try {
await global.dispatchHandleDownloadFiles()
} catch (e) {
console.error(e)
}
}
const restoreBackup = async () => {
try {
await global.dispatchHandleRestoreBackup()
} catch (e) {
console.error(e)
}
}
const onFinishRenameWorkspace = async () => { const onFinishRenameWorkspace = async () => {
if (workspaceRenameInput.current === undefined) return if (workspaceRenameInput.current === undefined) return
// @ts-ignore: Object is possibly 'null'. // @ts-ignore: Object is possibly 'null'.
@ -156,9 +172,30 @@ export function Workspace () {
e.stopPropagation() e.stopPropagation()
deleteCurrentWorkspace() deleteCurrentWorkspace()
}} }}
className='fas fa-trash' className='fas fa-trash remixui_menuicon'
title='Delete'> title='Delete'>
</span> </span>
<span
hidden={currentWorkspace === NO_WORKSPACE}
id='workspacesDownload'
data-id='workspacesDownload'
onClick={(e) => {
e.stopPropagation()
downloadWorkspaces()
}}
className='far fa-download remixui_menuicon'
title='Download Workspaces'>
</span>
<span
id='workspacesRestore'
data-id='workspacesRestore'
onClick={(e) => {
e.stopPropagation()
restoreBackup()
}}
className='far fa-upload remixui_menuicon'
title='Restore Workspaces Backup'>
</span>
</span> </span>
<select id="workspacesSelect" value={currentWorkspace} data-id="workspacesSelect" onChange={(e) => switchWorkspace(e.target.value)} className="form-control custom-select"> <select id="workspacesSelect" value={currentWorkspace} data-id="workspacesSelect" onChange={(e) => switchWorkspace(e.target.value)} className="form-control custom-select">
{ {

@ -135,7 +135,7 @@ export class SlitherClient extends PluginClient {
} }
const solcRemaps = remaps ? `--solc-remaps "${remaps}"` : '' const solcRemaps = remaps ? `--solc-remaps "${remaps}"` : ''
const outputFile: string = 'remix-slitherReport_' + Math.floor(Date.now() / 1000) + '.json' const outputFile = 'remix-slither-report.json'
const cmd = `slither ${filePath} ${solcArgs} ${solcRemaps} --json ${outputFile}` const cmd = `slither ${filePath} ${solcArgs} ${solcRemaps} --json ${outputFile}`
console.log('\x1b[32m%s\x1b[0m', '[Slither Analysis]: Running Slither...') console.log('\x1b[32m%s\x1b[0m', '[Slither Analysis]: Running Slither...')
// Added `stdio: 'ignore'` as for contract with NPM imports analysis which is exported in 'stderr' // Added `stdio: 'ignore'` as for contract with NPM imports analysis which is exported in 'stderr'
@ -149,9 +149,6 @@ export class SlitherClient extends PluginClient {
if (existsSync(outputFileAbsPath)) { if (existsSync(outputFileAbsPath)) {
let report = readFileSync(outputFileAbsPath, 'utf8') let report = readFileSync(outputFileAbsPath, 'utf8')
report = JSON.parse(report) report = JSON.parse(report)
unlink(outputFileAbsPath, (err) => {
if (err) console.log(err)
})
if (report['success']) { if (report['success']) {
response['status'] = true response['status'] = true
if (!report['results'] || !report['results'].detectors || !report['results'].detectors.length) { if (!report['results'] || !report['results'].detectors || !report['results'].detectors.length) {

Loading…
Cancel
Save