diff --git a/.eslintrc.json b/.eslintrc.json index 1dc764a010..da9935daf2 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -48,7 +48,7 @@ "no-empty": "off", "jsx-a11y/anchor-is-valid": "off", "@typescript-eslint/no-inferrable-types": "off", - "indent": ["error", 2] + "indent": ["error", 1] } }, { @@ -60,7 +60,7 @@ "plugin:@nrwl/nx/javascript" ], "rules": { - "indent": ["error", 2] + "indent": ["error", 1] } } ], diff --git a/apps/remix-ide/background.js b/apps/remix-ide/background.js index 8b9269f940..192777565e 100644 --- a/apps/remix-ide/background.js +++ b/apps/remix-ide/background.js @@ -2,9 +2,9 @@ 'use strict' chrome.browserAction.onClicked.addListener(function (tab) { - chrome.storage.sync.set({ 'chrome-app-sync': true }) + chrome.storage.sync.set({ 'chrome-app-sync': true }) - chrome.tabs.create({ 'url': chrome.extension.getURL('index.html') }, function (tab) { - // tab opened - }) + chrome.tabs.create({ 'url': chrome.extension.getURL('index.html') }, function (tab) { + // tab opened + }) }) diff --git a/apps/remix-ide/ci/download_e2e_assets.js b/apps/remix-ide/ci/download_e2e_assets.js index 99a089174f..f570cff54a 100644 --- a/apps/remix-ide/ci/download_e2e_assets.js +++ b/apps/remix-ide/ci/download_e2e_assets.js @@ -5,8 +5,8 @@ const { exit } = require('process'); const child = child_process.spawnSync('grep -r --include="*.json" --include="*.ts" --include="*.tsx" "+commit" apps/**/* libs/**/*', [], { encoding: 'utf8', cwd: process.cwd(), shell: true }); if (child.error) { - console.log("ERROR: ", child); - exit(1); + console.log("ERROR: ", child); + exit(1); } @@ -15,63 +15,63 @@ let soljson =[]; const quotedVersionsRegex = /['"v]\d*\.\d*\.\d*\+commit\.[\d\w]*/g; let quotedVersionsRegexMatch = child.stdout.match(quotedVersionsRegex) if(quotedVersionsRegexMatch){ - let soljson2 = quotedVersionsRegexMatch.map((item) => item.replace('\'', 'v').replace('"', 'v')) - console.log('non nightly soljson versions found: ', soljson2); - if(soljson2) soljson = soljson.concat(soljson2); + let soljson2 = quotedVersionsRegexMatch.map((item) => item.replace('\'', 'v').replace('"', 'v')) + console.log('non nightly soljson versions found: ', soljson2); + if(soljson2) soljson = soljson.concat(soljson2); } const nightlyVersionsRegex = /\d*\.\d*\.\d-nightly.*\+commit\.[\d\w]*/g const nightlyVersionsRegexMatch = child.stdout.match(nightlyVersionsRegex) if(nightlyVersionsRegexMatch){ - let soljson3 = nightlyVersionsRegexMatch.map((item) => 'v' + item); - console.log('nightly soljson versions found: ', soljson3); - if(soljson3) soljson = soljson.concat(soljson3); + let soljson3 = nightlyVersionsRegexMatch.map((item) => 'v' + item); + console.log('nightly soljson versions found: ', soljson3); + if(soljson3) soljson = soljson.concat(soljson3); } if (soljson) { - // filter out duplicates - soljson = soljson.filter((item, index) => soljson.indexOf(item) === index); + // filter out duplicates + soljson = soljson.filter((item, index) => soljson.indexOf(item) === index); - // manually add some versions - soljson.push('v0.7.6+commit.7338295f'); + // manually add some versions + soljson.push('v0.7.6+commit.7338295f'); - console.log('soljson versions found: ', soljson, soljson.length); + console.log('soljson versions found: ', soljson, soljson.length); - for (let i = 0; i < soljson.length; i++) { - const version = soljson[i]; - if (version) { - let url = '' - - // if nightly - if (version.includes('nightly')) { - url = `https://binaries.soliditylang.org/bin/soljson-${version}.js`; - }else{ - url = `https://binaries.soliditylang.org/wasm/soljson-${version}.js`; - } - - const dir = './dist/apps/remix-ide/assets/js/soljson'; - if (!fs.existsSync(dir)) { - fs.mkdirSync(dir); - } - - const path = `./dist/apps/remix-ide/assets/js/soljson/soljson-${version}.js`; - // check if the file exists - const exists = fs.existsSync(path); - if (!exists) { - console.log('URL:', url) - try { - // use curl to download the file - child_process.exec(`curl -o ${path} ${url}`, { encoding: 'utf8', cwd: process.cwd(), shell: true }) - } catch (e) { - console.log('Failed to download soljson' + version + ' from ' + url) - } - } + for (let i = 0; i < soljson.length; i++) { + const version = soljson[i]; + if (version) { + let url = '' + + // if nightly + if (version.includes('nightly')) { + url = `https://binaries.soliditylang.org/bin/soljson-${version}.js`; + }else{ + url = `https://binaries.soliditylang.org/wasm/soljson-${version}.js`; + } + + const dir = './dist/apps/remix-ide/assets/js/soljson'; + if (!fs.existsSync(dir)) { + fs.mkdirSync(dir); + } + + const path = `./dist/apps/remix-ide/assets/js/soljson/soljson-${version}.js`; + // check if the file exists + const exists = fs.existsSync(path); + if (!exists) { + console.log('URL:', url) + try { + // use curl to download the file + child_process.exec(`curl -o ${path} ${url}`, { encoding: 'utf8', cwd: process.cwd(), shell: true }) + } catch (e) { + console.log('Failed to download soljson' + version + ' from ' + url) + } + } - } + } - } + } } diff --git a/apps/remix-ide/ci/lint-targets.js b/apps/remix-ide/ci/lint-targets.js index 44e71d8c0f..d99137e6c8 100644 --- a/apps/remix-ide/ci/lint-targets.js +++ b/apps/remix-ide/ci/lint-targets.js @@ -5,15 +5,15 @@ const file = fs.readFileSync('projects.json') const projects = JSON.parse(file) console.log(Object.keys(projects.graph.nodes)) for(let node of Object.keys(projects.graph.nodes)){ - if(projects.graph.nodes[node].data.targets.lint){ - console.log(projects.graph.nodes[node].data.name) - const result = spawnSync('yarn', ['lint', projects.graph.nodes[node].data.name]) - if(result.status == 0){ - console.log('success') - }else{ - console.log(result.stdout.toString()) - console.log(result.stderr.toString()) - exit(1) - } + if(projects.graph.nodes[node].data.targets.lint){ + console.log(projects.graph.nodes[node].data.name) + const result = spawnSync('yarn', ['lint', projects.graph.nodes[node].data.name]) + if(result.status == 0){ + console.log('success') + }else{ + console.log(result.stdout.toString()) + console.log(result.stderr.toString()) + exit(1) } + } } \ No newline at end of file diff --git a/apps/remix-ide/ci/makeMockCompiler.js b/apps/remix-ide/ci/makeMockCompiler.js index 601f99e8ac..41a1ce181d 100644 --- a/apps/remix-ide/ci/makeMockCompiler.js +++ b/apps/remix-ide/ci/makeMockCompiler.js @@ -7,82 +7,82 @@ var defaultVersion = 'soljson-v0.8.18+commit.87f61d96.js' const path = require('path') compiler.loadRemoteVersion(defaultVersion, (error, solcSnapshot) => { - console.log('solcSnapshot: ', solcSnapshot) - if (error) console.log(error) - var compilationResult = {} + console.log('solcSnapshot: ', solcSnapshot) + if (error) console.log(error) + var compilationResult = {} const testsFolder = path.resolve(__dirname + '/../test-browser/tests/') + '/' // eslint-disable-line - gatherCompilationResults(testsFolder, compilationResult, solcSnapshot) - replaceSolCompiler(compilationResult, solcSnapshot) + gatherCompilationResults(testsFolder, compilationResult, solcSnapshot) + replaceSolCompiler(compilationResult, solcSnapshot) }) function gatherCompilationResults (dir, compilationResult, solcSnapshot) { - var filenames = fs.readdirSync(dir, 'utf8') - filenames.map(function (item, i) { - if (item.endsWith('.js')) { - var testDef = require(dir + item) - if ('@sources' in testDef) { - var sources = testDef['@sources']() - for (var files in sources) { - compile(solcSnapshot, sources[files], true, function (result) { - compilationResult[result.key] = result - }) - compile(solcSnapshot, sources[files], false, function (result) { - compilationResult[result.key] = result - }) - } - } + var filenames = fs.readdirSync(dir, 'utf8') + filenames.map(function (item, i) { + if (item.endsWith('.js')) { + var testDef = require(dir + item) + if ('@sources' in testDef) { + var sources = testDef['@sources']() + for (var files in sources) { + compile(solcSnapshot, sources[files], true, function (result) { + compilationResult[result.key] = result + }) + compile(solcSnapshot, sources[files], false, function (result) { + compilationResult[result.key] = result + }) } - }) - return compilationResult + } + } + }) + return compilationResult } function compile (solcSnapshot, source, optimization, runs, addCompilationResult) { - var missingInputs = [] - try { - var input = compilerInput(source, {optimize: optimization, runs: runs}) - var result = solcSnapshot.compileStandardWrapper(input, function (path) { - missingInputs.push(path) - }) - input = input.replace(/(\t)|(\n)|(\\n)|( )/g, '') - } catch (e) { - console.log(e) - } - if (result && (result.error || (result.errors && result.errors.length > 0))) { - console.log(result.error, result.errors) - } - if (result) { - console.log(result.error, result.errors) - } - var ret = { - key: input, - source: source, - optimization: optimization, - missingInputs: missingInputs, - result: result - } - addCompilationResult(ret) + var missingInputs = [] + try { + var input = compilerInput(source, {optimize: optimization, runs: runs}) + var result = solcSnapshot.compileStandardWrapper(input, function (path) { + missingInputs.push(path) + }) + input = input.replace(/(\t)|(\n)|(\\n)|( )/g, '') + } catch (e) { + console.log(e) + } + if (result && (result.error || (result.errors && result.errors.length > 0))) { + console.log(result.error, result.errors) + } + if (result) { + console.log(result.error, result.errors) + } + var ret = { + key: input, + source: source, + optimization: optimization, + missingInputs: missingInputs, + result: result + } + addCompilationResult(ret) } function replaceSolCompiler (results, solcSnapshot) { const compilerPath = path.resolve(__dirname + '/../test-browser/mockcompiler/compiler.js') // eslint-disable-line const soljsonPath = path.resolve(__dirname + '/../soljson.js') // eslint-disable-line - fs.readFile(compilerPath, 'utf8', function (error, data) { - if (error) { - console.log(error) - process.exit(1) - return - } - console.log(solcSnapshot.version()) - data = data + '\n\nvar mockCompilerVersion = \'' + solcSnapshot.version() + '\'' - data = data + '\n\nvar mockData = ' + JSON.stringify(results) + ';\n' - fs.writeFile(soljsonPath, data, 'utf8', function (error) { - if (error) { - console.log(error) - process.exit(1) - return - } - }) + fs.readFile(compilerPath, 'utf8', function (error, data) { + if (error) { + console.log(error) + process.exit(1) + return + } + console.log(solcSnapshot.version()) + data = data + '\n\nvar mockCompilerVersion = \'' + solcSnapshot.version() + '\'' + data = data + '\n\nvar mockData = ' + JSON.stringify(results) + ';\n' + fs.writeFile(soljsonPath, data, 'utf8', function (error) { + if (error) { + console.log(error) + process.exit(1) + return + } }) + }) } diff --git a/apps/remix-ide/ci/sauceDisconnect.js b/apps/remix-ide/ci/sauceDisconnect.js index a8069a669e..4d45b1e52d 100644 --- a/apps/remix-ide/ci/sauceDisconnect.js +++ b/apps/remix-ide/ci/sauceDisconnect.js @@ -7,69 +7,69 @@ var accessKey = process.argv[3] var tunnelName = process.argv[4] function removeTunnel () { - const requestPath = `/rest/v1/${userName}/tunnels` - console.log(requestPath) - callSauce(requestPath, 'GET', function (error, result) { - if (error) { + const requestPath = `/rest/v1/${userName}/tunnels` + console.log(requestPath) + callSauce(requestPath, 'GET', function (error, result) { + if (error) { + console.log(error) + } else { + var data = JSON.parse(result) + for (var k in data) { + retrieveTunnel(data[k], function (error, result) { + if (error) { console.log(error) - } else { - var data = JSON.parse(result) - for (var k in data) { - retrieveTunnel(data[k], function (error, result) { - if (error) { - console.log(error) - } else if (result.identtifier === tunnelName) { - deleteTunnel(result.id, function () { - console.log('tunnel deleted ' + data[k] + ' ' + tunnelName) - }) - } - }) - } - } - }) + } else if (result.identtifier === tunnelName) { + deleteTunnel(result.id, function () { + console.log('tunnel deleted ' + data[k] + ' ' + tunnelName) + }) + } + }) + } + } + }) } function retrieveTunnel (tunnelid, callback) { - const requestPath = `/rest/v1/${userName}/tunnels/${tunnelid}` - callSauce(requestPath, 'GET', function (error, result) { - if (error) { - callback(error) - } else { - callback(null, {'identtifier': JSON.parse(result).tunnel_identifier, 'id': tunnelid}) - } - }) + const requestPath = `/rest/v1/${userName}/tunnels/${tunnelid}` + callSauce(requestPath, 'GET', function (error, result) { + if (error) { + callback(error) + } else { + callback(null, {'identtifier': JSON.parse(result).tunnel_identifier, 'id': tunnelid}) + } + }) } function deleteTunnel (tunnelid, callback) { - const requestPath = `/rest/v1/${userName}/tunnels/${tunnelid}` - callSauce(requestPath, 'DELETE', callback) + const requestPath = `/rest/v1/${userName}/tunnels/${tunnelid}` + callSauce(requestPath, 'DELETE', callback) } function callSauce (requestPath, type, callback) { - function responseCallback (res) { - res.setEncoding('utf8') - console.log('Response: ', res.statusCode, JSON.stringify(res.headers)) - res.on('data', function onData (chunk) { - console.log('BODY: ' + chunk) - callback(null, chunk) - }) - // eslint-disable-next-line @typescript-eslint/no-empty-function - res.on('end', function onEnd () {}) - } + function responseCallback (res) { + res.setEncoding('utf8') + console.log('Response: ', res.statusCode, JSON.stringify(res.headers)) + res.on('data', function onData (chunk) { + console.log('BODY: ' + chunk) + callback(null, chunk) + }) + // eslint-disable-next-line @typescript-eslint/no-empty-function + res.on('end', function onEnd () {}) + } - var req = https.request({ - hostname: 'saucelabs.com', - path: requestPath, - method: type, - auth: userName + ':' + accessKey - }, responseCallback) + var req = https.request({ + hostname: 'saucelabs.com', + path: requestPath, + method: type, + auth: userName + ':' + accessKey + }, responseCallback) - req.on('error', function onError (e) { - console.log('problem with request: ' + e.message) - callback(e.message) - }) - req.write('') - req.end() + req.on('error', function onError (e) { + console.log('problem with request: ' + e.message) + callback(e.message) + }) + req.write('') + req.end() } removeTunnel() diff --git a/apps/remix-ide/ci/splice_tests.js b/apps/remix-ide/ci/splice_tests.js index 57cc1db31b..b83da90039 100644 --- a/apps/remix-ide/ci/splice_tests.js +++ b/apps/remix-ide/ci/splice_tests.js @@ -7,21 +7,21 @@ let args = process.argv.slice(2) const jobsize = args[0] || 10; const job = args[1] || 0; exec(cmd, (error, stdout, stderr) => { - if (error) { - console.error(`error: ${error.message}`); - return; - } + if (error) { + console.error(`error: ${error.message}`); + return; + } - if (stderr) { - console.error(`stderr: ${stderr}`); - return; - } + if (stderr) { + console.error(`stderr: ${stderr}`); + return; + } - let files = stdout.split('\n').filter(f => f.includes('.test')).map(f => f.replace('dist/apps/remix-ide-e2e/src/tests/', '')).map(f => f.replace('.js', '')) - let splitIndex = Math.ceil(files.length / jobsize); - const parts = [] - for (let i = 0; i < jobsize; i++) { - parts.push(files.slice(i * splitIndex, (i + 1) * splitIndex)) - } - console.log(parts[job].join('\n')) + let files = stdout.split('\n').filter(f => f.includes('.test')).map(f => f.replace('dist/apps/remix-ide-e2e/src/tests/', '')).map(f => f.replace('.js', '')) + let splitIndex = Math.ceil(files.length / jobsize); + const parts = [] + for (let i = 0; i < jobsize; i++) { + parts.push(files.slice(i * splitIndex, (i + 1) * splitIndex)) + } + console.log(parts[job].join('\n')) }); diff --git a/apps/remix-ide/src/app.js b/apps/remix-ide/src/app.js index 27e2e5de88..8e9db0301f 100644 --- a/apps/remix-ide/src/app.js +++ b/apps/remix-ide/src/app.js @@ -75,436 +75,436 @@ const Terminal = require('./app/panels/terminal') const { TabProxy } = require('./app/panels/tab-proxy.js') class AppComponent { - constructor() { - this.appManager = new RemixAppManager({}) - this.queryParams = new QueryParams() - this._components = {} - // setup storage - const configStorage = new Storage('config-v0.8:') - - // load app config - const config = new Config(configStorage) - Registry.getInstance().put({ api: config, name: 'config' }) - - // load file system - this._components.filesProviders = {} - this._components.filesProviders.browser = new FileProvider('browser') - Registry.getInstance().put({ - api: this._components.filesProviders.browser, - name: 'fileproviders/browser' - }) - this._components.filesProviders.localhost = new RemixDProvider( - this.appManager - ) - Registry.getInstance().put({ - api: this._components.filesProviders.localhost, - name: 'fileproviders/localhost' - }) - this._components.filesProviders.workspace = new WorkspaceFileProvider() - Registry.getInstance().put({ - api: this._components.filesProviders.workspace, - name: 'fileproviders/workspace' - }) - - Registry.getInstance().put({ - api: this._components.filesProviders, - name: 'fileproviders' - }) + constructor() { + this.appManager = new RemixAppManager({}) + this.queryParams = new QueryParams() + this._components = {} + // setup storage + const configStorage = new Storage('config-v0.8:') + + // load app config + const config = new Config(configStorage) + Registry.getInstance().put({ api: config, name: 'config' }) + + // load file system + this._components.filesProviders = {} + this._components.filesProviders.browser = new FileProvider('browser') + Registry.getInstance().put({ + api: this._components.filesProviders.browser, + name: 'fileproviders/browser' + }) + this._components.filesProviders.localhost = new RemixDProvider( + this.appManager + ) + Registry.getInstance().put({ + api: this._components.filesProviders.localhost, + name: 'fileproviders/localhost' + }) + this._components.filesProviders.workspace = new WorkspaceFileProvider() + Registry.getInstance().put({ + api: this._components.filesProviders.workspace, + name: 'fileproviders/workspace' + }) + + Registry.getInstance().put({ + api: this._components.filesProviders, + name: 'fileproviders' + }) + } + + async run() { + // APP_MANAGER + const appManager = this.appManager + const pluginLoader = this.appManager.pluginLoader + this.panels = {} + this.workspace = pluginLoader.get() + this.engine = new RemixEngine() + this.engine.register(appManager); + + const matomoDomains = { + 'remix-alpha.ethereum.org': 27, + 'remix-beta.ethereum.org': 25, + 'remix.ethereum.org': 23, + '6fd22d6fe5549ad4c4d8fd3ca0b7816b.mod': 35 // remix desktop } - - async run() { - // APP_MANAGER - const appManager = this.appManager - const pluginLoader = this.appManager.pluginLoader - this.panels = {} - this.workspace = pluginLoader.get() - this.engine = new RemixEngine() - this.engine.register(appManager); - - const matomoDomains = { - 'remix-alpha.ethereum.org': 27, - 'remix-beta.ethereum.org': 25, - 'remix.ethereum.org': 23, - '6fd22d6fe5549ad4c4d8fd3ca0b7816b.mod': 35 // remix desktop - } - this.showMatamo = + this.showMatamo = matomoDomains[window.location.hostname] && !Registry.getInstance() - .get('config') - .api.exists('settings/matomo-analytics') - this.walkthroughService = new WalkthroughService( - appManager, - this.showMatamo - ) - - const hosts = ['127.0.0.1:8080', '192.168.0.101:8080', 'localhost:8080'] - // workaround for Electron support - if (!isElectron() && !hosts.includes(window.location.host)) { - // Oops! Accidentally trigger refresh or bookmark. - window.onbeforeunload = function () { - return 'Are you sure you want to leave?' - } - } + .get('config') + .api.exists('settings/matomo-analytics') + this.walkthroughService = new WalkthroughService( + appManager, + this.showMatamo + ) + + const hosts = ['127.0.0.1:8080', '192.168.0.101:8080', 'localhost:8080'] + // workaround for Electron support + if (!isElectron() && !hosts.includes(window.location.host)) { + // Oops! Accidentally trigger refresh or bookmark. + window.onbeforeunload = function () { + return 'Are you sure you want to leave?' + } + } - // SERVICES - // ----------------- gist service --------------------------------- - this.gistHandler = new GistHandler() - // ----------------- theme service --------------------------------- - this.themeModule = new ThemeModule() - // ----------------- locale service --------------------------------- - this.localeModule = new LocaleModule() - Registry.getInstance().put({ api: this.themeModule, name: 'themeModule' }) - Registry.getInstance().put({ api: this.localeModule, name: 'localeModule' }) - - // ----------------- editor service ---------------------------- - const editor = new Editor() // wrapper around ace editor - Registry.getInstance().put({ api: editor, name: 'editor' }) - editor.event.register('requiringToSaveCurrentfile', () => - fileManager.saveCurrentFile() - ) - - // ----------------- fileManager service ---------------------------- - const fileManager = new FileManager(editor, appManager) - Registry.getInstance().put({ api: fileManager, name: 'filemanager' }) - // ----------------- dGit provider --------------------------------- - const dGitProvider = new DGitProvider() - - // ----------------- Storage plugin --------------------------------- - const storagePlugin = new StoragePlugin() - - // ------- FILE DECORATOR PLUGIN ------------------ - const fileDecorator = new FileDecorator() - - // ------- CODE FORMAT PLUGIN ------------------ - const codeFormat = new CodeFormat() - - //----- search - const search = new SearchPlugin() - - //---------------- Solidity UML Generator ------------------------- - const solidityumlgen = new SolidityUmlGen(appManager) - - // ----------------- ContractFlattener ---------------------------- - const contractFlattener = new ContractFlattener() - - // ----------------- import content service ------------------------ - const contentImport = new CompilerImports() - - const blockchain = new Blockchain(Registry.getInstance().get('config').api) - - // ----------------- compilation metadata generation service --------- - const compilerMetadataGenerator = new CompilerMetadata() - // ----------------- compilation result service (can keep track of compilation results) ---------------------------- - const compilersArtefacts = new CompilerArtefacts() // store all the compilation results (key represent a compiler name) - Registry.getInstance().put({ - api: compilersArtefacts, - name: 'compilersartefacts' - }) - - // service which fetch contract artifacts from sourve-verify, put artifacts in remix and compile it - const fetchAndCompile = new FetchAndCompile() - // ----------------- network service (resolve network id / name) ----- - const networkModule = new NetworkModule(blockchain) - // ----------------- represent the current selected web3 provider ---- - const web3Provider = new Web3ProviderModule(blockchain) - const vmProviderCustomFork = new CustomForkVMProvider(blockchain) - const vmProviderMainnetFork = new MainnetForkVMProvider(blockchain) - const vmProviderSepoliaFork = new SepoliaForkVMProvider(blockchain) - const vmProviderGoerliFork = new GoerliForkVMProvider(blockchain) - const vmProviderShanghai = new ShanghaiVMProvider(blockchain) - const vmProviderMerge = new MergeVMProvider(blockchain) - const vmProviderBerlin = new BerlinVMProvider(blockchain) - const vmProviderLondon = new LondonVMProvider(blockchain) - const hardhatProvider = new HardhatProvider(blockchain) - const ganacheProvider = new GanacheProvider(blockchain) - const foundryProvider = new FoundryProvider(blockchain) - const externalHttpProvider = new ExternalHttpProvider(blockchain) - const trustWalletInjectedProvider = new InjectedProviderTrustWallet() - const defaultInjectedProvider = new InjectedProviderDefault - const injected0ptimismProvider = new Injected0ptimismProvider() - const injectedArbitrumOneProvider = new InjectedArbitrumOneProvider() - // ----------------- convert offset to line/column service ----------- - const offsetToLineColumnConverter = new OffsetToLineColumnConverter() - Registry.getInstance().put({ - api: offsetToLineColumnConverter, - name: 'offsettolinecolumnconverter' - }) - // ----------------- run script after each compilation results ----------- - const compileAndRun = new CompileAndRun() - // -------------------Terminal---------------------------------------- - makeUdapp(blockchain, compilersArtefacts, domEl => terminal.logHtml(domEl)) - const terminal = new Terminal( - { appManager, blockchain }, - { - getPosition: event => { - const limitUp = 36 - const limitDown = 20 - const height = window.innerHeight - let newpos = event.pageY < limitUp ? limitUp : event.pageY - newpos = newpos < height - limitDown ? newpos : height - limitDown - return height - newpos - } - } - ) - - const codeParser = new CodeParser(new AstWalker()) - const solidityScript = new SolidityScript() - - this.notification = new NotificationPlugin() - - const configPlugin = new ConfigPlugin() - this.layout = new Layout() - - const permissionHandler = new PermissionHandlerPlugin() - - this.engine.register([ - permissionHandler, - this.layout, - this.notification, - this.gistHandler, - configPlugin, - blockchain, - contentImport, - this.themeModule, - this.localeModule, - editor, - fileManager, - compilerMetadataGenerator, - compilersArtefacts, - networkModule, - offsetToLineColumnConverter, - codeParser, - fileDecorator, - codeFormat, - terminal, - web3Provider, - compileAndRun, - fetchAndCompile, - dGitProvider, - storagePlugin, - vmProviderShanghai, - vmProviderMerge, - vmProviderBerlin, - vmProviderLondon, - vmProviderSepoliaFork, - vmProviderGoerliFork, - vmProviderMainnetFork, - vmProviderCustomFork, - hardhatProvider, - ganacheProvider, - foundryProvider, - externalHttpProvider, - defaultInjectedProvider, - trustWalletInjectedProvider, - injected0ptimismProvider, - injectedArbitrumOneProvider, - this.walkthroughService, - search, - solidityumlgen, - contractFlattener, - solidityScript - ]) - - // LAYOUT & SYSTEM VIEWS - const appPanel = new MainPanel() - Registry.getInstance().put({ api: this.mainview, name: 'mainview' }) - const tabProxy = new TabProxy(fileManager, editor) - this.engine.register([appPanel, tabProxy]) - - // those views depend on app_manager - this.menuicons = new VerticalIcons() - this.sidePanel = new SidePanel() - this.hiddenPanel = new HiddenPanel() - - const pluginManagerComponent = new PluginManagerComponent( - appManager, - this.engine - ) - const filePanel = new FilePanel(appManager) - const landingPage = new LandingPage( - appManager, - this.menuicons, - fileManager, - filePanel, - contentImport - ) - this.settings = new SettingsTab( - Registry.getInstance().get('config').api, - editor, - appManager - ) - - this.engine.register([ - this.menuicons, - landingPage, - this.hiddenPanel, - this.sidePanel, - filePanel, - pluginManagerComponent, - this.settings - ]) - - // CONTENT VIEWS & DEFAULT PLUGINS - const openZeppelinProxy = new OpenZeppelinProxy(blockchain) - const linkLibraries = new LinkLibraries(blockchain) - const deployLibraries = new DeployLibraries(blockchain) - const compileTab = new CompileTab( - Registry.getInstance().get('config').api, - Registry.getInstance().get('filemanager').api - ) - const run = new RunTab( - blockchain, - Registry.getInstance().get('config').api, - Registry.getInstance().get('filemanager').api, - Registry.getInstance().get('editor').api, - filePanel, - Registry.getInstance().get('compilersartefacts').api, - networkModule, - Registry.getInstance().get('fileproviders/browser').api - ) - const analysis = new AnalysisTab() - const debug = new DebuggerTab() - const test = new TestTab( - Registry.getInstance().get('filemanager').api, - Registry.getInstance().get('offsettolinecolumnconverter').api, - filePanel, - compileTab, - appManager, - contentImport - ) - - this.engine.register([ - compileTab, - run, - debug, - analysis, - test, - filePanel.remixdHandle, - filePanel.hardhatHandle, - filePanel.foundryHandle, - filePanel.truffleHandle, - filePanel.slitherHandle, - linkLibraries, - deployLibraries, - openZeppelinProxy, - run.recorder - ]) - - this.layout.panels = { - tabs: { plugin: tabProxy, active: true }, - editor: { plugin: editor, active: true }, - main: { plugin: appPanel, active: false }, - terminal: { plugin: terminal, active: true, minimized: false } + // SERVICES + // ----------------- gist service --------------------------------- + this.gistHandler = new GistHandler() + // ----------------- theme service --------------------------------- + this.themeModule = new ThemeModule() + // ----------------- locale service --------------------------------- + this.localeModule = new LocaleModule() + Registry.getInstance().put({ api: this.themeModule, name: 'themeModule' }) + Registry.getInstance().put({ api: this.localeModule, name: 'localeModule' }) + + // ----------------- editor service ---------------------------- + const editor = new Editor() // wrapper around ace editor + Registry.getInstance().put({ api: editor, name: 'editor' }) + editor.event.register('requiringToSaveCurrentfile', () => + fileManager.saveCurrentFile() + ) + + // ----------------- fileManager service ---------------------------- + const fileManager = new FileManager(editor, appManager) + Registry.getInstance().put({ api: fileManager, name: 'filemanager' }) + // ----------------- dGit provider --------------------------------- + const dGitProvider = new DGitProvider() + + // ----------------- Storage plugin --------------------------------- + const storagePlugin = new StoragePlugin() + + // ------- FILE DECORATOR PLUGIN ------------------ + const fileDecorator = new FileDecorator() + + // ------- CODE FORMAT PLUGIN ------------------ + const codeFormat = new CodeFormat() + + //----- search + const search = new SearchPlugin() + + //---------------- Solidity UML Generator ------------------------- + const solidityumlgen = new SolidityUmlGen(appManager) + + // ----------------- ContractFlattener ---------------------------- + const contractFlattener = new ContractFlattener() + + // ----------------- import content service ------------------------ + const contentImport = new CompilerImports() + + const blockchain = new Blockchain(Registry.getInstance().get('config').api) + + // ----------------- compilation metadata generation service --------- + const compilerMetadataGenerator = new CompilerMetadata() + // ----------------- compilation result service (can keep track of compilation results) ---------------------------- + const compilersArtefacts = new CompilerArtefacts() // store all the compilation results (key represent a compiler name) + Registry.getInstance().put({ + api: compilersArtefacts, + name: 'compilersartefacts' + }) + + // service which fetch contract artifacts from sourve-verify, put artifacts in remix and compile it + const fetchAndCompile = new FetchAndCompile() + // ----------------- network service (resolve network id / name) ----- + const networkModule = new NetworkModule(blockchain) + // ----------------- represent the current selected web3 provider ---- + const web3Provider = new Web3ProviderModule(blockchain) + const vmProviderCustomFork = new CustomForkVMProvider(blockchain) + const vmProviderMainnetFork = new MainnetForkVMProvider(blockchain) + const vmProviderSepoliaFork = new SepoliaForkVMProvider(blockchain) + const vmProviderGoerliFork = new GoerliForkVMProvider(blockchain) + const vmProviderShanghai = new ShanghaiVMProvider(blockchain) + const vmProviderMerge = new MergeVMProvider(blockchain) + const vmProviderBerlin = new BerlinVMProvider(blockchain) + const vmProviderLondon = new LondonVMProvider(blockchain) + const hardhatProvider = new HardhatProvider(blockchain) + const ganacheProvider = new GanacheProvider(blockchain) + const foundryProvider = new FoundryProvider(blockchain) + const externalHttpProvider = new ExternalHttpProvider(blockchain) + const trustWalletInjectedProvider = new InjectedProviderTrustWallet() + const defaultInjectedProvider = new InjectedProviderDefault + const injected0ptimismProvider = new Injected0ptimismProvider() + const injectedArbitrumOneProvider = new InjectedArbitrumOneProvider() + // ----------------- convert offset to line/column service ----------- + const offsetToLineColumnConverter = new OffsetToLineColumnConverter() + Registry.getInstance().put({ + api: offsetToLineColumnConverter, + name: 'offsettolinecolumnconverter' + }) + // ----------------- run script after each compilation results ----------- + const compileAndRun = new CompileAndRun() + // -------------------Terminal---------------------------------------- + makeUdapp(blockchain, compilersArtefacts, domEl => terminal.logHtml(domEl)) + const terminal = new Terminal( + { appManager, blockchain }, + { + getPosition: event => { + const limitUp = 36 + const limitDown = 20 + const height = window.innerHeight + let newpos = event.pageY < limitUp ? limitUp : event.pageY + newpos = newpos < height - limitDown ? newpos : height - limitDown + return height - newpos } + } + ) + + const codeParser = new CodeParser(new AstWalker()) + const solidityScript = new SolidityScript() + + this.notification = new NotificationPlugin() + + const configPlugin = new ConfigPlugin() + this.layout = new Layout() + + const permissionHandler = new PermissionHandlerPlugin() + + this.engine.register([ + permissionHandler, + this.layout, + this.notification, + this.gistHandler, + configPlugin, + blockchain, + contentImport, + this.themeModule, + this.localeModule, + editor, + fileManager, + compilerMetadataGenerator, + compilersArtefacts, + networkModule, + offsetToLineColumnConverter, + codeParser, + fileDecorator, + codeFormat, + terminal, + web3Provider, + compileAndRun, + fetchAndCompile, + dGitProvider, + storagePlugin, + vmProviderShanghai, + vmProviderMerge, + vmProviderBerlin, + vmProviderLondon, + vmProviderSepoliaFork, + vmProviderGoerliFork, + vmProviderMainnetFork, + vmProviderCustomFork, + hardhatProvider, + ganacheProvider, + foundryProvider, + externalHttpProvider, + defaultInjectedProvider, + trustWalletInjectedProvider, + injected0ptimismProvider, + injectedArbitrumOneProvider, + this.walkthroughService, + search, + solidityumlgen, + contractFlattener, + solidityScript + ]) + + // LAYOUT & SYSTEM VIEWS + const appPanel = new MainPanel() + Registry.getInstance().put({ api: this.mainview, name: 'mainview' }) + const tabProxy = new TabProxy(fileManager, editor) + this.engine.register([appPanel, tabProxy]) + + // those views depend on app_manager + this.menuicons = new VerticalIcons() + this.sidePanel = new SidePanel() + this.hiddenPanel = new HiddenPanel() + + const pluginManagerComponent = new PluginManagerComponent( + appManager, + this.engine + ) + const filePanel = new FilePanel(appManager) + const landingPage = new LandingPage( + appManager, + this.menuicons, + fileManager, + filePanel, + contentImport + ) + this.settings = new SettingsTab( + Registry.getInstance().get('config').api, + editor, + appManager + ) + + this.engine.register([ + this.menuicons, + landingPage, + this.hiddenPanel, + this.sidePanel, + filePanel, + pluginManagerComponent, + this.settings + ]) + + // CONTENT VIEWS & DEFAULT PLUGINS + const openZeppelinProxy = new OpenZeppelinProxy(blockchain) + const linkLibraries = new LinkLibraries(blockchain) + const deployLibraries = new DeployLibraries(blockchain) + const compileTab = new CompileTab( + Registry.getInstance().get('config').api, + Registry.getInstance().get('filemanager').api + ) + const run = new RunTab( + blockchain, + Registry.getInstance().get('config').api, + Registry.getInstance().get('filemanager').api, + Registry.getInstance().get('editor').api, + filePanel, + Registry.getInstance().get('compilersartefacts').api, + networkModule, + Registry.getInstance().get('fileproviders/browser').api + ) + const analysis = new AnalysisTab() + const debug = new DebuggerTab() + const test = new TestTab( + Registry.getInstance().get('filemanager').api, + Registry.getInstance().get('offsettolinecolumnconverter').api, + filePanel, + compileTab, + appManager, + contentImport + ) + + this.engine.register([ + compileTab, + run, + debug, + analysis, + test, + filePanel.remixdHandle, + filePanel.hardhatHandle, + filePanel.foundryHandle, + filePanel.truffleHandle, + filePanel.slitherHandle, + linkLibraries, + deployLibraries, + openZeppelinProxy, + run.recorder + ]) + + this.layout.panels = { + tabs: { plugin: tabProxy, active: true }, + editor: { plugin: editor, active: true }, + main: { plugin: appPanel, active: false }, + terminal: { plugin: terminal, active: true, minimized: false } } + } - async activate() { - const queryParams = new QueryParams() - const params = queryParams.get() + async activate() { + const queryParams = new QueryParams() + const params = queryParams.get() - try { - this.engine.register(await this.appManager.registeredPlugins()) - } catch (e) { - console.log("couldn't register iframe plugins", e.message) - } - await this.appManager.activatePlugin(['layout']) - await this.appManager.activatePlugin(['notification']) - await this.appManager.activatePlugin(['editor']) - await this.appManager.activatePlugin(['permissionhandler', 'theme', 'locale', 'fileManager', 'compilerMetadata', 'compilerArtefacts', 'network', 'web3Provider', 'offsetToLineColumnConverter']) - await this.appManager.activatePlugin(['mainPanel', 'menuicons', 'tabs']) - await this.appManager.activatePlugin(['sidePanel']) // activating host plugin separately - await this.appManager.activatePlugin(['home']) - await this.appManager.activatePlugin(['settings', 'config']) - await this.appManager.activatePlugin(['hiddenPanel', 'pluginManager', 'codeParser', 'codeFormatter', 'fileDecorator', 'terminal', 'blockchain', 'fetchAndCompile', 'contentImport', 'gistHandler']) - await this.appManager.activatePlugin(['settings']) - await this.appManager.activatePlugin(['walkthrough', 'storage', 'search', 'compileAndRun', 'recorder']) - await this.appManager.activatePlugin(['solidity-script']) - - this.appManager.on( - 'filePanel', - 'workspaceInitializationCompleted', - async () => { - // for e2e tests - const loadedElement = document.createElement('span') - loadedElement.setAttribute('data-id', 'workspaceloaded') - document.body.appendChild(loadedElement) - await this.appManager.registerContextMenuItems() + try { + this.engine.register(await this.appManager.registeredPlugins()) + } catch (e) { + console.log("couldn't register iframe plugins", e.message) + } + await this.appManager.activatePlugin(['layout']) + await this.appManager.activatePlugin(['notification']) + await this.appManager.activatePlugin(['editor']) + await this.appManager.activatePlugin(['permissionhandler', 'theme', 'locale', 'fileManager', 'compilerMetadata', 'compilerArtefacts', 'network', 'web3Provider', 'offsetToLineColumnConverter']) + await this.appManager.activatePlugin(['mainPanel', 'menuicons', 'tabs']) + await this.appManager.activatePlugin(['sidePanel']) // activating host plugin separately + await this.appManager.activatePlugin(['home']) + await this.appManager.activatePlugin(['settings', 'config']) + await this.appManager.activatePlugin(['hiddenPanel', 'pluginManager', 'codeParser', 'codeFormatter', 'fileDecorator', 'terminal', 'blockchain', 'fetchAndCompile', 'contentImport', 'gistHandler']) + await this.appManager.activatePlugin(['settings']) + await this.appManager.activatePlugin(['walkthrough', 'storage', 'search', 'compileAndRun', 'recorder']) + await this.appManager.activatePlugin(['solidity-script']) + + this.appManager.on( + 'filePanel', + 'workspaceInitializationCompleted', + async () => { + // for e2e tests + const loadedElement = document.createElement('span') + loadedElement.setAttribute('data-id', 'workspaceloaded') + document.body.appendChild(loadedElement) + await this.appManager.registerContextMenuItems() + } + ) + + await this.appManager.activatePlugin(['filePanel']) + // Set workspace after initial activation + this.appManager.on('editor', 'editorMounted', () => { + if (Array.isArray(this.workspace)) { + this.appManager + .activatePlugin(this.workspace) + .then(async () => { + try { + if (params.deactivate) { + await this.appManager.deactivatePlugin( + params.deactivate.split(',') + ) + } + } catch (e) { + console.log(e) } - ) - - await this.appManager.activatePlugin(['filePanel']) - // Set workspace after initial activation - this.appManager.on('editor', 'editorMounted', () => { - if (Array.isArray(this.workspace)) { - this.appManager - .activatePlugin(this.workspace) - .then(async () => { - try { - if (params.deactivate) { - await this.appManager.deactivatePlugin( - params.deactivate.split(',') - ) - } - } catch (e) { - console.log(e) - } - if (params.code && (!params.activate || params.activate.split(',').includes('solidity'))) { - // if code is given in url we focus on solidity plugin - this.menuicons.select('solidity') - } else { - // If plugins are loaded from the URL params, we focus on the last one. - if ( - this.appManager.pluginLoader.current === 'queryParams' && + if (params.code && (!params.activate || params.activate.split(',').includes('solidity'))) { + // if code is given in url we focus on solidity plugin + this.menuicons.select('solidity') + } else { + // If plugins are loaded from the URL params, we focus on the last one. + if ( + this.appManager.pluginLoader.current === 'queryParams' && this.workspace.length > 0 - ) { - this.menuicons.select(this.workspace[this.workspace.length - 1]) - } else { - this.appManager.call('tabs', 'focus', 'home') - } - } - - if (params.call) { - const callDetails = params.call.split('//') - if (callDetails.length > 1) { - this.appManager.call('notification', 'toast', `initiating ${callDetails[0]} and calling "${callDetails[1]}" ...`) - // @todo(remove the timeout when activatePlugin is on 0.3.0) - await this.appManager.call(...callDetails).catch(console.error) - } - } - - if (params.calls) { - const calls = params.calls.split("///"); - - // call all functions in the list, one after the other - for (const call of calls) { - const callDetails = call.split("//"); - if (callDetails.length > 1) { - this.appManager.call( - "notification", - "toast", - `initiating ${callDetails[0]} and calling "${callDetails[1]}" ...` - ); - - // @todo(remove the timeout when activatePlugin is on 0.3.0) - try { - await this.appManager.call(...callDetails) - } catch (e) { - console.error(e) - } - } - } - } - }) - .catch(console.error) + ) { + this.menuicons.select(this.workspace[this.workspace.length - 1]) + } else { + this.appManager.call('tabs', 'focus', 'home') + } } - const loadedElement = document.createElement('span') - loadedElement.setAttribute('data-id', 'apploaded') - document.body.appendChild(loadedElement) - }) - // activate solidity plugin - this.appManager.activatePlugin(['solidity', 'udapp', 'deploy-libraries', 'link-libraries', 'openzeppelin-proxy']) - } + if (params.call) { + const callDetails = params.call.split('//') + if (callDetails.length > 1) { + this.appManager.call('notification', 'toast', `initiating ${callDetails[0]} and calling "${callDetails[1]}" ...`) + // @todo(remove the timeout when activatePlugin is on 0.3.0) + await this.appManager.call(...callDetails).catch(console.error) + } + } + + if (params.calls) { + const calls = params.calls.split("///"); + + // call all functions in the list, one after the other + for (const call of calls) { + const callDetails = call.split("//"); + if (callDetails.length > 1) { + this.appManager.call( + "notification", + "toast", + `initiating ${callDetails[0]} and calling "${callDetails[1]}" ...` + ); + + // @todo(remove the timeout when activatePlugin is on 0.3.0) + try { + await this.appManager.call(...callDetails) + } catch (e) { + console.error(e) + } + } + } + } + }) + .catch(console.error) + } + const loadedElement = document.createElement('span') + loadedElement.setAttribute('data-id', 'apploaded') + document.body.appendChild(loadedElement) + }) + + // activate solidity plugin + this.appManager.activatePlugin(['solidity', 'udapp', 'deploy-libraries', 'link-libraries', 'openzeppelin-proxy']) + } } export default AppComponent diff --git a/apps/remix-ide/src/app/components/hidden-panel.tsx b/apps/remix-ide/src/app/components/hidden-panel.tsx index 6aa78a4052..53591be332 100644 --- a/apps/remix-ide/src/app/components/hidden-panel.tsx +++ b/apps/remix-ide/src/app/components/hidden-panel.tsx @@ -6,45 +6,45 @@ import { RemixPluginPanel } from '@remix-ui/panel' import { PluginViewWrapper } from '@remix-ui/helper' const profile = { - name: 'hiddenPanel', - displayName: 'Hidden Panel', - description: 'Remix IDE hidden panel', - version: packageJson.version, - methods: ['addView', 'removeView'] + name: 'hiddenPanel', + displayName: 'Hidden Panel', + description: 'Remix IDE hidden panel', + version: packageJson.version, + methods: ['addView', 'removeView'] } export class HiddenPanel extends AbstractPanel { - el: HTMLElement - dispatch: React.Dispatch = () => {} - constructor () { - super(profile) - this.el = document.createElement('div') - this.el.setAttribute('class', 'pluginsContainer') - } + el: HTMLElement + dispatch: React.Dispatch = () => {} + constructor () { + super(profile) + this.el = document.createElement('div') + this.el.setAttribute('class', 'pluginsContainer') + } - addView (profile: any, view: any): void { - super.removeView(profile) - super.addView(profile, view) - this.renderComponent() - } + addView (profile: any, view: any): void { + super.removeView(profile) + super.addView(profile, view) + this.renderComponent() + } - updateComponent (state: any) { - return } plugins={state.plugins}/> - } + updateComponent (state: any) { + return } plugins={state.plugins}/> + } - setDispatch (dispatch: React.Dispatch) { - this.dispatch = dispatch - } + setDispatch (dispatch: React.Dispatch) { + this.dispatch = dispatch + } - render() { - return ( -
- ); - } + render() { + return ( +
+ ); + } - renderComponent () { - this.dispatch({ - plugins: this.plugins, - }) - } + renderComponent () { + this.dispatch({ + plugins: this.plugins, + }) + } } diff --git a/apps/remix-ide/src/app/components/main-panel.tsx b/apps/remix-ide/src/app/components/main-panel.tsx index 0eb7487b94..a8834c8994 100644 --- a/apps/remix-ide/src/app/components/main-panel.tsx +++ b/apps/remix-ide/src/app/components/main-panel.tsx @@ -5,64 +5,64 @@ import packageJson from '../../../../../package.json' import { PluginViewWrapper } from '@remix-ui/helper' const profile = { - name: 'mainPanel', - displayName: 'Main Panel', - description: 'Remix IDE main panel', - version: packageJson.version, - methods: ['addView', 'removeView', 'showContent'] + name: 'mainPanel', + displayName: 'Main Panel', + description: 'Remix IDE main panel', + version: packageJson.version, + methods: ['addView', 'removeView', 'showContent'] } export class MainPanel extends AbstractPanel { - element: HTMLDivElement - dispatch: React.Dispatch = () => {} - constructor (config) { - super(profile) - this.element = document.createElement('div') - this.element.setAttribute('data-id', 'mainPanelPluginsContainer') - this.element.setAttribute('style', 'height: 100%; width: 100%;') - // this.config = config - } + element: HTMLDivElement + dispatch: React.Dispatch = () => {} + constructor (config) { + super(profile) + this.element = document.createElement('div') + this.element.setAttribute('data-id', 'mainPanelPluginsContainer') + this.element.setAttribute('style', 'height: 100%; width: 100%;') + // this.config = config + } - setDispatch (dispatch: React.Dispatch) { - this.dispatch = dispatch - } + setDispatch (dispatch: React.Dispatch) { + this.dispatch = dispatch + } - onActivation () { - this.renderComponent() - } + onActivation () { + this.renderComponent() + } - focus (name) { - this.emit('focusChanged', name) - super.focus(name) - this.renderComponent() - } + focus (name) { + this.emit('focusChanged', name) + super.focus(name) + this.renderComponent() + } - addView (profile, view) { - super.addView(profile, view) - this.renderComponent() - } + addView (profile, view) { + super.addView(profile, view) + this.renderComponent() + } - removeView (profile) { - super.removeView(profile) - this.renderComponent() - } + removeView (profile) { + super.removeView(profile) + this.renderComponent() + } - async showContent (name) { - super.showContent(name) - this.renderComponent() - } + async showContent (name) { + super.showContent(name) + this.renderComponent() + } - renderComponent () { - this.dispatch({ - plugins: this.plugins - }) - } + renderComponent () { + this.dispatch({ + plugins: this.plugins + }) + } - render() { - return
- } + render() { + return
+ } - updateComponent (state: any) { - return } plugins={state.plugins}/> - } + updateComponent (state: any) { + return } plugins={state.plugins}/> + } } diff --git a/apps/remix-ide/src/app/components/panel.ts b/apps/remix-ide/src/app/components/panel.ts index 4dade1fbca..1dc464736e 100644 --- a/apps/remix-ide/src/app/components/panel.ts +++ b/apps/remix-ide/src/app/components/panel.ts @@ -5,58 +5,58 @@ import { PluginRecord } from '@remix-ui/panel' import EventManager from '../../lib/events' export class AbstractPanel extends HostPlugin { - events: EventEmitter - event: any - public plugins: Record = {} - constructor (profile) { - super(profile) - this.events = new EventEmitter() - this.event = new EventManager() - } + events: EventEmitter + event: any + public plugins: Record = {} + constructor (profile) { + super(profile) + this.events = new EventEmitter() + this.event = new EventManager() + } - currentFocus (): string { - return Object.values(this.plugins).find(plugin => { - return plugin.active - }).profile.name - } + currentFocus (): string { + return Object.values(this.plugins).find(plugin => { + return plugin.active + }).profile.name + } - addView (profile, view) { - if (this.plugins[profile.name]) throw new Error(`Plugin ${profile.name} already rendered`) - this.plugins[profile.name] = { - profile: profile, - view: view, - active: false, - class: 'plugItIn active' - } + addView (profile, view) { + if (this.plugins[profile.name]) throw new Error(`Plugin ${profile.name} already rendered`) + this.plugins[profile.name] = { + profile: profile, + view: view, + active: false, + class: 'plugItIn active' } + } - removeView (profile) { - this.emit('pluginDisabled', profile.name) - this.call('menuicons', 'unlinkContent', profile) - this.remove(profile.name) - } + removeView (profile) { + this.emit('pluginDisabled', profile.name) + this.call('menuicons', 'unlinkContent', profile) + this.remove(profile.name) + } - /** + /** * Remove a plugin from the panel * @param {String} name The name of the plugin to remove */ - remove (name) { - delete this.plugins[name] - } + remove (name) { + delete this.plugins[name] + } - /** + /** * Display the content of this specific plugin * @param {String} name The name of the plugin to display the content */ - showContent (name) { - if (!this.plugins[name]) throw new Error(`Plugin ${name} is not yet activated`) - Object.values(this.plugins).forEach(plugin => { - plugin.active = false - }) - this.plugins[name].active = true - } + showContent (name) { + if (!this.plugins[name]) throw new Error(`Plugin ${name} is not yet activated`) + Object.values(this.plugins).forEach(plugin => { + plugin.active = false + }) + this.plugins[name].active = true + } - focus (name) { - this.showContent(name) - } + focus (name) { + this.showContent(name) + } } diff --git a/apps/remix-ide/src/app/components/plugin-manager-component.js b/apps/remix-ide/src/app/components/plugin-manager-component.js index d658915f1b..e1d7ad11d3 100644 --- a/apps/remix-ide/src/app/components/plugin-manager-component.js +++ b/apps/remix-ide/src/app/components/plugin-manager-component.js @@ -6,145 +6,145 @@ import { PluginViewWrapper } from '@remix-ui/helper' const _paq = window._paq = window._paq || [] const profile = { - name: 'pluginManager', - displayName: 'Plugin manager', - methods: [], - events: [], - icon: 'assets/img/pluginManager.webp', - description: 'Start/stop services, modules and plugins', - kind: 'settings', - location: 'sidePanel', - documentation: 'https://remix-ide.readthedocs.io/en/latest/plugin_manager.html', - version: packageJson.version, - maintainedBy: "Remix" + name: 'pluginManager', + displayName: 'Plugin manager', + methods: [], + events: [], + icon: 'assets/img/pluginManager.webp', + description: 'Start/stop services, modules and plugins', + kind: 'settings', + location: 'sidePanel', + documentation: 'https://remix-ide.readthedocs.io/en/latest/plugin_manager.html', + version: packageJson.version, + maintainedBy: "Remix" } class PluginManagerComponent extends ViewPlugin { - constructor (appManager, engine) { - super(profile) - this.appManager = appManager - this.engine = engine - this.htmlElement = document.createElement('div') - this.htmlElement.setAttribute('id', 'pluginManager') - this.filter = '' - - this.activePlugins = [] - this.inactivePlugins = [] - this.activeProfiles = this.appManager.actives - this._paq = _paq - this.dispatch = null - this.listenOnEvent() - } - - /** + constructor (appManager, engine) { + super(profile) + this.appManager = appManager + this.engine = engine + this.htmlElement = document.createElement('div') + this.htmlElement.setAttribute('id', 'pluginManager') + this.filter = '' + + this.activePlugins = [] + this.inactivePlugins = [] + this.activeProfiles = this.appManager.actives + this._paq = _paq + this.dispatch = null + this.listenOnEvent() + } + + /** * Checks and returns true or false if plugin name * passed in exists in the actives string array in * RemixAppManager * @param {string} name name of Plugin */ - isActive = (name) =>{ - return this.appManager.actives.includes(name) - } + isActive = (name) =>{ + return this.appManager.actives.includes(name) + } - /** + /** * Delegates to method activatePlugin in * RemixAppManager to enable plugin activation * @param {string} name name of Plugin */ - activateP = (name) => { - this.appManager.activatePlugin(name) - _paq.push(['trackEvent', 'manager', 'activate', name]) - } + activateP = (name) => { + this.appManager.activatePlugin(name) + _paq.push(['trackEvent', 'manager', 'activate', name]) + } - /** + /** * Takes the name of a local plugin and does both * activation and registration * @param {Profile} pluginName * @returns {void} */ - activateAndRegisterLocalPlugin = async (localPlugin) => { - if (localPlugin) { - this.engine.register(localPlugin) - this.appManager.activatePlugin(localPlugin.profile.name) - this.getAndFilterPlugins() - localStorage.setItem('plugins/local', JSON.stringify(localPlugin.profile)) - } + activateAndRegisterLocalPlugin = async (localPlugin) => { + if (localPlugin) { + this.engine.register(localPlugin) + this.appManager.activatePlugin(localPlugin.profile.name) + this.getAndFilterPlugins() + localStorage.setItem('plugins/local', JSON.stringify(localPlugin.profile)) } + } - /** + /** * Calls and triggers the event deactivatePlugin * with with manager permission passing in the name * of the plugin * @param {string} name name of Plugin */ - deactivateP = (name) => { - this.call('manager', 'deactivatePlugin', name) - _paq.push(['trackEvent', 'manager', 'deactivate', name]) - } - - setDispatch (dispatch) { - this.dispatch = dispatch - this.renderComponent() - } - - updateComponent(state){ - return - } - - renderComponent () { - if(this.dispatch) this.dispatch({...this, activePlugins: this.activePlugins, inactivePlugins: this.inactivePlugins}) - } - - render () { - return ( -
- ); - - } - - getAndFilterPlugins = (filter) => { - this.filter = typeof filter === 'string' ? filter.toLowerCase() : this.filter - - const isFiltered = (profile) => (profile.displayName ? profile.displayName : profile.name).toLowerCase().includes(this.filter) - const isNotRequired = (profile) => !this.appManager.isRequired(profile.name) - const isNotDependent = (profile) => !this.appManager.isDependent(profile.name) - const isNotHome = (profile) => profile.name !== 'home' - const sortByName = (profileA, profileB) => { - const nameA = ((profileA.displayName) ? profileA.displayName : profileA.name).toUpperCase() - const nameB = ((profileB.displayName) ? profileB.displayName : profileB.name).toUpperCase() - return (nameA < nameB) ? -1 : (nameA > nameB) ? 1 : 0 - } - const activatedPlugins = [] - const deactivatedPlugins = [] - const tempArray = this.appManager.getAll() - .filter(isFiltered) - .filter(isNotRequired) - .filter(isNotDependent) - .filter(isNotHome) - .sort(sortByName) - - tempArray.forEach(profile => { - if (this.appManager.actives.includes(profile.name)) { - activatedPlugins.push(profile) - } else { - deactivatedPlugins.push(profile) - } - }) - this.activePlugins = activatedPlugins - this.inactivePlugins = deactivatedPlugins - this.renderComponent() - } - - listenOnEvent () { - this.engine.event.on('onRegistration', () => this.renderComponent()) - this.appManager.event.on('activate', () => { - this.getAndFilterPlugins() - }) - this.appManager.event.on('deactivate', () => { - this.getAndFilterPlugins() - }) + deactivateP = (name) => { + this.call('manager', 'deactivatePlugin', name) + _paq.push(['trackEvent', 'manager', 'deactivate', name]) + } + + setDispatch (dispatch) { + this.dispatch = dispatch + this.renderComponent() + } + + updateComponent(state){ + return + } + + renderComponent () { + if(this.dispatch) this.dispatch({...this, activePlugins: this.activePlugins, inactivePlugins: this.inactivePlugins}) + } + + render () { + return ( +
+ ); + + } + + getAndFilterPlugins = (filter) => { + this.filter = typeof filter === 'string' ? filter.toLowerCase() : this.filter + + const isFiltered = (profile) => (profile.displayName ? profile.displayName : profile.name).toLowerCase().includes(this.filter) + const isNotRequired = (profile) => !this.appManager.isRequired(profile.name) + const isNotDependent = (profile) => !this.appManager.isDependent(profile.name) + const isNotHome = (profile) => profile.name !== 'home' + const sortByName = (profileA, profileB) => { + const nameA = ((profileA.displayName) ? profileA.displayName : profileA.name).toUpperCase() + const nameB = ((profileB.displayName) ? profileB.displayName : profileB.name).toUpperCase() + return (nameA < nameB) ? -1 : (nameA > nameB) ? 1 : 0 } + const activatedPlugins = [] + const deactivatedPlugins = [] + const tempArray = this.appManager.getAll() + .filter(isFiltered) + .filter(isNotRequired) + .filter(isNotDependent) + .filter(isNotHome) + .sort(sortByName) + + tempArray.forEach(profile => { + if (this.appManager.actives.includes(profile.name)) { + activatedPlugins.push(profile) + } else { + deactivatedPlugins.push(profile) + } + }) + this.activePlugins = activatedPlugins + this.inactivePlugins = deactivatedPlugins + this.renderComponent() + } + + listenOnEvent () { + this.engine.event.on('onRegistration', () => this.renderComponent()) + this.appManager.event.on('activate', () => { + this.getAndFilterPlugins() + }) + this.appManager.event.on('deactivate', () => { + this.getAndFilterPlugins() + }) + } } module.exports = PluginManagerComponent diff --git a/apps/remix-ide/src/app/components/preload.tsx b/apps/remix-ide/src/app/components/preload.tsx index 4cca83a00a..06f1acf517 100644 --- a/apps/remix-ide/src/app/components/preload.tsx +++ b/apps/remix-ide/src/app/components/preload.tsx @@ -11,134 +11,134 @@ const _paq = window._paq = window._paq || [] export const Preload = () => { - const [supported, setSupported] = useState(true) - const [error, setError] = useState(false) - const [showDownloader, setShowDownloader] = useState(false) - const remixFileSystems = useRef(new fileSystems()) - const remixIndexedDB = useRef(new indexedDBFileSystem()) - const localStorageFileSystem = useRef(new localStorageFS()) - // url parameters to e2e test the fallbacks and error warnings - const testmigrationFallback = useRef(window.location.hash.includes('e2e_testmigration_fallback=true') && window.location.host === '127.0.0.1:8080' && window.location.protocol === 'http:') - const testmigrationResult = useRef(window.location.hash.includes('e2e_testmigration=true') && window.location.host === '127.0.0.1:8080' && window.location.protocol === 'http:') - const testBlockStorage = useRef(window.location.hash.includes('e2e_testblock_storage=true') && window.location.host === '127.0.0.1:8080' && window.location.protocol === 'http:') + const [supported, setSupported] = useState(true) + const [error, setError] = useState(false) + const [showDownloader, setShowDownloader] = useState(false) + const remixFileSystems = useRef(new fileSystems()) + const remixIndexedDB = useRef(new indexedDBFileSystem()) + const localStorageFileSystem = useRef(new localStorageFS()) + // url parameters to e2e test the fallbacks and error warnings + const testmigrationFallback = useRef(window.location.hash.includes('e2e_testmigration_fallback=true') && window.location.host === '127.0.0.1:8080' && window.location.protocol === 'http:') + const testmigrationResult = useRef(window.location.hash.includes('e2e_testmigration=true') && window.location.host === '127.0.0.1:8080' && window.location.protocol === 'http:') + const testBlockStorage = useRef(window.location.hash.includes('e2e_testblock_storage=true') && window.location.host === '127.0.0.1:8080' && window.location.protocol === 'http:') - function loadAppComponent() { - import('../../app').then((AppComponent) => { - const appComponent = new AppComponent.default() - appComponent.run().then(() => { - render( - <> - - , - document.getElementById('root') - ) - }) - }).catch(err => { - _paq.push(['trackEvent', 'Preload', 'error', err && err.message]) - console.error('Error loading Remix:', err) - setError(true) - }) - } + function loadAppComponent() { + import('../../app').then((AppComponent) => { + const appComponent = new AppComponent.default() + appComponent.run().then(() => { + render( + <> + + , + document.getElementById('root') + ) + }) + }).catch(err => { + _paq.push(['trackEvent', 'Preload', 'error', err && err.message]) + console.error('Error loading Remix:', err) + setError(true) + }) + } - const downloadBackup = async () => { - setShowDownloader(false) - const fsUtility = new fileSystemUtility() - await fsUtility.downloadBackup(remixFileSystems.current.fileSystems['localstorage']) - await migrateAndLoad() - } + const downloadBackup = async () => { + setShowDownloader(false) + const fsUtility = new fileSystemUtility() + await fsUtility.downloadBackup(remixFileSystems.current.fileSystems['localstorage']) + await migrateAndLoad() + } - const migrateAndLoad = async () => { - setShowDownloader(false) - const fsUtility = new fileSystemUtility() - const migrationResult = await fsUtility.migrate(localStorageFileSystem.current, remixIndexedDB.current) - _paq.push(['trackEvent', 'Migrate', 'result', migrationResult?'success' : 'fail']) - await setFileSystems() - } + const migrateAndLoad = async () => { + setShowDownloader(false) + const fsUtility = new fileSystemUtility() + const migrationResult = await fsUtility.migrate(localStorageFileSystem.current, remixIndexedDB.current) + _paq.push(['trackEvent', 'Migrate', 'result', migrationResult?'success' : 'fail']) + await setFileSystems() + } - const setFileSystems = async() => { - const fsLoaded = await remixFileSystems.current.setFileSystem([(testmigrationFallback.current || testBlockStorage.current)? null: remixIndexedDB.current, testBlockStorage.current? null:localStorageFileSystem.current]) - if (fsLoaded) { - console.log(fsLoaded.name + ' activated') - _paq.push(['trackEvent', 'Storage', 'activate', fsLoaded.name]) - loadAppComponent() - } else { - _paq.push(['trackEvent', 'Storage', 'error', 'no supported storage']) - setSupported(false) - } + const setFileSystems = async() => { + const fsLoaded = await remixFileSystems.current.setFileSystem([(testmigrationFallback.current || testBlockStorage.current)? null: remixIndexedDB.current, testBlockStorage.current? null:localStorageFileSystem.current]) + if (fsLoaded) { + console.log(fsLoaded.name + ' activated') + _paq.push(['trackEvent', 'Storage', 'activate', fsLoaded.name]) + loadAppComponent() + } else { + _paq.push(['trackEvent', 'Storage', 'error', 'no supported storage']) + setSupported(false) } + } - const testmigration = async() => { - if (testmigrationResult.current) { - const fsUtility = new fileSystemUtility() - fsUtility.populateWorkspace(migrationTestData, remixFileSystems.current.fileSystems['localstorage'].fs) - } + const testmigration = async() => { + if (testmigrationResult.current) { + const fsUtility = new fileSystemUtility() + fsUtility.populateWorkspace(migrationTestData, remixFileSystems.current.fileSystems['localstorage'].fs) } + } - useEffect(() => { - async function loadStorage() { - await remixFileSystems.current.addFileSystem(remixIndexedDB.current) || _paq.push(['trackEvent', 'Storage', 'error', 'indexedDB not supported']) - await remixFileSystems.current.addFileSystem(localStorageFileSystem.current) || _paq.push(['trackEvent', 'Storage', 'error', 'localstorage not supported']) - await testmigration() - remixIndexedDB.current.loaded && await remixIndexedDB.current.checkWorkspaces() - localStorageFileSystem.current.loaded && await localStorageFileSystem.current.checkWorkspaces() - remixIndexedDB.current.loaded && ( (remixIndexedDB.current.hasWorkSpaces || !localStorageFileSystem.current.hasWorkSpaces)? await setFileSystems():setShowDownloader(true)) - !remixIndexedDB.current.loaded && await setFileSystems() - } - loadStorage() - }, []) + useEffect(() => { + async function loadStorage() { + await remixFileSystems.current.addFileSystem(remixIndexedDB.current) || _paq.push(['trackEvent', 'Storage', 'error', 'indexedDB not supported']) + await remixFileSystems.current.addFileSystem(localStorageFileSystem.current) || _paq.push(['trackEvent', 'Storage', 'error', 'localstorage not supported']) + await testmigration() + remixIndexedDB.current.loaded && await remixIndexedDB.current.checkWorkspaces() + localStorageFileSystem.current.loaded && await localStorageFileSystem.current.checkWorkspaces() + remixIndexedDB.current.loaded && ( (remixIndexedDB.current.hasWorkSpaces || !localStorageFileSystem.current.hasWorkSpaces)? await setFileSystems():setShowDownloader(true)) + !remixIndexedDB.current.loaded && await setFileSystems() + } + loadStorage() + }, []) - return <> -
-
- {logo} -
+ return <> +
+
+ {logo} +
REMIX IDE -
- v{packageJson.version} -
-
- {!supported ? -
+
+ v{packageJson.version} +
+
+ {!supported ? +
Your browser does not support any of the filesystems required by Remix. Either change the settings in your browser or use a supported browser. -
: null} - {error ? -
+
: null} + {error ? +
An unknown error has occurred while loading the application.

Doing a hard refresh might fix this issue:

-
+
Windows:

- Chrome: CTRL + F5 or CTRL + Reload Button

- Firefox: CTRL + SHIFT + R or CTRL + F5

-
-
+
+
MacOS:

- Chrome & FireFox: CMD + SHIFT + R or SHIFT + Reload Button

-
-
+
+
Linux:

- Chrome & FireFox: CTRL + SHIFT + R

-
-
: null} - {showDownloader ? -
+
+
: null} + {showDownloader ? +
This app will be updated now. Please download a backup of your files now to make sure you don't lose your work. -

+

You don't need to do anything else, your files will be available when the app loads. -
{ await downloadBackup() }} data-id='downloadbackup-btn' className='btn btn-primary mt-1'>download backup
-
{ await migrateAndLoad() }} data-id='skipbackup-btn' className='btn btn-primary mt-1'>skip backup
-
: null} - {(supported && !error && !showDownloader) ? -
- -
: null} -
- +
{ await downloadBackup() }} data-id='downloadbackup-btn' className='btn btn-primary mt-1'>download backup
+
{ await migrateAndLoad() }} data-id='skipbackup-btn' className='btn btn-primary mt-1'>skip backup
+
: null} + {(supported && !error && !showDownloader) ? +
+ +
: null} +
+ } const logo = - - - + + + diff --git a/apps/remix-ide/src/app/components/side-panel.tsx b/apps/remix-ide/src/app/components/side-panel.tsx index 8b58171492..6df9408303 100644 --- a/apps/remix-ide/src/app/components/side-panel.tsx +++ b/apps/remix-ide/src/app/components/side-panel.tsx @@ -8,94 +8,94 @@ import { PluginViewWrapper } from '@remix-ui/helper' // const csjs = require('csjs-inject') const sidePanel = { - name: 'sidePanel', - displayName: 'Side Panel', - description: 'Remix IDE side panel', - version: packageJson.version, - methods: ['addView', 'removeView', 'currentFocus'] + name: 'sidePanel', + displayName: 'Side Panel', + description: 'Remix IDE side panel', + version: packageJson.version, + methods: ['addView', 'removeView', 'currentFocus'] } export class SidePanel extends AbstractPanel { - sideelement: any - dispatch: React.Dispatch = () => {} - constructor() { - super(sidePanel) - this.sideelement = document.createElement('section') - this.sideelement.setAttribute('class', 'panel plugin-manager') - } + sideelement: any + dispatch: React.Dispatch = () => {} + constructor() { + super(sidePanel) + this.sideelement = document.createElement('section') + this.sideelement.setAttribute('class', 'panel plugin-manager') + } - onActivation() { - this.renderComponent() - // Toggle content - this.on('menuicons', 'toggleContent', (name) => { - if (!this.plugins[name]) return - if (this.plugins[name].active) { - // TODO: Only keep `this.emit` (issue#2210) - this.emit('toggle', name) - this.events.emit('toggle', name) - return - } - this.showContent(name) - // TODO: Only keep `this.emit` (issue#2210) - this.emit('showing', name) - this.events.emit('showing', name) - }) - // Force opening - this.on('menuicons', 'showContent', (name) => { - if (!this.plugins[name]) return - this.showContent(name) - // TODO: Only keep `this.emit` (issue#2210) - this.emit('showing', name) - this.events.emit('showing', name) - }) - } + onActivation() { + this.renderComponent() + // Toggle content + this.on('menuicons', 'toggleContent', (name) => { + if (!this.plugins[name]) return + if (this.plugins[name].active) { + // TODO: Only keep `this.emit` (issue#2210) + this.emit('toggle', name) + this.events.emit('toggle', name) + return + } + this.showContent(name) + // TODO: Only keep `this.emit` (issue#2210) + this.emit('showing', name) + this.events.emit('showing', name) + }) + // Force opening + this.on('menuicons', 'showContent', (name) => { + if (!this.plugins[name]) return + this.showContent(name) + // TODO: Only keep `this.emit` (issue#2210) + this.emit('showing', name) + this.events.emit('showing', name) + }) + } - focus(name) { - this.emit('focusChanged', name) - super.focus(name) - } + focus(name) { + this.emit('focusChanged', name) + super.focus(name) + } - removeView(profile) { - if (this.plugins[profile.name].active) this.call('menuicons', 'select', 'filePanel') - super.removeView(profile) - this.emit('pluginDisabled', profile.name) - this.call('menuicons', 'unlinkContent', profile) - this.renderComponent() - } + removeView(profile) { + if (this.plugins[profile.name].active) this.call('menuicons', 'select', 'filePanel') + super.removeView(profile) + this.emit('pluginDisabled', profile.name) + this.call('menuicons', 'unlinkContent', profile) + this.renderComponent() + } - addView(profile, view) { - super.addView(profile, view) - this.call('menuicons', 'linkContent', profile) - this.renderComponent() - } + addView(profile, view) { + super.addView(profile, view) + this.call('menuicons', 'linkContent', profile) + this.renderComponent() + } - /** + /** * Display content and update the header * @param {String} name The name of the plugin to display */ - async showContent(name) { - super.showContent(name) - this.emit('focusChanged', name) - this.renderComponent() - } + async showContent(name) { + super.showContent(name) + this.emit('focusChanged', name) + this.renderComponent() + } - setDispatch (dispatch: React.Dispatch) { - this.dispatch = dispatch - } + setDispatch (dispatch: React.Dispatch) { + this.dispatch = dispatch + } - render() { - return ( -
- ); - } + render() { + return ( +
+ ); + } - updateComponent(state: any) { - return } plugins={state.plugins} /> - } + updateComponent(state: any) { + return } plugins={state.plugins} /> + } - renderComponent() { - this.dispatch({ - plugins: this.plugins - }) - } + renderComponent() { + this.dispatch({ + plugins: this.plugins + }) + } } diff --git a/apps/remix-ide/src/app/components/vertical-icons.tsx b/apps/remix-ide/src/app/components/vertical-icons.tsx index 87f78ffd40..fbdae5e8b8 100644 --- a/apps/remix-ide/src/app/components/vertical-icons.tsx +++ b/apps/remix-ide/src/app/components/vertical-icons.tsx @@ -8,119 +8,119 @@ import { Profile } from '@remixproject/plugin-utils' import { PluginViewWrapper } from '@remix-ui/helper' const profile = { - name: 'menuicons', - displayName: 'Vertical Icons', - description: 'Remix IDE vertical icons', - version: packageJson.version, - methods: ['select', 'unlinkContent', 'linkContent'], - events: ['toggleContent', 'showContent'] + name: 'menuicons', + displayName: 'Vertical Icons', + description: 'Remix IDE vertical icons', + version: packageJson.version, + methods: ['select', 'unlinkContent', 'linkContent'], + events: ['toggleContent', 'showContent'] } export class VerticalIcons extends Plugin { - events: EventEmitter - htmlElement: HTMLDivElement - icons: Record = {} - dispatch: React.Dispatch = () => {} - constructor () { - super(profile) - this.events = new EventEmitter() - this.htmlElement = document.createElement('div') - this.htmlElement.setAttribute('id', 'icon-panel') + events: EventEmitter + htmlElement: HTMLDivElement + icons: Record = {} + dispatch: React.Dispatch = () => {} + constructor () { + super(profile) + this.events = new EventEmitter() + this.htmlElement = document.createElement('div') + this.htmlElement.setAttribute('id', 'icon-panel') + } + + renderComponent () { + const fixedOrder = ['filePanel', 'search', 'solidity','udapp', 'debugger', 'solidityStaticAnalysis', 'solidityUnitTesting', 'pluginManager'] + + const divived = Object.values(this.icons).map((value) => { return { + ...value, + isRequired: fixedOrder.indexOf(value.profile.name) > -1 + }}).sort((a,b) => { + return a.timestamp - b.timestamp + }) + + const required = divived.filter((value) => value.isRequired).sort((a,b) => { + return fixedOrder.indexOf(a.profile.name) - fixedOrder.indexOf(b.profile.name) + }) + + const sorted: IconRecord[] = [ + ...required, + ...divived.filter((value) => { return !value.isRequired }) + ] + + this.dispatch({ + verticalIconsPlugin: this, + icons: sorted + }) + + } + + setDispatch (dispatch: React.Dispatch) { + this.dispatch = dispatch + } + + onActivation () { + this.renderComponent() + this.on('sidePanel', 'focusChanged', (name: string) => { + Object.keys(this.icons).map((o) => { + this.icons[o].active = false + }) + this.icons[name].active = true + this.renderComponent() + }) + } + + async linkContent (profile: Profile) { + if (!profile.icon) return + if (!profile.kind) profile.kind = 'none' + this.icons[profile.name] = { + profile: profile, + active: false, + canbeDeactivated: await this.call('manager', 'canDeactivate', this.profile, profile), + timestamp: Date.now() } + this.renderComponent() + } - renderComponent () { - const fixedOrder = ['filePanel', 'search', 'solidity','udapp', 'debugger', 'solidityStaticAnalysis', 'solidityUnitTesting', 'pluginManager'] + unlinkContent (profile: Profile) { + delete this.icons[profile.name] + this.renderComponent() + } - const divived = Object.values(this.icons).map((value) => { return { - ...value, - isRequired: fixedOrder.indexOf(value.profile.name) > -1 - }}).sort((a,b) => { - return a.timestamp - b.timestamp - }) + async activateHome() { + await this.call('manager', 'activatePlugin', 'home') + await this.call('tabs', 'focus', 'home') + } - const required = divived.filter((value) => value.isRequired).sort((a,b) => { - return fixedOrder.indexOf(a.profile.name) - fixedOrder.indexOf(b.profile.name) - }) - - const sorted: IconRecord[] = [ - ...required, - ...divived.filter((value) => { return !value.isRequired }) - ] - - this.dispatch({ - verticalIconsPlugin: this, - icons: sorted - }) - - } - - setDispatch (dispatch: React.Dispatch) { - this.dispatch = dispatch - } - - onActivation () { - this.renderComponent() - this.on('sidePanel', 'focusChanged', (name: string) => { - Object.keys(this.icons).map((o) => { - this.icons[o].active = false - }) - this.icons[name].active = true - this.renderComponent() - }) - } - - async linkContent (profile: Profile) { - if (!profile.icon) return - if (!profile.kind) profile.kind = 'none' - this.icons[profile.name] = { - profile: profile, - active: false, - canbeDeactivated: await this.call('manager', 'canDeactivate', this.profile, profile), - timestamp: Date.now() - } - this.renderComponent() - } - - unlinkContent (profile: Profile) { - delete this.icons[profile.name] - this.renderComponent() - } - - async activateHome() { - await this.call('manager', 'activatePlugin', 'home') - await this.call('tabs', 'focus', 'home') - } - - /** + /** * Set an icon as active * @param {string} name Name of profile of the module to activate */ - select (name: string) { - // TODO: Only keep `this.emit` (issue#2210) - this.emit('showContent', name) - this.events.emit('showContent', name) - } + select (name: string) { + // TODO: Only keep `this.emit` (issue#2210) + this.emit('showContent', name) + this.events.emit('showContent', name) + } - /** + /** * Toggles the side panel for plugin * @param {string} name Name of profile of the module to activate */ - toggle (name: string) { - // TODO: Only keep `this.emit` (issue#2210) - this.emit('toggleContent', name) - this.events.emit('toggleContent', name) - } - - updateComponent(state: any){ - return - } - - render() { - return ( -
- ); - } + toggle (name: string) { + // TODO: Only keep `this.emit` (issue#2210) + this.emit('toggleContent', name) + this.events.emit('toggleContent', name) + } + + updateComponent(state: any){ + return + } + + render() { + return ( +
+ ); + } } diff --git a/apps/remix-ide/src/app/editor/editor.js b/apps/remix-ide/src/app/editor/editor.js index 2de1353d15..edf007ae1d 100644 --- a/apps/remix-ide/src/app/editor/editor.js +++ b/apps/remix-ide/src/app/editor/editor.js @@ -9,448 +9,448 @@ import { PluginViewWrapper } from '@remix-ui/helper' const EventManager = require('../../lib/events') const profile = { - displayName: 'Editor', - name: 'editor', - description: 'service - editor', - version: packageJson.version, - methods: ['highlight', 'discardHighlight', 'clearAnnotations', 'addLineText', 'discardLineTexts', 'addAnnotation', 'gotoLine', 'revealRange', 'getCursorPosition', 'open', 'addModel','addErrorMarker', 'clearErrorMarkers', 'getText'], + displayName: 'Editor', + name: 'editor', + description: 'service - editor', + version: packageJson.version, + methods: ['highlight', 'discardHighlight', 'clearAnnotations', 'addLineText', 'discardLineTexts', 'addAnnotation', 'gotoLine', 'revealRange', 'getCursorPosition', 'open', 'addModel','addErrorMarker', 'clearErrorMarkers', 'getText'], } class Editor extends Plugin { - constructor () { - super(profile) - - this._themes = { - light: 'light', - dark: 'vs-dark', - remixDark: 'remix-dark' - } - - this.registeredDecorations = { sourceAnnotationsPerFile: {}, markerPerFile: {}, lineTextPerFile: {} } - this.currentDecorations = { sourceAnnotationsPerFile: {}, markerPerFile: {}, lineTextPerFile: {} } - - // Init - this.event = new EventManager() - this.sessions = {} - this.readOnlySessions = {} - this.previousInput = '' - this.saveTimeout = null - this.emptySession = null - this.modes = { - sol: 'sol', - yul: 'sol', - mvir: 'move', - js: 'javascript', - py: 'python', - vy: 'python', - zok: 'zokrates', - lex: 'lexon', - txt: 'text', - json: 'json', - abi: 'json', - rs: 'rust', - cairo: 'cairo', - ts: 'typescript', - move: 'move' - } - - this.activated = false - - this.events = { - onBreakPointAdded: (file, line) => this.triggerEvent('breakpointAdded', [file, line]), - onBreakPointCleared: (file, line) => this.triggerEvent('breakpointCleared', [file, line]), - onDidChangeContent: (file) => this._onChange(file), - onEditorMounted: () => this.triggerEvent('editorMounted', []) - } - - // to be implemented by the react component - this.api = {} - this.dispatch = null - this.ref = null - } - - setDispatch (dispatch) { - this.dispatch = dispatch - } - - updateComponent(state) { - return - } - - render () { - return
{ - this.ref = element - this.ref.currentContent = () => this.currentContent() // used by e2e test - this.ref.setCurrentContent = (value) => { - if (this.sessions[this.currentFile]) { - this.sessions[this.currentFile].setValue(value) - this._onChange(this.currentFile) - } - } - this.ref.gotoLine = (line, column) => this.gotoLine(line, column || 0) - this.ref.getCursorPosition = () => this.getCursorPosition() - this.ref.addDecoration = (marker, filePath, typeOfDecoration) => this.addDecoration(marker, filePath, typeOfDecoration) - this.ref.clearDecorationsByPlugin = (filePath, plugin, typeOfDecoration) => this.clearDecorationsByPlugin(filePath, plugin, typeOfDecoration) - this.ref.keepDecorationsFor = (name, typeOfDecoration) => this.keepDecorationsFor(name, typeOfDecoration) - }} id='editorView'> - -
- } - - renderComponent () { - this.dispatch({ - api: this.api, - currentThemeType: this.currentThemeType, - currentFile: this.currentFile, - events: this.events, - plugin: this - }) - } - - triggerEvent (name, params) { - this.event.trigger(name, params) // internal stack - this.emit(name, ...params) // plugin stack - } - - async onActivation () { - this.activated = true - this.on('sidePanel', 'focusChanged', (name) => { - this.keepDecorationsFor(name, 'sourceAnnotationsPerFile') - this.keepDecorationsFor(name, 'markerPerFile') - }) - this.on('sidePanel', 'pluginDisabled', (name) => { - this.clearAllDecorationsFor(name) - }) - this.on('theme', 'themeLoaded', (theme) => { - this.currentThemeType = theme.quality - this.renderComponent() - }) - this.on('fileManager', 'noFileSelected', async () => { - this.currentFile = null - this.renderComponent() - }) - try { - this.currentThemeType = (await this.call('theme', 'currentTheme')).quality - } catch (e) { - console.log('unable to select the theme ' + e.message) - } - this.renderComponent() - } - - onDeactivation () { - this.off('sidePanel', 'focusChanged') - this.off('sidePanel', 'pluginDisabled') - } - - async _onChange (file) { - this.triggerEvent('didChangeFile', [file]) - const currentFile = await this.call('fileManager', 'file') - if (!currentFile) { - return - } - if (currentFile !== file) { - return - } - const input = this.get(currentFile) - if (!input) { - return - } - // if there's no change, don't do anything - if (input === this.previousInput) { - return - } - this.previousInput = input - - // fire storage update - // NOTE: save at most once per 5 seconds - if (this.saveTimeout) { - window.clearTimeout(this.saveTimeout) + constructor () { + super(profile) + + this._themes = { + light: 'light', + dark: 'vs-dark', + remixDark: 'remix-dark' + } + + this.registeredDecorations = { sourceAnnotationsPerFile: {}, markerPerFile: {}, lineTextPerFile: {} } + this.currentDecorations = { sourceAnnotationsPerFile: {}, markerPerFile: {}, lineTextPerFile: {} } + + // Init + this.event = new EventManager() + this.sessions = {} + this.readOnlySessions = {} + this.previousInput = '' + this.saveTimeout = null + this.emptySession = null + this.modes = { + sol: 'sol', + yul: 'sol', + mvir: 'move', + js: 'javascript', + py: 'python', + vy: 'python', + zok: 'zokrates', + lex: 'lexon', + txt: 'text', + json: 'json', + abi: 'json', + rs: 'rust', + cairo: 'cairo', + ts: 'typescript', + move: 'move' + } + + this.activated = false + + this.events = { + onBreakPointAdded: (file, line) => this.triggerEvent('breakpointAdded', [file, line]), + onBreakPointCleared: (file, line) => this.triggerEvent('breakpointCleared', [file, line]), + onDidChangeContent: (file) => this._onChange(file), + onEditorMounted: () => this.triggerEvent('editorMounted', []) + } + + // to be implemented by the react component + this.api = {} + this.dispatch = null + this.ref = null + } + + setDispatch (dispatch) { + this.dispatch = dispatch + } + + updateComponent(state) { + return + } + + render () { + return
{ + this.ref = element + this.ref.currentContent = () => this.currentContent() // used by e2e test + this.ref.setCurrentContent = (value) => { + if (this.sessions[this.currentFile]) { + this.sessions[this.currentFile].setValue(value) + this._onChange(this.currentFile) } - - this.saveTimeout = window.setTimeout(() => { - this.triggerEvent('contentChanged', []) - this.triggerEvent('requiringToSaveCurrentfile', []) - }, 500) - } - - _switchSession (path) { - if (path === this.currentFile) return - this.triggerEvent('sessionSwitched', []) - this.currentFile = path - this.renderComponent() - } - - /** + } + this.ref.gotoLine = (line, column) => this.gotoLine(line, column || 0) + this.ref.getCursorPosition = () => this.getCursorPosition() + this.ref.addDecoration = (marker, filePath, typeOfDecoration) => this.addDecoration(marker, filePath, typeOfDecoration) + this.ref.clearDecorationsByPlugin = (filePath, plugin, typeOfDecoration) => this.clearDecorationsByPlugin(filePath, plugin, typeOfDecoration) + this.ref.keepDecorationsFor = (name, typeOfDecoration) => this.keepDecorationsFor(name, typeOfDecoration) + }} id='editorView'> + +
+ } + + renderComponent () { + this.dispatch({ + api: this.api, + currentThemeType: this.currentThemeType, + currentFile: this.currentFile, + events: this.events, + plugin: this + }) + } + + triggerEvent (name, params) { + this.event.trigger(name, params) // internal stack + this.emit(name, ...params) // plugin stack + } + + async onActivation () { + this.activated = true + this.on('sidePanel', 'focusChanged', (name) => { + this.keepDecorationsFor(name, 'sourceAnnotationsPerFile') + this.keepDecorationsFor(name, 'markerPerFile') + }) + this.on('sidePanel', 'pluginDisabled', (name) => { + this.clearAllDecorationsFor(name) + }) + this.on('theme', 'themeLoaded', (theme) => { + this.currentThemeType = theme.quality + this.renderComponent() + }) + this.on('fileManager', 'noFileSelected', async () => { + this.currentFile = null + this.renderComponent() + }) + try { + this.currentThemeType = (await this.call('theme', 'currentTheme')).quality + } catch (e) { + console.log('unable to select the theme ' + e.message) + } + this.renderComponent() + } + + onDeactivation () { + this.off('sidePanel', 'focusChanged') + this.off('sidePanel', 'pluginDisabled') + } + + async _onChange (file) { + this.triggerEvent('didChangeFile', [file]) + const currentFile = await this.call('fileManager', 'file') + if (!currentFile) { + return + } + if (currentFile !== file) { + return + } + const input = this.get(currentFile) + if (!input) { + return + } + // if there's no change, don't do anything + if (input === this.previousInput) { + return + } + this.previousInput = input + + // fire storage update + // NOTE: save at most once per 5 seconds + if (this.saveTimeout) { + window.clearTimeout(this.saveTimeout) + } + + this.saveTimeout = window.setTimeout(() => { + this.triggerEvent('contentChanged', []) + this.triggerEvent('requiringToSaveCurrentfile', []) + }, 500) + } + + _switchSession (path) { + if (path === this.currentFile) return + this.triggerEvent('sessionSwitched', []) + this.currentFile = path + this.renderComponent() + } + + /** * Get Ace mode base of the extension of the session file * @param {string} path Path of the file */ - _getMode (path) { - if (!path) return this.modes.txt - const root = path.split('#')[0].split('?')[0] - let ext = root.indexOf('.') !== -1 ? /[^.]+$/.exec(root) : null - if (ext) ext = ext[0] - else ext = 'txt' - return ext && this.modes[ext] ? this.modes[ext] : this.modes.txt - } - - async handleTypeScriptDependenciesOf (path, content, readFile, exists) { - if (path.endsWith('.ts')) { - // extract the import, resolve their content - // and add the imported files to Monaco through the `addModel` - // so Monaco can provide auto completion - const paths = path.split('/') - paths.pop() - const fromPath = paths.join('/') // get current execution context path - for (const match of content.matchAll(/import\s+.*\s+from\s+(?:"(.*?)"|'(.*?)')/g)) { - let pathDep = match[2] - if (pathDep.startsWith('./') || pathDep.startsWith('../')) pathDep = resolve(fromPath, pathDep) - if (pathDep.startsWith('/')) pathDep = pathDep.substring(1) - if (!pathDep.endsWith('.ts')) pathDep = pathDep + '.ts' - try { - // we can't use the fileManager plugin call directly - // because it's itself called in a plugin context, and that causes a timeout in the plugin stack - const pathExists = await exists(pathDep) - let contentDep = '' - if (pathExists) { - contentDep = await readFile(pathDep) - if (contentDep !== '') { - this.emit('addModel', contentDep, 'typescript', pathDep, this.readOnlySessions[path]) - } - } else { - console.log("The file ", pathDep, " can't be found.") - } - } catch (e) { - console.log(e) - } + _getMode (path) { + if (!path) return this.modes.txt + const root = path.split('#')[0].split('?')[0] + let ext = root.indexOf('.') !== -1 ? /[^.]+$/.exec(root) : null + if (ext) ext = ext[0] + else ext = 'txt' + return ext && this.modes[ext] ? this.modes[ext] : this.modes.txt + } + + async handleTypeScriptDependenciesOf (path, content, readFile, exists) { + if (path.endsWith('.ts')) { + // extract the import, resolve their content + // and add the imported files to Monaco through the `addModel` + // so Monaco can provide auto completion + const paths = path.split('/') + paths.pop() + const fromPath = paths.join('/') // get current execution context path + for (const match of content.matchAll(/import\s+.*\s+from\s+(?:"(.*?)"|'(.*?)')/g)) { + let pathDep = match[2] + if (pathDep.startsWith('./') || pathDep.startsWith('../')) pathDep = resolve(fromPath, pathDep) + if (pathDep.startsWith('/')) pathDep = pathDep.substring(1) + if (!pathDep.endsWith('.ts')) pathDep = pathDep + '.ts' + try { + // we can't use the fileManager plugin call directly + // because it's itself called in a plugin context, and that causes a timeout in the plugin stack + const pathExists = await exists(pathDep) + let contentDep = '' + if (pathExists) { + contentDep = await readFile(pathDep) + if (contentDep !== '') { + this.emit('addModel', contentDep, 'typescript', pathDep, this.readOnlySessions[path]) } + } else { + console.log("The file ", pathDep, " can't be found.") + } + } catch (e) { + console.log(e) } + } } + } - /** + /** * Create an editor session * @param {string} path path of the file * @param {string} content Content of the file to open * @param {string} mode Mode for this file [Default is `text`] */ - async _createSession (path, content, mode) { - if (!this.activated) return + async _createSession (path, content, mode) { + if (!this.activated) return - this.emit('addModel', content, mode, path, this.readOnlySessions[path]) - return { - path, - language: mode, - setValue: (content) => { - this.emit('setValue', path, content) - }, - getValue: () => { - return this.api.getValue(path, content) - }, - dispose: () => { - this.emit('disposeModel', path) - } - } - } - - /** + this.emit('addModel', content, mode, path, this.readOnlySessions[path]) + return { + path, + language: mode, + setValue: (content) => { + this.emit('setValue', path, content) + }, + getValue: () => { + return this.api.getValue(path, content) + }, + dispose: () => { + this.emit('disposeModel', path) + } + } + } + + /** * Attempts to find the string in the current document * @param {string} string */ - find (string) { - return this.api.findMatches(this.currentFile, string) - } + find (string) { + return this.api.findMatches(this.currentFile, string) + } - addModel(path, content) { - this.emit('addModel', content, this._getMode(path), path, this.readOnlySessions[path]) - } + addModel(path, content) { + this.emit('addModel', content, this._getMode(path), path, this.readOnlySessions[path]) + } - /** + /** * Display an Empty read-only session */ - displayEmptyReadOnlySession () { - if (!this.activated) return - this.currentFile = null - this.emit('addModel', '', 'text', '_blank', true) - } + displayEmptyReadOnlySession () { + if (!this.activated) return + this.currentFile = null + this.emit('addModel', '', 'text', '_blank', true) + } - /** + /** * Set the text in the current session, if any. * @param {string} url Address of the text to replace. * @param {string} text New text to be place. */ - setText (url, text) { - if (this.sessions[url]) { - this.sessions[url].setValue(text) - } + setText (url, text) { + if (this.sessions[url]) { + this.sessions[url].setValue(text) } + } - /** + /** * Get the text in the current session, if any. * @param {string} url Address of the content to retrieve. */ - getText (url) { - if (this.sessions[url]) { - return this.sessions[url].getValue() - } + getText (url) { + if (this.sessions[url]) { + return this.sessions[url].getValue() } + } - /** + /** * Upsert and open a session. * @param {string} path Path of the session to open. * @param {string} content Content of the document or update. */ - async open (path, content) { - /* + async open (path, content) { + /* we have the following cases: - URL prepended with "localhost" - URL prepended with "browser" - URL not prepended with the file explorer. We assume (as it is in the whole app, that this is a "browser" URL */ - if (!this.sessions[path]) { - this.readOnlySessions[path] = false - const session = await this._createSession(path, content, this._getMode(path)) - this.sessions[path] = session - } else if (this.sessions[path].getValue() !== content) { - this.sessions[path].setValue(content) - } - this._switchSession(path) + if (!this.sessions[path]) { + this.readOnlySessions[path] = false + const session = await this._createSession(path, content, this._getMode(path)) + this.sessions[path] = session + } else if (this.sessions[path].getValue() !== content) { + this.sessions[path].setValue(content) } + this._switchSession(path) + } - /** + /** * Upsert and Open a session and set it as Read-only. * @param {string} path Path of the session to open. * @param {string} content Content of the document or update. */ - async openReadOnly (path, content) { - if (!this.sessions[path]) { - this.readOnlySessions[path] = true - const session = await this._createSession(path, content, this._getMode(path)) - this.sessions[path] = session - } - this._switchSession(path) + async openReadOnly (path, content) { + if (!this.sessions[path]) { + this.readOnlySessions[path] = true + const session = await this._createSession(path, content, this._getMode(path)) + this.sessions[path] = session } + this._switchSession(path) + } - /** + /** * Content of the current session * @return {String} content of the file referenced by @arg path */ - currentContent () { - return this.get(this.current()) - } + currentContent () { + return this.get(this.current()) + } - /** + /** * Content of the session targeted by @arg path * if @arg path is null, the content of the current session is returned * @param {string} path Path of the session to get. * @return {String} content of the file referenced by @arg path */ - get (path) { - if (!path || this.currentFile === path) { - return this.api.getValue(path) - } else if (this.sessions[path]) { - return this.sessions[path].getValue() - } + get (path) { + if (!path || this.currentFile === path) { + return this.api.getValue(path) + } else if (this.sessions[path]) { + return this.sessions[path].getValue() } + } - /** + /** * Path of the currently editing file * returns `undefined` if no session is being editer * @return {String} path of the current session */ - current () { - return this.currentFile - } + current () { + return this.currentFile + } - /** + /** * The position of the cursor */ - getCursorPosition (offset = true) { - return this.api.getCursorPosition(offset) - } + getCursorPosition (offset = true) { + return this.api.getCursorPosition(offset) + } - /** + /** * Remove the current session from the list of sessions. */ - discardCurrentSession () { - if (this.sessions[this.currentFile]) { - delete this.sessions[this.currentFile] - this.currentFile = null - } + discardCurrentSession () { + if (this.sessions[this.currentFile]) { + delete this.sessions[this.currentFile] + this.currentFile = null } + } - /** + /** * Remove a session based on its path. * @param {string} path */ - discard (path) { - if (this.sessions[path]) { - this.sessions[path].dispose() - delete this.sessions[path] - } - if (this.currentFile === path) this.currentFile = null + discard (path) { + if (this.sessions[path]) { + this.sessions[path].dispose() + delete this.sessions[path] } + if (this.currentFile === path) this.currentFile = null + } - /** + /** * Increment the font size (in pixels) for the editor text. * @param {number} incr The amount of pixels to add to the font. */ - editorFontSize (incr) { - if (!this.activated) return - const newSize = this.api.getFontSize() + incr - if (newSize >= 6) { - this.emit('setFontSize', newSize) - } + editorFontSize (incr) { + if (!this.activated) return + const newSize = this.api.getFontSize() + incr + if (newSize >= 6) { + this.emit('setFontSize', newSize) } + } - /** + /** * Resize the editor, and sets whether or not line wrapping is enabled. * @param {boolean} useWrapMode Enable (or disable) wrap mode */ - resize (useWrapMode) { - if (!this.activated) return - this.emit('setWordWrap', useWrapMode) - } + resize (useWrapMode) { + if (!this.activated) return + this.emit('setWordWrap', useWrapMode) + } - /** + /** * Moves the cursor and focus to the specified line and column number * @param {number} line * @param {number} col */ - gotoLine (line, col) { - if (!this.activated) return - this.emit('focus') - this.emit('revealLine', line + 1, col) - } + gotoLine (line, col) { + if (!this.activated) return + this.emit('focus') + this.emit('revealLine', line + 1, col) + } - /** + /** * Reveals the range in the editor. * @param {number} startLineNumber * @param {number} startColumn * @param {number} endLineNumber * @param {number} endColumn */ - revealRange (startLineNumber, startColumn, endLineNumber, endColumn) { - if (!this.activated) return - this.emit('focus') - console.log(startLineNumber, startColumn, endLineNumber, endColumn) - this.emit('revealRange', startLineNumber, startColumn, endLineNumber, endColumn) - } - - /** + revealRange (startLineNumber, startColumn, endLineNumber, endColumn) { + if (!this.activated) return + this.emit('focus') + console.log(startLineNumber, startColumn, endLineNumber, endColumn) + this.emit('revealRange', startLineNumber, startColumn, endLineNumber, endColumn) + } + + /** * Scrolls to a line. If center is true, it puts the line in middle of screen (or attempts to). * @param {number} line The line to scroll to */ - scrollToLine (line) { - if (!this.activated) return - this.emit('revealLine', line + 1, 0) - } + scrollToLine (line) { + if (!this.activated) return + this.emit('revealLine', line + 1, 0) + } - /** + /** * Clears all the decorations for the given @arg filePath and @arg plugin, if none is given, the current sesssion is used. * An annotation has the following shape: column: -1 @@ -461,22 +461,22 @@ class Editor extends Plugin { * @param {String} plugin * @param {String} typeOfDecoration */ - clearDecorationsByPlugin (filePath, plugin, typeOfDecoration) { - if (filePath && !this.sessions[filePath]) throw new Error('file not found' + filePath) - const path = filePath || this.currentFile - - const { currentDecorations, registeredDecorations } = this.api.clearDecorationsByPlugin(path, plugin, typeOfDecoration, this.registeredDecorations[typeOfDecoration][filePath] || [], this.currentDecorations[typeOfDecoration][filePath] || []) - this.currentDecorations[typeOfDecoration][filePath] = currentDecorations - this.registeredDecorations[typeOfDecoration][filePath] = registeredDecorations - } - - keepDecorationsFor (plugin, typeOfDecoration) { - if (!this.currentFile) return - const { currentDecorations } = this.api.keepDecorationsFor(this.currentFile, plugin, typeOfDecoration, this.registeredDecorations[typeOfDecoration][this.currentFile] || [], this.currentDecorations[typeOfDecoration][this.currentFile] || []) - this.currentDecorations[typeOfDecoration][this.currentFile] = currentDecorations - } - - /** + clearDecorationsByPlugin (filePath, plugin, typeOfDecoration) { + if (filePath && !this.sessions[filePath]) throw new Error('file not found' + filePath) + const path = filePath || this.currentFile + + const { currentDecorations, registeredDecorations } = this.api.clearDecorationsByPlugin(path, plugin, typeOfDecoration, this.registeredDecorations[typeOfDecoration][filePath] || [], this.currentDecorations[typeOfDecoration][filePath] || []) + this.currentDecorations[typeOfDecoration][filePath] = currentDecorations + this.registeredDecorations[typeOfDecoration][filePath] = registeredDecorations + } + + keepDecorationsFor (plugin, typeOfDecoration) { + if (!this.currentFile) return + const { currentDecorations } = this.api.keepDecorationsFor(this.currentFile, plugin, typeOfDecoration, this.registeredDecorations[typeOfDecoration][this.currentFile] || [], this.currentDecorations[typeOfDecoration][this.currentFile] || []) + this.currentDecorations[typeOfDecoration][this.currentFile] = currentDecorations + } + + /** * Clears all the decorations and for all the sessions for the given @arg plugin * An annotation has the following shape: column: -1 @@ -485,25 +485,25 @@ class Editor extends Plugin { type: "warning" * @param {String} filePath */ - clearAllDecorationsFor (plugin) { - for (const session in this.sessions) { - this.clearDecorationsByPlugin(session, plugin, 'sourceAnnotationsPerFile') - this.clearDecorationsByPlugin(session, plugin, 'markerPerFile') - } + clearAllDecorationsFor (plugin) { + for (const session in this.sessions) { + this.clearDecorationsByPlugin(session, plugin, 'sourceAnnotationsPerFile') + this.clearDecorationsByPlugin(session, plugin, 'markerPerFile') } + } - // error markers - async addErrorMarker (error){ - const { from } = this.currentRequest - this.api.addErrorMarker(error, from) - } + // error markers + async addErrorMarker (error){ + const { from } = this.currentRequest + this.api.addErrorMarker(error, from) + } - async clearErrorMarkers(sources){ - const { from } = this.currentRequest - this.api.clearErrorMarkers(sources, from) - } + async clearErrorMarkers(sources){ + const { from } = this.currentRequest + this.api.clearErrorMarkers(sources, from) + } - /** + /** * Clears all the annotations for the given @arg filePath, the plugin name is retrieved from the context, if none is given, the current sesssion is used. * An annotation has the following shape: column: -1 @@ -513,30 +513,30 @@ class Editor extends Plugin { * @param {String} filePath * @param {String} plugin */ - clearAnnotations (filePath) { - filePath = filePath || this.currentFile - const { from } = this.currentRequest - this.clearDecorationsByPlugin(filePath, from, 'sourceAnnotationsPerFile') - } - - async addDecoration (decoration, filePath, typeOfDecoration) { - if (!filePath) return - filePath = await this.call('fileManager', 'getPathFromUrl', filePath) - filePath = filePath.file - if (!this.sessions[filePath]) return - const path = filePath || this.currentFile - - const { from } = this.currentRequest - decoration.from = from - - const { currentDecorations, registeredDecorations } = this.api.addDecoration(decoration, path, typeOfDecoration) - if (!this.registeredDecorations[typeOfDecoration][filePath]) this.registeredDecorations[typeOfDecoration][filePath] = [] - this.registeredDecorations[typeOfDecoration][filePath].push(...registeredDecorations) - if (!this.currentDecorations[typeOfDecoration][filePath]) this.currentDecorations[typeOfDecoration][filePath] = [] - this.currentDecorations[typeOfDecoration][filePath].push(...currentDecorations) - } - - /** + clearAnnotations (filePath) { + filePath = filePath || this.currentFile + const { from } = this.currentRequest + this.clearDecorationsByPlugin(filePath, from, 'sourceAnnotationsPerFile') + } + + async addDecoration (decoration, filePath, typeOfDecoration) { + if (!filePath) return + filePath = await this.call('fileManager', 'getPathFromUrl', filePath) + filePath = filePath.file + if (!this.sessions[filePath]) return + const path = filePath || this.currentFile + + const { from } = this.currentRequest + decoration.from = from + + const { currentDecorations, registeredDecorations } = this.api.addDecoration(decoration, path, typeOfDecoration) + if (!this.registeredDecorations[typeOfDecoration][filePath]) this.registeredDecorations[typeOfDecoration][filePath] = [] + this.registeredDecorations[typeOfDecoration][filePath].push(...registeredDecorations) + if (!this.currentDecorations[typeOfDecoration][filePath]) this.currentDecorations[typeOfDecoration][filePath] = [] + this.currentDecorations[typeOfDecoration][filePath].push(...currentDecorations) + } + + /** * Add an annotation to the current session. * An annotation has the following shape: column: -1 @@ -546,38 +546,38 @@ class Editor extends Plugin { * @param {Object} annotation * @param {String} filePath */ - async addAnnotation (annotation, filePath) { - filePath = filePath || this.currentFile - await this.addDecoration(annotation, filePath, 'sourceAnnotationsPerFile') - } - - async highlight (position, filePath, highlightColor, opt = { focus: true }) { - filePath = filePath || this.currentFile - if (opt.focus) { - await this.call('fileManager', 'open', filePath) - this.scrollToLine(position.start.line) - } - await this.addDecoration({ position }, filePath, 'markerPerFile') - } - - discardHighlight () { - const { from } = this.currentRequest - for (const session in this.sessions) { - this.clearDecorationsByPlugin(session, from, 'markerPerFile', this.registeredDecorations, this.currentDecorations) - } - } - - async addLineText (lineText, filePath) { - filePath = filePath || this.currentFile - await this.addDecoration(lineText, filePath, 'lineTextPerFile') - } - - discardLineTexts() { - const { from } = this.currentRequest - for (const session in this.sessions) { - this.clearDecorationsByPlugin(session, from, 'lineTextPerFile', this.registeredDecorations, this.currentDecorations) - } - } + async addAnnotation (annotation, filePath) { + filePath = filePath || this.currentFile + await this.addDecoration(annotation, filePath, 'sourceAnnotationsPerFile') + } + + async highlight (position, filePath, highlightColor, opt = { focus: true }) { + filePath = filePath || this.currentFile + if (opt.focus) { + await this.call('fileManager', 'open', filePath) + this.scrollToLine(position.start.line) + } + await this.addDecoration({ position }, filePath, 'markerPerFile') + } + + discardHighlight () { + const { from } = this.currentRequest + for (const session in this.sessions) { + this.clearDecorationsByPlugin(session, from, 'markerPerFile', this.registeredDecorations, this.currentDecorations) + } + } + + async addLineText (lineText, filePath) { + filePath = filePath || this.currentFile + await this.addDecoration(lineText, filePath, 'lineTextPerFile') + } + + discardLineTexts() { + const { from } = this.currentRequest + for (const session in this.sessions) { + this.clearDecorationsByPlugin(session, from, 'lineTextPerFile', this.registeredDecorations, this.currentDecorations) + } + } } module.exports = Editor diff --git a/apps/remix-ide/src/app/files/dgitProvider.js b/apps/remix-ide/src/app/files/dgitProvider.js index d4c78d8099..738e3f966b 100644 --- a/apps/remix-ide/src/app/files/dgitProvider.js +++ b/apps/remix-ide/src/app/files/dgitProvider.js @@ -1,12 +1,12 @@ 'use strict' import { - Plugin + Plugin } from '@remixproject/engine' import git from 'isomorphic-git' import IpfsHttpClient from 'ipfs-http-client' import { - saveAs + saveAs } from 'file-saver' import http from 'isomorphic-git/http/web' @@ -16,596 +16,596 @@ const FormData = require('form-data') const axios = require('axios') const profile = { - name: 'dGitProvider', - displayName: 'Decentralized git', - description: 'Decentralized git provider', - icon: 'assets/img/fileManager.webp', - version: '0.0.1', - methods: ['init', 'localStorageUsed', 'addremote', 'delremote', 'remotes', 'fetch', 'clone', 'export', 'import', 'status', 'log', 'commit', 'add', 'remove', 'rm', 'lsfiles', 'readblob', 'resolveref', 'branches', 'branch', 'checkout', 'currentbranch', 'push', 'pin', 'pull', 'pinList', 'unPin', 'setIpfsConfig', 'zip', 'setItem', 'getItem'], - kind: 'file-system' + name: 'dGitProvider', + displayName: 'Decentralized git', + description: 'Decentralized git provider', + icon: 'assets/img/fileManager.webp', + version: '0.0.1', + methods: ['init', 'localStorageUsed', 'addremote', 'delremote', 'remotes', 'fetch', 'clone', 'export', 'import', 'status', 'log', 'commit', 'add', 'remove', 'rm', 'lsfiles', 'readblob', 'resolveref', 'branches', 'branch', 'checkout', 'currentbranch', 'push', 'pin', 'pull', 'pinList', 'unPin', 'setIpfsConfig', 'zip', 'setItem', 'getItem'], + kind: 'file-system' } class DGitProvider extends Plugin { - constructor () { - super(profile) - this.ipfsconfig = { - host: 'jqgt.remixproject.org', - port: 443, - protocol: 'https', - ipfsurl: 'https://jqgt.remixproject.org/ipfs/' - } - this.globalIPFSConfig = { - host: 'ipfs.io', - port: 443, - protocol: 'https', - ipfsurl: 'https://ipfs.io/ipfs/' - } - this.remixIPFS = { - host: 'jqgt.remixproject.org', - port: 443, - protocol: 'https', - ipfsurl: 'https://jqgt.remixproject.org/ipfs/' - } - this.ipfsSources = [this.remixIPFS, this.globalIPFSConfig, this.ipfsconfig] - } - - async getGitConfig () { - const workspace = await this.call('filePanel', 'getCurrentWorkspace') - - if (!workspace) return - return { - fs: window.remixFileSystemCallback, - dir: addSlash(workspace.absolutePath) - } - } - - async parseInput (input) { - return { - corsProxy: 'https://corsproxy.remixproject.org/', - http, - onAuth: url => { - url - const auth = { - username: input.token, - password: '' - } - return auth - } - } - } - - async init (input) { - await git.init({ - ...await this.getGitConfig(), - defaultBranch: (input && input.branch) || 'main' - }) - this.emit('init') - } - - async status (cmd) { - const status = await git.statusMatrix({ - ...await this.getGitConfig(), - ...cmd - }) - return status - } - - async add (cmd) { - await git.add({ - ...await this.getGitConfig(), - ...cmd - }) - this.emit('add') - } - - async rm (cmd) { - await git.remove({ - ...await this.getGitConfig(), - ...cmd - }) - } + constructor () { + super(profile) + this.ipfsconfig = { + host: 'jqgt.remixproject.org', + port: 443, + protocol: 'https', + ipfsurl: 'https://jqgt.remixproject.org/ipfs/' + } + this.globalIPFSConfig = { + host: 'ipfs.io', + port: 443, + protocol: 'https', + ipfsurl: 'https://ipfs.io/ipfs/' + } + this.remixIPFS = { + host: 'jqgt.remixproject.org', + port: 443, + protocol: 'https', + ipfsurl: 'https://jqgt.remixproject.org/ipfs/' + } + this.ipfsSources = [this.remixIPFS, this.globalIPFSConfig, this.ipfsconfig] + } + + async getGitConfig () { + const workspace = await this.call('filePanel', 'getCurrentWorkspace') + + if (!workspace) return + return { + fs: window.remixFileSystemCallback, + dir: addSlash(workspace.absolutePath) + } + } + + async parseInput (input) { + return { + corsProxy: 'https://corsproxy.remixproject.org/', + http, + onAuth: url => { + url + const auth = { + username: input.token, + password: '' + } + return auth + } + } + } + + async init (input) { + await git.init({ + ...await this.getGitConfig(), + defaultBranch: (input && input.branch) || 'main' + }) + this.emit('init') + } - async checkout (cmd, refresh = true) { - await git.checkout({ - ...await this.getGitConfig(), - ...cmd - }) - if (refresh) { - setTimeout(async () => { - await this.call('fileManager', 'refresh') - }, 1000) - } - this.emit('checkout') - } + async status (cmd) { + const status = await git.statusMatrix({ + ...await this.getGitConfig(), + ...cmd + }) + return status + } - async log (cmd) { - const status = await git.log({ - ...await this.getGitConfig(), - ...cmd - }) - return status - } + async add (cmd) { + await git.add({ + ...await this.getGitConfig(), + ...cmd + }) + this.emit('add') + } - async remotes (config) { - let remotes = [] - try { - remotes = await git.listRemotes({ ...config ? config : await this.getGitConfig() }) - } catch (e) { - // do nothing - } - return remotes - } + async rm (cmd) { + await git.remove({ + ...await this.getGitConfig(), + ...cmd + }) + } - async branch (cmd, refresh = true) { - const status = await git.branch({ - ...await this.getGitConfig(), - ...cmd - }) - if (refresh) { - setTimeout(async () => { - await this.call('fileManager', 'refresh') - }, 1000) - } - this.emit('branch') - return status - } + async checkout (cmd, refresh = true) { + await git.checkout({ + ...await this.getGitConfig(), + ...cmd + }) + if (refresh) { + setTimeout(async () => { + await this.call('fileManager', 'refresh') + }, 1000) + } + this.emit('checkout') + } + + async log (cmd) { + const status = await git.log({ + ...await this.getGitConfig(), + ...cmd + }) + return status + } + + async remotes (config) { + let remotes = [] + try { + remotes = await git.listRemotes({ ...config ? config : await this.getGitConfig() }) + } catch (e) { + // do nothing + } + return remotes + } + + async branch (cmd, refresh = true) { + const status = await git.branch({ + ...await this.getGitConfig(), + ...cmd + }) + if (refresh) { + setTimeout(async () => { + await this.call('fileManager', 'refresh') + }, 1000) + } + this.emit('branch') + return status + } + + async currentbranch (config) { + try{ + const defaultConfig = await this.getGitConfig() + const cmd = config ? defaultConfig ? { ...defaultConfig, ...config } : config : defaultConfig + const name = await git.currentBranch(cmd) + + return name + }catch(e){ + return '' + } + } + + async branches (config) { + try{ + const defaultConfig = await this.getGitConfig() + const cmd = config ? defaultConfig ? { ...defaultConfig, ...config } : config : defaultConfig + const remotes = await this.remotes(config) + let branches = [] + branches = (await git.listBranches(cmd)).map((branch) => { return { remote: undefined, name: branch } }) + for (const remote of remotes) { + cmd.remote = remote.remote + const remotebranches = (await git.listBranches(cmd)).map((branch) => { return { remote: remote.remote, name: branch } }) + branches = [...branches, ...remotebranches] + } + return branches + }catch(e){ + return [] + } + } + + async commit (cmd) { + await this.init() + try { + const sha = await git.commit({ + ...await this.getGitConfig(), + ...cmd + }) + this.emit('commit') + return sha + } catch (e) { + throw new Error(e) + } + } + + async lsfiles (cmd) { + const filesInStaging = await git.listFiles({ + ...await this.getGitConfig(), + ...cmd + }) + return filesInStaging + } - async currentbranch (config) { - try{ - const defaultConfig = await this.getGitConfig() - const cmd = config ? defaultConfig ? { ...defaultConfig, ...config } : config : defaultConfig - const name = await git.currentBranch(cmd) + async resolveref (cmd) { + const oid = await git.resolveRef({ + ...await this.getGitConfig(), + ...cmd + }) + return oid + } - return name - }catch(e){ - return '' - } - } + async readblob (cmd) { + const readBlobResult = await git.readBlob({ + ...await this.getGitConfig(), + ...cmd + }) + return readBlobResult + } - async branches (config) { - try{ - const defaultConfig = await this.getGitConfig() - const cmd = config ? defaultConfig ? { ...defaultConfig, ...config } : config : defaultConfig - const remotes = await this.remotes(config) - let branches = [] - branches = (await git.listBranches(cmd)).map((branch) => { return { remote: undefined, name: branch } }) - for (const remote of remotes) { - cmd.remote = remote.remote - const remotebranches = (await git.listBranches(cmd)).map((branch) => { return { remote: remote.remote, name: branch } }) - branches = [...branches, ...remotebranches] + async setIpfsConfig (config) { + this.ipfsconfig = config + return new Promise((resolve) => { + resolve(this.checkIpfsConfig()) + }) + } + + async checkIpfsConfig (config) { + this.ipfs = IpfsHttpClient(config || this.ipfsconfig) + try { + await this.ipfs.config.getAll() + return true + } catch (e) { + return false + } + } + + async addremote (input) { + await git.addRemote({ ...await this.getGitConfig(), url: input.url, remote: input.remote }) + } + + async delremote (input) { + await git.deleteRemote({ ...await this.getGitConfig(), remote: input.remote }) + } + + async localStorageUsed () { + return this.calculateLocalStorage() + } + + async clone (input, workspaceName, workspaceExists = false) { + const permission = await this.askUserPermission('clone', 'Import multiple files into your workspaces.') + if (!permission) return false + if (this.calculateLocalStorage() > 10000) throw new Error('The local storage of the browser is full.') + if (!workspaceExists) await this.call('filePanel', 'createWorkspace', workspaceName || `workspace_${Date.now()}`, true) + const cmd = { + url: input.url, + singleBranch: input.singleBranch, + ref: input.branch, + depth: input.depth || 10, + ...await this.parseInput(input), + ...await this.getGitConfig() + } + + const result = await git.clone(cmd) + if (!workspaceExists) { + setTimeout(async () => { + await this.call('fileManager', 'refresh') + }, 1000) + } + this.emit('clone') + return result + } + + async push (input) { + const cmd = { + force: input.force, + ref: input.ref, + remoteRef: input.remoteRef, + remote: input.remote, + author: { + name: input.name, + email: input.email + }, + ...await this.parseInput(input), + ...await this.getGitConfig() + } + return await git.push(cmd) + } + + async pull (input) { + const cmd = { + ref: input.ref, + remoteRef: input.remoteRef, + author: { + name: input.name, + email: input.email + }, + remote: input.remote, + ...await this.parseInput(input), + ...await this.getGitConfig() + } + const result = await git.pull(cmd) + setTimeout(async () => { + await this.call('fileManager', 'refresh') + }, 1000) + return result + } + + async fetch (input) { + const cmd = { + ref: input.ref, + remoteRef: input.remoteRef, + author: { + name: input.name, + email: input.email + }, + remote: input.remote, + ...await this.parseInput(input), + ...await this.getGitConfig() + } + const result = await git.fetch(cmd) + setTimeout(async () => { + await this.call('fileManager', 'refresh') + }, 1000) + return result + } + + async export (config) { + if (!this.checkIpfsConfig(config)) return false + const workspace = await this.call('filePanel', 'getCurrentWorkspace') + const files = await this.getDirectory('/') + this.filesToSend = [] + for (const file of files) { + const c = await window.remixFileSystem.readFile(`${workspace.absolutePath}/${file}`) + const ob = { + path: file, + content: c + } + this.filesToSend.push(ob) + } + const addOptions = { + wrapWithDirectory: true + } + const r = await this.ipfs.add(this.filesToSend, addOptions) + return r.cid.string + } + + async pin (pinataApiKey, pinataSecretApiKey) { + const workspace = await this.call('filePanel', 'getCurrentWorkspace') + const files = await this.getDirectory('/') + this.filesToSend = [] + + const data = new FormData() + for (const file of files) { + const c = await window.remixFileSystem.readFile(`${workspace.absolutePath}/${file}`) + data.append('file', new Blob([c]), `base/${file}`) + } + // get last commit data + let ob + try { + const commits = await this.log({ ref: 'HEAD' }) + ob = { + ref: commits[0].oid, + message: commits[0].commit.message, + commits: JSON.stringify(commits.map((commit) => { + return { + oid: commit.oid, + commit: { + parent: commit.commit?.parent, + tree: commit.commit?.tree, + message: commit.commit?.message, + committer: { + timestamp: commit.commit?.committer?.timestamp + } } - return branches - }catch(e){ - return [] - } - } - - async commit (cmd) { - await this.init() - try { - const sha = await git.commit({ - ...await this.getGitConfig(), - ...cmd - }) - this.emit('commit') - return sha - } catch (e) { - throw new Error(e) - } - } - - async lsfiles (cmd) { - const filesInStaging = await git.listFiles({ - ...await this.getGitConfig(), - ...cmd - }) - return filesInStaging - } - - async resolveref (cmd) { - const oid = await git.resolveRef({ - ...await this.getGitConfig(), - ...cmd - }) - return oid - } - - async readblob (cmd) { - const readBlobResult = await git.readBlob({ - ...await this.getGitConfig(), - ...cmd - }) - return readBlobResult - } - - async setIpfsConfig (config) { - this.ipfsconfig = config - return new Promise((resolve) => { - resolve(this.checkIpfsConfig()) + } + })) + } + } catch (e) { + ob = { + ref: 'no commits', + message: 'no commits' + } + } + const today = new Date() + const metadata = JSON.stringify({ + name: `remix - ${workspace.name} - ${today.toLocaleString()}`, + keyvalues: ob + }) + const pinataOptions = JSON.stringify({ + wrapWithDirectory: false + }) + data.append('pinataOptions', pinataOptions) + data.append('pinataMetadata', metadata) + const url = 'https://api.pinata.cloud/pinning/pinFileToIPFS' + try { + const result = await axios + .post(url, data, { + maxBodyLength: 'Infinity', + headers: { + 'Content-Type': `multipart/form-data; boundary=${data._boundary}`, + pinata_api_key: pinataApiKey, + pinata_secret_api_key: pinataSecretApiKey + } + }).catch((e) => { + console.log(e) }) - } - - async checkIpfsConfig (config) { - this.ipfs = IpfsHttpClient(config || this.ipfsconfig) - try { - await this.ipfs.config.getAll() - return true - } catch (e) { - return false - } - } - - async addremote (input) { - await git.addRemote({ ...await this.getGitConfig(), url: input.url, remote: input.remote }) - } - - async delremote (input) { - await git.deleteRemote({ ...await this.getGitConfig(), remote: input.remote }) - } - - async localStorageUsed () { - return this.calculateLocalStorage() - } - - async clone (input, workspaceName, workspaceExists = false) { - const permission = await this.askUserPermission('clone', 'Import multiple files into your workspaces.') - if (!permission) return false - if (this.calculateLocalStorage() > 10000) throw new Error('The local storage of the browser is full.') - if (!workspaceExists) await this.call('filePanel', 'createWorkspace', workspaceName || `workspace_${Date.now()}`, true) - const cmd = { - url: input.url, - singleBranch: input.singleBranch, - ref: input.branch, - depth: input.depth || 10, - ...await this.parseInput(input), - ...await this.getGitConfig() - } - - const result = await git.clone(cmd) - if (!workspaceExists) { - setTimeout(async () => { - await this.call('fileManager', 'refresh') - }, 1000) - } - this.emit('clone') - return result - } - - async push (input) { - const cmd = { - force: input.force, - ref: input.ref, - remoteRef: input.remoteRef, - remote: input.remote, - author: { - name: input.name, - email: input.email - }, - ...await this.parseInput(input), - ...await this.getGitConfig() - } - return await git.push(cmd) - } - - async pull (input) { - const cmd = { - ref: input.ref, - remoteRef: input.remoteRef, - author: { - name: input.name, - email: input.email - }, - remote: input.remote, - ...await this.parseInput(input), - ...await this.getGitConfig() - } - const result = await git.pull(cmd) - setTimeout(async () => { - await this.call('fileManager', 'refresh') - }, 1000) - return result - } - - async fetch (input) { - const cmd = { - ref: input.ref, - remoteRef: input.remoteRef, - author: { - name: input.name, - email: input.email - }, - remote: input.remote, - ...await this.parseInput(input), - ...await this.getGitConfig() - } - const result = await git.fetch(cmd) - setTimeout(async () => { - await this.call('fileManager', 'refresh') - }, 1000) - return result - } - - async export (config) { - if (!this.checkIpfsConfig(config)) return false - const workspace = await this.call('filePanel', 'getCurrentWorkspace') - const files = await this.getDirectory('/') - this.filesToSend = [] - for (const file of files) { - const c = await window.remixFileSystem.readFile(`${workspace.absolutePath}/${file}`) - const ob = { - path: file, - content: c - } - this.filesToSend.push(ob) - } - const addOptions = { - wrapWithDirectory: true - } - const r = await this.ipfs.add(this.filesToSend, addOptions) - return r.cid.string - } - - async pin (pinataApiKey, pinataSecretApiKey) { - const workspace = await this.call('filePanel', 'getCurrentWorkspace') - const files = await this.getDirectory('/') - this.filesToSend = [] - - const data = new FormData() - for (const file of files) { - const c = await window.remixFileSystem.readFile(`${workspace.absolutePath}/${file}`) - data.append('file', new Blob([c]), `base/${file}`) - } - // get last commit data - let ob - try { - const commits = await this.log({ ref: 'HEAD' }) - ob = { - ref: commits[0].oid, - message: commits[0].commit.message, - commits: JSON.stringify(commits.map((commit) => { - return { - oid: commit.oid, - commit: { - parent: commit.commit?.parent, - tree: commit.commit?.tree, - message: commit.commit?.message, - committer: { - timestamp: commit.commit?.committer?.timestamp - } - } - } - })) - } - } catch (e) { - ob = { - ref: 'no commits', - message: 'no commits' - } - } - const today = new Date() - const metadata = JSON.stringify({ - name: `remix - ${workspace.name} - ${today.toLocaleString()}`, - keyvalues: ob + // also commit to remix IPFS for availability after pinning to Pinata + return await this.export(this.remixIPFS) || result.data.IpfsHash + } catch (error) { + throw new Error(error) + } + } + + async pinList (pinataApiKey, pinataSecretApiKey) { + const url = 'https://api.pinata.cloud/data/pinList?status=pinned' + try { + const result = await axios + .get(url, { + maxBodyLength: 'Infinity', + headers: { + pinata_api_key: pinataApiKey, + pinata_secret_api_key: pinataSecretApiKey + } + }).catch((e) => { + console.log('Pinata unreachable') }) - const pinataOptions = JSON.stringify({ - wrapWithDirectory: false + return result.data + } catch (error) { + throw new Error(error) + } + } + + async unPin (pinataApiKey, pinataSecretApiKey, hashToUnpin) { + const url = `https://api.pinata.cloud/pinning/unpin/${hashToUnpin}` + try { + await axios + .delete(url, { + headers: { + pinata_api_key: pinataApiKey, + pinata_secret_api_key: pinataSecretApiKey + } }) - data.append('pinataOptions', pinataOptions) - data.append('pinataMetadata', metadata) - const url = 'https://api.pinata.cloud/pinning/pinFileToIPFS' + return true + } catch (error) { + throw new Error(error) + } + } + + async importIPFSFiles (config, cid, workspace) { + const ipfs = IpfsHttpClient(config) + let result = false + try { + const data = ipfs.get(cid, { timeout: 60000 }) + for await (const file of data) { + if (file.path) result = true + file.path = file.path.replace(cid, '') + if (!file.content) { + continue + } + const content = [] + for await (const chunk of file.content) { + content.push(chunk) + } + const dir = path.dirname(file.path) try { - const result = await axios - .post(url, data, { - maxBodyLength: 'Infinity', - headers: { - 'Content-Type': `multipart/form-data; boundary=${data._boundary}`, - pinata_api_key: pinataApiKey, - pinata_secret_api_key: pinataSecretApiKey - } - }).catch((e) => { - console.log(e) - }) - // also commit to remix IPFS for availability after pinning to Pinata - return await this.export(this.remixIPFS) || result.data.IpfsHash - } catch (error) { - throw new Error(error) - } - } - - async pinList (pinataApiKey, pinataSecretApiKey) { - const url = 'https://api.pinata.cloud/data/pinList?status=pinned' + await this.createDirectories(`${workspace.absolutePath}/${dir}`) + } catch (e) { throw new Error(e) } try { - const result = await axios - .get(url, { - maxBodyLength: 'Infinity', - headers: { - pinata_api_key: pinataApiKey, - pinata_secret_api_key: pinataSecretApiKey - } - }).catch((e) => { - console.log('Pinata unreachable') - }) - return result.data - } catch (error) { - throw new Error(error) - } - } - - async unPin (pinataApiKey, pinataSecretApiKey, hashToUnpin) { - const url = `https://api.pinata.cloud/pinning/unpin/${hashToUnpin}` - try { - await axios - .delete(url, { - headers: { - pinata_api_key: pinataApiKey, - pinata_secret_api_key: pinataSecretApiKey - } - }) - return true - } catch (error) { - throw new Error(error) - } - } - - async importIPFSFiles (config, cid, workspace) { - const ipfs = IpfsHttpClient(config) - let result = false - try { - const data = ipfs.get(cid, { timeout: 60000 }) - for await (const file of data) { - if (file.path) result = true - file.path = file.path.replace(cid, '') - if (!file.content) { - continue - } - const content = [] - for await (const chunk of file.content) { - content.push(chunk) - } - const dir = path.dirname(file.path) - try { - await this.createDirectories(`${workspace.absolutePath}/${dir}`) - } catch (e) { throw new Error(e) } - try { - await window.remixFileSystem.writeFile(`${workspace.absolutePath}/${file.path}`, Buffer.concat(content) || new Uint8Array()) - } catch (e) { throw new Error(e) } - } - } catch (e) { - throw new Error(e) - } - return result - } - - calculateLocalStorage () { - var _lsTotal = 0 - var _xLen; var _x - for (_x in localStorage) { - // eslint-disable-next-line no-prototype-builtins - if (!localStorage.hasOwnProperty(_x)) { - continue - } - _xLen = ((localStorage[_x].length + _x.length) * 2) - _lsTotal += _xLen - } - return (_lsTotal / 1024).toFixed(2) - } - - async import (cmd) { - const permission = await this.askUserPermission('import', 'Import multiple files into your workspaces.') - if (!permission) return false - if (this.calculateLocalStorage() > 10000) throw new Error('The local storage of the browser is full.') - const cid = cmd.cid - await this.call('filePanel', 'createWorkspace', `workspace_${Date.now()}`, true) - const workspace = await this.call('filePanel', 'getCurrentWorkspace') - let result - if (cmd.local) { - result = await this.importIPFSFiles(this.ipfsconfig, cid, workspace) + await window.remixFileSystem.writeFile(`${workspace.absolutePath}/${file.path}`, Buffer.concat(content) || new Uint8Array()) + } catch (e) { throw new Error(e) } + } + } catch (e) { + throw new Error(e) + } + return result + } + + calculateLocalStorage () { + var _lsTotal = 0 + var _xLen; var _x + for (_x in localStorage) { + // eslint-disable-next-line no-prototype-builtins + if (!localStorage.hasOwnProperty(_x)) { + continue + } + _xLen = ((localStorage[_x].length + _x.length) * 2) + _lsTotal += _xLen + } + return (_lsTotal / 1024).toFixed(2) + } + + async import (cmd) { + const permission = await this.askUserPermission('import', 'Import multiple files into your workspaces.') + if (!permission) return false + if (this.calculateLocalStorage() > 10000) throw new Error('The local storage of the browser is full.') + const cid = cmd.cid + await this.call('filePanel', 'createWorkspace', `workspace_${Date.now()}`, true) + const workspace = await this.call('filePanel', 'getCurrentWorkspace') + let result + if (cmd.local) { + result = await this.importIPFSFiles(this.ipfsconfig, cid, workspace) + } else { + result = await this.importIPFSFiles(this.remixIPFS, cid, workspace) || await this.importIPFSFiles(this.ipfsconfig, cid, workspace) || await this.importIPFSFiles(this.globalIPFSConfig, cid, workspace) + } + setTimeout(async () => { + await this.call('fileManager', 'refresh') + }, 1000) + if (!result) throw new Error(`Cannot pull files from IPFS at ${cid}`) + } + + async getItem (name) { + if (typeof window !== 'undefined') { + return window.localStorage.getItem(name) + } + } + + async setItem (name, content) { + try { + if (typeof window !== 'undefined') { + window.localStorage.setItem(name, content) + } + } catch (e) { + console.log(e) + return false + } + return true + } + + async zip () { + const zip = new JSZip() + const workspace = await this.call('filePanel', 'getCurrentWorkspace') + const files = await this.getDirectory('/') + this.filesToSend = [] + for (const file of files) { + const c = await window.remixFileSystem.readFile(`${workspace.absolutePath}/${file}`) + zip.file(file, c) + } + await zip.generateAsync({ + type: 'blob' + }) + .then(function (content) { + saveAs(content, `${workspace.name}.zip`) + }) + } + + async createDirectories (strdirectories) { + const ignore = ['.', '/.', ''] + if (ignore.indexOf(strdirectories) > -1) return false + const directories = strdirectories.split('/') + for (let i = 0; i < directories.length; i++) { + let previouspath = '' + if (i > 0) previouspath = '/' + directories.slice(0, i).join('/') + const finalPath = previouspath + '/' + directories[i] + try { + if (!await window.remixFileSystem.exists(finalPath)) { + await window.remixFileSystem.mkdir(finalPath) + } + } catch (e) { + console.log(e) + } + } + } + + async getDirectory (dir) { + let result = [] + const files = await this.call('fileManager', 'readdir', dir) + const fileArray = normalize(files) + for (const fi of fileArray) { + if (fi) { + const type = fi.data.isDirectory + if (type === true) { + result = [ + ...result, + ...(await this.getDirectory( + `${fi.filename}` + )) + ] } else { - result = await this.importIPFSFiles(this.remixIPFS, cid, workspace) || await this.importIPFSFiles(this.ipfsconfig, cid, workspace) || await this.importIPFSFiles(this.globalIPFSConfig, cid, workspace) - } - setTimeout(async () => { - await this.call('fileManager', 'refresh') - }, 1000) - if (!result) throw new Error(`Cannot pull files from IPFS at ${cid}`) - } - - async getItem (name) { - if (typeof window !== 'undefined') { - return window.localStorage.getItem(name) + result = [...result, fi.filename] } + } } - - async setItem (name, content) { - try { - if (typeof window !== 'undefined') { - window.localStorage.setItem(name, content) - } - } catch (e) { - console.log(e) - return false - } - return true - } - - async zip () { - const zip = new JSZip() - const workspace = await this.call('filePanel', 'getCurrentWorkspace') - const files = await this.getDirectory('/') - this.filesToSend = [] - for (const file of files) { - const c = await window.remixFileSystem.readFile(`${workspace.absolutePath}/${file}`) - zip.file(file, c) - } - await zip.generateAsync({ - type: 'blob' - }) - .then(function (content) { - saveAs(content, `${workspace.name}.zip`) - }) - } - - async createDirectories (strdirectories) { - const ignore = ['.', '/.', ''] - if (ignore.indexOf(strdirectories) > -1) return false - const directories = strdirectories.split('/') - for (let i = 0; i < directories.length; i++) { - let previouspath = '' - if (i > 0) previouspath = '/' + directories.slice(0, i).join('/') - const finalPath = previouspath + '/' + directories[i] - try { - if (!await window.remixFileSystem.exists(finalPath)) { - await window.remixFileSystem.mkdir(finalPath) - } - } catch (e) { - console.log(e) - } - } - } - - async getDirectory (dir) { - let result = [] - const files = await this.call('fileManager', 'readdir', dir) - const fileArray = normalize(files) - for (const fi of fileArray) { - if (fi) { - const type = fi.data.isDirectory - if (type === true) { - result = [ - ...result, - ...(await this.getDirectory( - `${fi.filename}` - )) - ] - } else { - result = [...result, fi.filename] - } - } - } - return result - } + return result + } } const addSlash = (file) => { - if (!file.startsWith('/'))file = '/' + file - return file + if (!file.startsWith('/'))file = '/' + file + return file } const normalize = (filesList) => { - const folders = [] - const files = [] - Object.keys(filesList || {}).forEach(key => { - if (filesList[key].isDirectory) { - folders.push({ - filename: key, - data: filesList[key] - }) - } else { - files.push({ - filename: key, - data: filesList[key] - }) - } - }) - return [...folders, ...files] + const folders = [] + const files = [] + Object.keys(filesList || {}).forEach(key => { + if (filesList[key].isDirectory) { + folders.push({ + filename: key, + data: filesList[key] + }) + } else { + files.push({ + filename: key, + data: filesList[key] + }) + } + }) + return [...folders, ...files] } module.exports = DGitProvider diff --git a/apps/remix-ide/src/app/files/fileManager.ts b/apps/remix-ide/src/app/files/fileManager.ts index 2c3dcccda0..1f4827a8bf 100644 --- a/apps/remix-ide/src/app/files/fileManager.ts +++ b/apps/remix-ide/src/app/files/fileManager.ts @@ -15,909 +15,909 @@ import { RemixAppManager } from '../../remixAppManager' */ const profile = { - name: 'fileManager', - displayName: 'File manager', - description: 'Service - read/write to any files or folders, require giving permissions', - icon: 'assets/img/fileManager.webp', - permission: true, - version: packageJson.version, - methods: ['closeAllFiles', 'closeFile', 'file', 'exists', 'open', 'writeFile', 'readFile', 'copyFile', 'copyDir', 'rename', 'mkdir', - 'readdir', 'dirList', 'fileList', 'remove', 'getCurrentFile', 'getFile', 'getFolder', 'setFile', 'switchFile', 'refresh', - 'getProviderOf', 'getProviderByName', 'getPathFromUrl', 'getUrlFromPath', 'saveCurrentFile', 'setBatchFiles', 'isGitRepo'], - kind: 'file-system' + name: 'fileManager', + displayName: 'File manager', + description: 'Service - read/write to any files or folders, require giving permissions', + icon: 'assets/img/fileManager.webp', + permission: true, + version: packageJson.version, + methods: ['closeAllFiles', 'closeFile', 'file', 'exists', 'open', 'writeFile', 'readFile', 'copyFile', 'copyDir', 'rename', 'mkdir', + 'readdir', 'dirList', 'fileList', 'remove', 'getCurrentFile', 'getFile', 'getFolder', 'setFile', 'switchFile', 'refresh', + 'getProviderOf', 'getProviderByName', 'getPathFromUrl', 'getUrlFromPath', 'saveCurrentFile', 'setBatchFiles', 'isGitRepo'], + kind: 'file-system' } const errorMsg = { - ENOENT: 'No such file or directory', - EISDIR: 'Path is a directory', - ENOTDIR: 'Path is not on a directory', - EEXIST: 'File already exists', - EPERM: 'Permission denied' + ENOENT: 'No such file or directory', + EISDIR: 'Path is a directory', + ENOTDIR: 'Path is not on a directory', + EEXIST: 'File already exists', + EPERM: 'Permission denied' } const createError = (err) => { - return new Error(`${errorMsg[err.code]} ${err.message || ''}`) + return new Error(`${errorMsg[err.code]} ${err.message || ''}`) } class FileManager extends Plugin { - mode: string - openedFiles: any - events: EventEmitter - editor: any - _components: any - appManager: RemixAppManager - _deps: any - getCurrentFile: () => any - getFile: (path: any) => Promise - getFolder: (path: any) => Promise - setFile: (path: any, data: any) => Promise - switchFile: (path: any) => Promise - constructor(editor, appManager) { - super(profile) - this.mode = 'browser' - this.openedFiles = {} // list all opened files - this.events = new EventEmitter() - this.editor = editor - this._components = {} - this._components.registry = Registry.getInstance() - this.appManager = appManager - this.init() - } - - getOpenedFiles() { - return this.openedFiles - } - - setMode(mode) { - this.mode = mode - } - - limitPluginScope(path) { - return path.replace(/^\/browser\//, '').replace(/^browser\//, '') // forbids plugin to access the root filesystem - } - - normalize(path) { - return path.replace(/^\/+/, '') - } - - /** + mode: string + openedFiles: any + events: EventEmitter + editor: any + _components: any + appManager: RemixAppManager + _deps: any + getCurrentFile: () => any + getFile: (path: any) => Promise + getFolder: (path: any) => Promise + setFile: (path: any, data: any) => Promise + switchFile: (path: any) => Promise + constructor(editor, appManager) { + super(profile) + this.mode = 'browser' + this.openedFiles = {} // list all opened files + this.events = new EventEmitter() + this.editor = editor + this._components = {} + this._components.registry = Registry.getInstance() + this.appManager = appManager + this.init() + } + + getOpenedFiles() { + return this.openedFiles + } + + setMode(mode) { + this.mode = mode + } + + limitPluginScope(path) { + return path.replace(/^\/browser\//, '').replace(/^browser\//, '') // forbids plugin to access the root filesystem + } + + normalize(path) { + return path.replace(/^\/+/, '') + } + + /** * Emit error if path doesn't exist * @param {string} path path of the file/directory * @param {string} message message to display if path doesn't exist. */ - async _handleExists(path: string, message?: string) { - const exists = await this.exists(path) + async _handleExists(path: string, message?: string) { + const exists = await this.exists(path) - if (!exists) { - throw createError({ code: 'ENOENT', message }) - } + if (!exists) { + throw createError({ code: 'ENOENT', message }) } + } - /** + /** * Emit error if path is not a file * @param {string} path path of the file/directory * @param {string} message message to display if path is not a file. */ - async _handleIsFile(path, message) { - const isFile = await this.isFile(path) + async _handleIsFile(path, message) { + const isFile = await this.isFile(path) - if (!isFile) { - throw createError({ code: 'EISDIR', message }) - } + if (!isFile) { + throw createError({ code: 'EISDIR', message }) } + } - /** + /** * Emit error if path is not a directory * @param {string} path path of the file/directory * @param {string} message message to display if path is not a directory. */ - async _handleIsDir(path: string, message?: string) { - const isDir = await this.isDirectory(path) + async _handleIsDir(path: string, message?: string) { + const isDir = await this.isDirectory(path) - if (!isDir) { - throw createError({ code: 'ENOTDIR', message }) - } + if (!isDir) { + throw createError({ code: 'ENOTDIR', message }) } + } - /** The current opened file */ - file() { - try { - const file = this.currentFile() + /** The current opened file */ + file() { + try { + const file = this.currentFile() - if (!file) throw createError({ code: 'ENOENT', message: 'No file selected' }) - return file - } catch (e) { - throw new Error(e) - } + if (!file) throw createError({ code: 'ENOENT', message: 'No file selected' }) + return file + } catch (e) { + throw new Error(e) } + } - /** + /** * Verify if the path exists (directory or file) * @param {string} path path of the directory or file * @returns {boolean} true if the path exists */ - async exists(path) { - try { - path = this.normalize(path) - path = this.limitPluginScope(path) - const provider = this.fileProviderOf(path) - const result = await provider.exists(path) + async exists(path) { + try { + path = this.normalize(path) + path = this.limitPluginScope(path) + const provider = this.fileProviderOf(path) + const result = await provider.exists(path) - return result - } catch (e) { - throw new Error(e) - } + return result + } catch (e) { + throw new Error(e) } + } - /* + /* * refresh the file explorer */ - refresh() { - const provider = this.fileProviderOf('/') - // emit rootFolderChanged so that File Explorer reloads the file tree - provider.event.emit('rootFolderChanged', provider.workspace || '/') - this.emit('rootFolderChanged', provider.workspace || '/') - } - - /** + refresh() { + const provider = this.fileProviderOf('/') + // emit rootFolderChanged so that File Explorer reloads the file tree + provider.event.emit('rootFolderChanged', provider.workspace || '/') + this.emit('rootFolderChanged', provider.workspace || '/') + } + + /** * Verify if the path provided is a file * @param {string} path path of the directory or file * @returns {boolean} true if path is a file. */ - async isFile(path) { - const provider = this.fileProviderOf(path) - const result = await provider.isFile(path) - return result - } + async isFile(path) { + const provider = this.fileProviderOf(path) + const result = await provider.isFile(path) + return result + } - /** + /** * Verify if the path provided is a directory * @param {string} path path of the directory * @returns {boolean} true if path is a directory. */ - async isDirectory(path) { - const provider = this.fileProviderOf(path) - const result = await provider.isDirectory(path) + async isDirectory(path) { + const provider = this.fileProviderOf(path) + const result = await provider.isDirectory(path) - return result - } + return result + } - /** + /** * Open the content of the file in the context (eg: Editor) * @param {string} path path of the file * @returns {void} */ - async open(path) { - path = this.normalize(path) - path = this.limitPluginScope(path) - path = this.getPathFromUrl(path).file - await this._handleExists(path, `Cannot open file ${path}`) - await this._handleIsFile(path, `Cannot open file ${path}`) - await this.openFile(path) - } - - /** + async open(path) { + path = this.normalize(path) + path = this.limitPluginScope(path) + path = this.getPathFromUrl(path).file + await this._handleExists(path, `Cannot open file ${path}`) + await this._handleIsFile(path, `Cannot open file ${path}`) + await this.openFile(path) + } + + /** * Set the content of a specific file * @param {string} path path of the file * @param {string} data content to write on the file * @returns {void} */ - async writeFile(path, data) { - try { - path = this.normalize(path) - path = this.limitPluginScope(path) - if (await this.exists(path)) { - await this._handleIsFile(path, `Cannot write file ${path}`) - return await this.setFileContent(path, data) - } else { - const ret = await this.setFileContent(path, data) - this.emit('fileAdded', path) - return ret - } - } catch (e) { - throw new Error(e) - } + async writeFile(path, data) { + try { + path = this.normalize(path) + path = this.limitPluginScope(path) + if (await this.exists(path)) { + await this._handleIsFile(path, `Cannot write file ${path}`) + return await this.setFileContent(path, data) + } else { + const ret = await this.setFileContent(path, data) + this.emit('fileAdded', path) + return ret + } + } catch (e) { + throw new Error(e) } + } - /** + /** * Return the content of a specific file * @param {string} path path of the file * @returns {string} content of the file */ - async readFile(path) { - try { - path = this.normalize(path) - path = this.limitPluginScope(path) - await this._handleExists(path, `Cannot read file ${path}`) - await this._handleIsFile(path, `Cannot read file ${path}`) - return this.getFileContent(path) - } catch (e) { - throw new Error(e) - } - } - - /** + async readFile(path) { + try { + path = this.normalize(path) + path = this.limitPluginScope(path) + await this._handleExists(path, `Cannot read file ${path}`) + await this._handleIsFile(path, `Cannot read file ${path}`) + return this.getFileContent(path) + } catch (e) { + throw new Error(e) + } + } + + /** * Upsert a file with the content of the source file * @param {string} src path of the source file * @param {string} dest path of the destrination file * @returns {void} */ - async copyFile(src: string, dest: string, customName?: string) { - try { - src = this.normalize(src) - dest = this.normalize(dest) - src = this.limitPluginScope(src) - dest = this.limitPluginScope(dest) - await this._handleExists(src, `Cannot copy from ${src}. Path does not exist.`) - await this._handleIsFile(src, `Cannot copy from ${src}. Path is not a file.`) - await this._handleExists(dest, `Cannot paste content into ${dest}. Path does not exist.`) - await this._handleIsDir(dest, `Cannot paste content into ${dest}. Path is not directory.`) - const content = await this.readFile(src) - let copiedFilePath = dest + (customName ? '/' + customName : '/' + `Copy_${helper.extractNameFromKey(src)}`) - copiedFilePath = await helper.createNonClashingNameAsync(copiedFilePath, this) - - await this.writeFile(copiedFilePath, content) - } catch (e) { - throw new Error(e) - } - } - - /** + async copyFile(src: string, dest: string, customName?: string) { + try { + src = this.normalize(src) + dest = this.normalize(dest) + src = this.limitPluginScope(src) + dest = this.limitPluginScope(dest) + await this._handleExists(src, `Cannot copy from ${src}. Path does not exist.`) + await this._handleIsFile(src, `Cannot copy from ${src}. Path is not a file.`) + await this._handleExists(dest, `Cannot paste content into ${dest}. Path does not exist.`) + await this._handleIsDir(dest, `Cannot paste content into ${dest}. Path is not directory.`) + const content = await this.readFile(src) + let copiedFilePath = dest + (customName ? '/' + customName : '/' + `Copy_${helper.extractNameFromKey(src)}`) + copiedFilePath = await helper.createNonClashingNameAsync(copiedFilePath, this) + + await this.writeFile(copiedFilePath, content) + } catch (e) { + throw new Error(e) + } + } + + /** * Upsert a directory with the content of the source directory * @param {string} src path of the source dir * @param {string} dest path of the destination dir * @returns {void} */ - async copyDir(src: string, dest: string, customName?: string) { - try { - src = this.normalize(src) - dest = this.normalize(dest) - src = this.limitPluginScope(src) - dest = this.limitPluginScope(dest) - await this._handleExists(src, `Cannot copy from ${src}. Path does not exist.`) - await this._handleIsDir(src, `Cannot copy from ${src}. Path is not a directory.`) - await this._handleExists(dest, `Cannot paste content into ${dest}. Path does not exist.`) - await this._handleIsDir(dest, `Cannot paste content into ${dest}. Path is not directory.`) - - const provider = this.fileProviderOf(src) - if (provider.isSubDirectory(src, dest)) { - this.call('notification', 'toast', recursivePasteToastMsg()) - } else { - await this.inDepthCopy(src, dest, customName) - } - } catch (e) { - throw new Error(e) - } - } - - async inDepthCopy(src: string, dest: string, customName?: string) { - const content = await this.readdir(src) - let copiedFolderPath = !customName ? dest + '/' + `Copy_${helper.extractNameFromKey(src)}` : dest + '/' + helper.extractNameFromKey(src) - copiedFolderPath = await helper.createNonClashingDirNameAsync(copiedFolderPath, this) - - await this.mkdir(copiedFolderPath) - - for (const [key, value] of Object.entries(content)) { - if (!value.isDirectory) { - await this.copyFile(key, copiedFolderPath, helper.extractNameFromKey(key)) - } else { - await this.inDepthCopy(key, copiedFolderPath, helper.extractNameFromKey(key)) - } - } - } - - /** + async copyDir(src: string, dest: string, customName?: string) { + try { + src = this.normalize(src) + dest = this.normalize(dest) + src = this.limitPluginScope(src) + dest = this.limitPluginScope(dest) + await this._handleExists(src, `Cannot copy from ${src}. Path does not exist.`) + await this._handleIsDir(src, `Cannot copy from ${src}. Path is not a directory.`) + await this._handleExists(dest, `Cannot paste content into ${dest}. Path does not exist.`) + await this._handleIsDir(dest, `Cannot paste content into ${dest}. Path is not directory.`) + + const provider = this.fileProviderOf(src) + if (provider.isSubDirectory(src, dest)) { + this.call('notification', 'toast', recursivePasteToastMsg()) + } else { + await this.inDepthCopy(src, dest, customName) + } + } catch (e) { + throw new Error(e) + } + } + + async inDepthCopy(src: string, dest: string, customName?: string) { + const content = await this.readdir(src) + let copiedFolderPath = !customName ? dest + '/' + `Copy_${helper.extractNameFromKey(src)}` : dest + '/' + helper.extractNameFromKey(src) + copiedFolderPath = await helper.createNonClashingDirNameAsync(copiedFolderPath, this) + + await this.mkdir(copiedFolderPath) + + for (const [key, value] of Object.entries(content)) { + if (!value.isDirectory) { + await this.copyFile(key, copiedFolderPath, helper.extractNameFromKey(key)) + } else { + await this.inDepthCopy(key, copiedFolderPath, helper.extractNameFromKey(key)) + } + } + } + + /** * Change the path of a file/directory * @param {string} oldPath current path of the file/directory * @param {string} newPath new path of the file/directory * @returns {void} */ - async rename(oldPath, newPath) { - try { - oldPath = this.normalize(oldPath) - newPath = this.normalize(newPath) - oldPath = this.limitPluginScope(oldPath) - newPath = this.limitPluginScope(newPath) - await this._handleExists(oldPath, `Cannot rename ${oldPath}`) - const isFile = await this.isFile(oldPath) - const newPathExists = await this.exists(newPath) - const provider = this.fileProviderOf(oldPath) - - if (isFile) { - if (newPathExists) { - this.call('notification', 'alert', { - id: 'fileManagerAlert', - message: 'File already exists' - }) - return - } - return provider.rename(oldPath, newPath, false) - } else { - if (newPathExists) { - this.call('notification', 'alert', { - id: 'fileManagerAlert', - message: 'Directory already exists' - }) - return - } - return provider.rename(oldPath, newPath, true) - } - } catch (e) { - throw new Error(e) - } - } - - async zipDir(dirPath, zip) { - const filesAndFolders = await this.readdir(dirPath) - for(let path in filesAndFolders) { - if (filesAndFolders[path].isDirectory) await this.zipDir(path, zip) - else { - path = this.normalize(path) - const content: any = await this.readFile(path) - zip.file(path, content) - } - } - } - - async download(path) { - try { - const downloadFileName = helper.extractNameFromKey(path) - if (await this.isDirectory(path)) { - const zip = new JSZip() - await this.zipDir(path, zip) - const content = await zip.generateAsync({type: 'blob'}) - saveAs(content, `${downloadFileName}.zip`) - } else { - path = this.normalize(path) - const content: any = await this.readFile(path) - saveAs(new Blob([content]), downloadFileName) - } - } catch (e) { - throw new Error(e) - } + async rename(oldPath, newPath) { + try { + oldPath = this.normalize(oldPath) + newPath = this.normalize(newPath) + oldPath = this.limitPluginScope(oldPath) + newPath = this.limitPluginScope(newPath) + await this._handleExists(oldPath, `Cannot rename ${oldPath}`) + const isFile = await this.isFile(oldPath) + const newPathExists = await this.exists(newPath) + const provider = this.fileProviderOf(oldPath) + + if (isFile) { + if (newPathExists) { + this.call('notification', 'alert', { + id: 'fileManagerAlert', + message: 'File already exists' + }) + return + } + return provider.rename(oldPath, newPath, false) + } else { + if (newPathExists) { + this.call('notification', 'alert', { + id: 'fileManagerAlert', + message: 'Directory already exists' + }) + return + } + return provider.rename(oldPath, newPath, true) + } + } catch (e) { + throw new Error(e) + } + } + + async zipDir(dirPath, zip) { + const filesAndFolders = await this.readdir(dirPath) + for(let path in filesAndFolders) { + if (filesAndFolders[path].isDirectory) await this.zipDir(path, zip) + else { + path = this.normalize(path) + const content: any = await this.readFile(path) + zip.file(path, content) + } + } + } + + async download(path) { + try { + const downloadFileName = helper.extractNameFromKey(path) + if (await this.isDirectory(path)) { + const zip = new JSZip() + await this.zipDir(path, zip) + const content = await zip.generateAsync({type: 'blob'}) + saveAs(content, `${downloadFileName}.zip`) + } else { + path = this.normalize(path) + const content: any = await this.readFile(path) + saveAs(new Blob([content]), downloadFileName) + } + } catch (e) { + throw new Error(e) } + } - /** + /** * Create a directory * @param {string} path path of the new directory * @returns {void} */ - async mkdir(path) { - try { - path = this.normalize(path) - path = this.limitPluginScope(path) - if (await this.exists(path)) { - throw createError({ code: 'EEXIST', message: `Cannot create directory ${path}` }) - } - const provider = this.fileProviderOf(path) - return await provider.createDir(path) - } catch (e) { - throw new Error(e) - } - } - - /** + async mkdir(path) { + try { + path = this.normalize(path) + path = this.limitPluginScope(path) + if (await this.exists(path)) { + throw createError({ code: 'EEXIST', message: `Cannot create directory ${path}` }) + } + const provider = this.fileProviderOf(path) + return await provider.createDir(path) + } catch (e) { + throw new Error(e) + } + } + + /** * Get the list of files in the directory * @param {string} path path of the directory * @returns {Object} list of the file/directory name in this directory e.g; {contracts/1_Storage.sol:{isDirectory: false}} */ - async readdir(path): Promise>> { - try { - path = this.normalize(path) - path = this.limitPluginScope(path) - await this._handleExists(path) - await this._handleIsDir(path) - - return new Promise((resolve, reject) => { - const provider = this.fileProviderOf(path) - - provider.resolveDirectory(path, (error, filesProvider) => { - if (error) reject(error) - resolve(filesProvider) - }) - }) - } catch (e) { - throw new Error(e) - } + async readdir(path): Promise>> { + try { + path = this.normalize(path) + path = this.limitPluginScope(path) + await this._handleExists(path) + await this._handleIsDir(path) + + return new Promise((resolve, reject) => { + const provider = this.fileProviderOf(path) + + provider.resolveDirectory(path, (error, filesProvider) => { + if (error) reject(error) + resolve(filesProvider) + }) + }) + } catch (e) { + throw new Error(e) } + } - /** + /** * Removes a file or directory recursively * @param {string} path path of the directory/file to remove * @returns {void} */ - async remove(path) { - try { - path = this.normalize(path) - path = this.limitPluginScope(path) - await this._handleExists(path, `Cannot remove file or directory ${path}`) - const provider = this.fileProviderOf(path) - return await provider.remove(path) - } catch (e) { - throw new Error(e) - } - } - - init() { - this._deps = { - config: this._components.registry.get('config').api, - browserExplorer: this._components.registry.get('fileproviders/browser').api, - localhostExplorer: this._components.registry.get('fileproviders/localhost').api, - workspaceExplorer: this._components.registry.get('fileproviders/workspace').api, - filesProviders: this._components.registry.get('fileproviders').api - } - - this._deps.config.set('currentFile', '') // make sure we remove the current file from the previous session - - this._deps.browserExplorer.event.on('fileChanged', (path) => { this.fileChangedEvent(path) }) - this._deps.browserExplorer.event.on('fileRenamed', (oldName, newName, isFolder) => { this.fileRenamedEvent(oldName, newName, isFolder) }) - this._deps.localhostExplorer.event.on('fileRenamed', (oldName, newName, isFolder) => { this.fileRenamedEvent(oldName, newName, isFolder) }) - this._deps.browserExplorer.event.on('fileRemoved', (path) => { this.fileRemovedEvent(path) }) - this._deps.browserExplorer.event.on('fileAdded', (path) => { this.fileAddedEvent(path) }) - this._deps.localhostExplorer.event.on('fileRemoved', (path) => { this.fileRemovedEvent(path) }) - this._deps.localhostExplorer.event.on('errored', (event) => { this.removeTabsOf(this._deps.localhostExplorer) }) - this._deps.localhostExplorer.event.on('closed', (event) => { this.removeTabsOf(this._deps.localhostExplorer) }) - this._deps.workspaceExplorer.event.on('fileChanged', (path) => { this.fileChangedEvent(path) }) - this._deps.workspaceExplorer.event.on('fileRenamed', (oldName, newName, isFolder) => { this.fileRenamedEvent(oldName, newName, isFolder) }) - this._deps.workspaceExplorer.event.on('fileRemoved', (path) => { this.fileRemovedEvent(path) }) - this._deps.workspaceExplorer.event.on('fileAdded', (path) => { this.fileAddedEvent(path) }) - - this.getCurrentFile = this.file - this.getFile = this.readFile - this.getFolder = this.readdir - this.setFile = this.writeFile - this.switchFile = this.open - } - - fileAddedEvent(path) { - this.emit('fileAdded', path) - } - - fileChangedEvent(path) { - this.emit('fileChanged', path) - } - - fileRenamedEvent(oldName, newName, isFolder) { - if (!isFolder) { - this._deps.config.set('currentFile', '') - this.editor.discard(oldName) - if (this.openedFiles[oldName]) { - delete this.openedFiles[oldName] - this.openedFiles[newName] = newName - } - this.openFile(newName) - } else { - for (const k in this.openedFiles) { - if (k.indexOf(oldName + '/') === 0) { - const newAbsolutePath = k.replace(oldName, newName) - this.openedFiles[newAbsolutePath] = newAbsolutePath - delete this.openedFiles[k] - if (this._deps.config.get('currentFile') === k) { - this._deps.config.set('currentFile', '') - } - } - } - } - // TODO: Only keep `this.emit` (issue#2210) - this.emit('fileRenamed', oldName, newName, isFolder) - this.events.emit('fileRenamed', oldName, newName, isFolder) - } - - currentFileProvider() { - const path = this.currentPath() - if (path) { - return this.fileProviderOf(path) - } - return null - } - - currentFile() { - return this.editor.current() - } - - async closeAllFiles() { - // TODO: Only keep `this.emit` (issue#2210) - this.emit('filesAllClosed') - this.events.emit('filesAllClosed') - for (const file in this.openedFiles) { - await this.closeFile(file) - } - } - - async closeFile(name) { - delete this.openedFiles[name] - if (!Object.keys(this.openedFiles).length) { + async remove(path) { + try { + path = this.normalize(path) + path = this.limitPluginScope(path) + await this._handleExists(path, `Cannot remove file or directory ${path}`) + const provider = this.fileProviderOf(path) + return await provider.remove(path) + } catch (e) { + throw new Error(e) + } + } + + init() { + this._deps = { + config: this._components.registry.get('config').api, + browserExplorer: this._components.registry.get('fileproviders/browser').api, + localhostExplorer: this._components.registry.get('fileproviders/localhost').api, + workspaceExplorer: this._components.registry.get('fileproviders/workspace').api, + filesProviders: this._components.registry.get('fileproviders').api + } + + this._deps.config.set('currentFile', '') // make sure we remove the current file from the previous session + + this._deps.browserExplorer.event.on('fileChanged', (path) => { this.fileChangedEvent(path) }) + this._deps.browserExplorer.event.on('fileRenamed', (oldName, newName, isFolder) => { this.fileRenamedEvent(oldName, newName, isFolder) }) + this._deps.localhostExplorer.event.on('fileRenamed', (oldName, newName, isFolder) => { this.fileRenamedEvent(oldName, newName, isFolder) }) + this._deps.browserExplorer.event.on('fileRemoved', (path) => { this.fileRemovedEvent(path) }) + this._deps.browserExplorer.event.on('fileAdded', (path) => { this.fileAddedEvent(path) }) + this._deps.localhostExplorer.event.on('fileRemoved', (path) => { this.fileRemovedEvent(path) }) + this._deps.localhostExplorer.event.on('errored', (event) => { this.removeTabsOf(this._deps.localhostExplorer) }) + this._deps.localhostExplorer.event.on('closed', (event) => { this.removeTabsOf(this._deps.localhostExplorer) }) + this._deps.workspaceExplorer.event.on('fileChanged', (path) => { this.fileChangedEvent(path) }) + this._deps.workspaceExplorer.event.on('fileRenamed', (oldName, newName, isFolder) => { this.fileRenamedEvent(oldName, newName, isFolder) }) + this._deps.workspaceExplorer.event.on('fileRemoved', (path) => { this.fileRemovedEvent(path) }) + this._deps.workspaceExplorer.event.on('fileAdded', (path) => { this.fileAddedEvent(path) }) + + this.getCurrentFile = this.file + this.getFile = this.readFile + this.getFolder = this.readdir + this.setFile = this.writeFile + this.switchFile = this.open + } + + fileAddedEvent(path) { + this.emit('fileAdded', path) + } + + fileChangedEvent(path) { + this.emit('fileChanged', path) + } + + fileRenamedEvent(oldName, newName, isFolder) { + if (!isFolder) { + this._deps.config.set('currentFile', '') + this.editor.discard(oldName) + if (this.openedFiles[oldName]) { + delete this.openedFiles[oldName] + this.openedFiles[newName] = newName + } + this.openFile(newName) + } else { + for (const k in this.openedFiles) { + if (k.indexOf(oldName + '/') === 0) { + const newAbsolutePath = k.replace(oldName, newName) + this.openedFiles[newAbsolutePath] = newAbsolutePath + delete this.openedFiles[k] + if (this._deps.config.get('currentFile') === k) { this._deps.config.set('currentFile', '') - // TODO: Only keep `this.emit` (issue#2210) - this.emit('noFileSelected') - this.events.emit('noFileSelected') - } - // TODO: Only keep `this.emit` (issue#2210) - this.emit('fileClosed', name) - this.events.emit('fileClosed', name) - } - - currentPath() { - const currentFile = this._deps.config.get('currentFile') - return this.extractPathOf(currentFile) - } - - extractPathOf(file) { - const reg = /(.*)(\/).*/ - const path = reg.exec(file) - return path ? path[1] : '/' - } - - getFileContent(path) { - const provider = this.fileProviderOf(path) - - if (!provider) throw createError({ code: 'ENOENT', message: `${path} not available` }) - // TODO: change provider to Promise - return new Promise((resolve, reject) => { - if (this.currentFile() === path) return resolve(this.editor.currentContent()) - provider.get(path, (err, content) => { - if (err) reject(err) - resolve(content) - }) - }) - } - - async setFileContent(path, content) { - if (this.currentRequest) { - const canCall = await this.askUserPermission(`writeFile`, `modifying ${path} ...`) - const required = this.appManager.isRequired(this.currentRequest.from) - if (canCall && !required) { - // inform the user about modification after permission is granted and even if permission was saved before - this.call('notification', 'toast', fileChangedToastMsg(this.currentRequest.from, path)) - } - } - return await this._setFileInternal(path, content) - } - - _setFileInternal(path, content) { - const provider = this.fileProviderOf(path) - if (!provider) throw createError({ code: 'ENOENT', message: `${path} not available` }) - // TODO : Add permission - // TODO : Change Provider to Promise - return new Promise((resolve, reject) => { - provider.set(path, content, (error) => { - if (error) reject(error) - this.syncEditor(path) - this.emit('fileSaved', path) - resolve(true) - }) - }) - } - - /** + } + } + } + } + // TODO: Only keep `this.emit` (issue#2210) + this.emit('fileRenamed', oldName, newName, isFolder) + this.events.emit('fileRenamed', oldName, newName, isFolder) + } + + currentFileProvider() { + const path = this.currentPath() + if (path) { + return this.fileProviderOf(path) + } + return null + } + + currentFile() { + return this.editor.current() + } + + async closeAllFiles() { + // TODO: Only keep `this.emit` (issue#2210) + this.emit('filesAllClosed') + this.events.emit('filesAllClosed') + for (const file in this.openedFiles) { + await this.closeFile(file) + } + } + + async closeFile(name) { + delete this.openedFiles[name] + if (!Object.keys(this.openedFiles).length) { + this._deps.config.set('currentFile', '') + // TODO: Only keep `this.emit` (issue#2210) + this.emit('noFileSelected') + this.events.emit('noFileSelected') + } + // TODO: Only keep `this.emit` (issue#2210) + this.emit('fileClosed', name) + this.events.emit('fileClosed', name) + } + + currentPath() { + const currentFile = this._deps.config.get('currentFile') + return this.extractPathOf(currentFile) + } + + extractPathOf(file) { + const reg = /(.*)(\/).*/ + const path = reg.exec(file) + return path ? path[1] : '/' + } + + getFileContent(path) { + const provider = this.fileProviderOf(path) + + if (!provider) throw createError({ code: 'ENOENT', message: `${path} not available` }) + // TODO: change provider to Promise + return new Promise((resolve, reject) => { + if (this.currentFile() === path) return resolve(this.editor.currentContent()) + provider.get(path, (err, content) => { + if (err) reject(err) + resolve(content) + }) + }) + } + + async setFileContent(path, content) { + if (this.currentRequest) { + const canCall = await this.askUserPermission(`writeFile`, `modifying ${path} ...`) + const required = this.appManager.isRequired(this.currentRequest.from) + if (canCall && !required) { + // inform the user about modification after permission is granted and even if permission was saved before + this.call('notification', 'toast', fileChangedToastMsg(this.currentRequest.from, path)) + } + } + return await this._setFileInternal(path, content) + } + + _setFileInternal(path, content) { + const provider = this.fileProviderOf(path) + if (!provider) throw createError({ code: 'ENOENT', message: `${path} not available` }) + // TODO : Add permission + // TODO : Change Provider to Promise + return new Promise((resolve, reject) => { + provider.set(path, content, (error) => { + if (error) reject(error) + this.syncEditor(path) + this.emit('fileSaved', path) + resolve(true) + }) + }) + } + + /** * Try to resolve the given file path (the actual path in the file system) * e.g if it's specified a github link, npm library, or any external content, * it returns the actual path where the content can be found. * @param {string} file url we are trying to resolve * @returns {{ string, provider }} file path resolved and its provider. */ - getPathFromUrl(file) { - const provider = this.fileProviderOf(file) - if (!provider) throw new Error(`no provider for ${file}`) - return { - file: provider.getPathFromUrl(file) || file, // in case an external URL is given as input, we resolve it to the right internal path - provider - } + getPathFromUrl(file) { + const provider = this.fileProviderOf(file) + if (!provider) throw new Error(`no provider for ${file}`) + return { + file: provider.getPathFromUrl(file) || file, // in case an external URL is given as input, we resolve it to the right internal path + provider } + } - /** + /** * Try to resolve the given file URl. opposite of getPathFromUrl * @param {string} file path we are trying to resolve * @returns {{ string, provider }} file url resolved and its provider. */ - getUrlFromPath(file) { - const provider = this.fileProviderOf(file) - if (!provider) throw new Error(`no provider for ${file}`) - return { - file: provider.getUrlFromPath(file) || file, // in case an external URL is given as input, we resolve it to the right internal path - provider - } - } - - removeTabsOf(provider) { - for (const tab in this.openedFiles) { - if (this.fileProviderOf(tab).type === provider.type) { - this.fileRemovedEvent(tab) - } - } - } - - fileRemovedEvent(path) { - if (path === this._deps.config.get('currentFile')) { - this._deps.config.set('currentFile', '') - } - this.editor.discard(path) - delete this.openedFiles[path] - // TODO: Only keep `this.emit` (issue#2210) - this.emit('fileRemoved', path) - this.events.emit('fileRemoved', path) - this.openFile(this._deps.config.get('currentFile')) - } - - async unselectCurrentFile() { - await this.saveCurrentFile() - this._deps.config.set('currentFile', '') - // TODO: Only keep `this.emit` (issue#2210) - this.emit('noFileSelected') - this.events.emit('noFileSelected') - } - - async openFile(file?: string) { - if (!file) { - this.emit('noFileSelected') - this.events.emit('noFileSelected') - } else { - file = this.normalize(file) - const resolved = this.getPathFromUrl(file) - file = resolved.file - await this.saveCurrentFile() - if (this.currentFile() === file) return + getUrlFromPath(file) { + const provider = this.fileProviderOf(file) + if (!provider) throw new Error(`no provider for ${file}`) + return { + file: provider.getUrlFromPath(file) || file, // in case an external URL is given as input, we resolve it to the right internal path + provider + } + } + + removeTabsOf(provider) { + for (const tab in this.openedFiles) { + if (this.fileProviderOf(tab).type === provider.type) { + this.fileRemovedEvent(tab) + } + } + } + + fileRemovedEvent(path) { + if (path === this._deps.config.get('currentFile')) { + this._deps.config.set('currentFile', '') + } + this.editor.discard(path) + delete this.openedFiles[path] + // TODO: Only keep `this.emit` (issue#2210) + this.emit('fileRemoved', path) + this.events.emit('fileRemoved', path) + this.openFile(this._deps.config.get('currentFile')) + } + + async unselectCurrentFile() { + await this.saveCurrentFile() + this._deps.config.set('currentFile', '') + // TODO: Only keep `this.emit` (issue#2210) + this.emit('noFileSelected') + this.events.emit('noFileSelected') + } + + async openFile(file?: string) { + if (!file) { + this.emit('noFileSelected') + this.events.emit('noFileSelected') + } else { + file = this.normalize(file) + const resolved = this.getPathFromUrl(file) + file = resolved.file + await this.saveCurrentFile() + if (this.currentFile() === file) return - const provider = resolved.provider - this._deps.config.set('currentFile', file) - this.openedFiles[file] = file + const provider = resolved.provider + this._deps.config.set('currentFile', file) + this.openedFiles[file] = file - let content = '' - try { - content = await provider.get(file) + let content = '' + try { + content = await provider.get(file) - } catch (error) { - console.log(error) - throw error - } - try { - // This make sure dependencies are loaded in the editor context. - // This ensure monaco is aware of deps artifacts, so it can provide basic features like "go to" symbols. - await this.editor.handleTypeScriptDependenciesOf(file, content, path => this.readFile(path), path => this.exists(path)) - } catch (e) { - console.log('unable to handle TypeScript dependencies of', file) - } - if (provider.isReadOnly(file)) { - await this.editor.openReadOnly(file, content) - } else { - await this.editor.open(file, content) - } - // TODO: Only keep `this.emit` (issue#2210) - this.emit('currentFileChanged', file) - this.events.emit('currentFileChanged', file) - return true - } - } - - /** + } catch (error) { + console.log(error) + throw error + } + try { + // This make sure dependencies are loaded in the editor context. + // This ensure monaco is aware of deps artifacts, so it can provide basic features like "go to" symbols. + await this.editor.handleTypeScriptDependenciesOf(file, content, path => this.readFile(path), path => this.exists(path)) + } catch (e) { + console.log('unable to handle TypeScript dependencies of', file) + } + if (provider.isReadOnly(file)) { + await this.editor.openReadOnly(file, content) + } else { + await this.editor.open(file, content) + } + // TODO: Only keep `this.emit` (issue#2210) + this.emit('currentFileChanged', file) + this.events.emit('currentFileChanged', file) + return true + } + } + + /** * Async API method getProviderOf * @param {string} file * */ - async getProviderOf(file) { - const cancall = await this.askUserPermission('getProviderByName') - if (cancall) { - return file ? this.fileProviderOf(file) : this.currentFileProvider() - } + async getProviderOf(file) { + const cancall = await this.askUserPermission('getProviderByName') + if (cancall) { + return file ? this.fileProviderOf(file) : this.currentFileProvider() } + } - /** + /** * Async API method getProviderByName * @param {string} name * */ - async getProviderByName(name) { - const cancall = await this.askUserPermission('getProviderByName') - if (cancall) { - return this.getProvider(name) - } - } - - getProvider(name) { - return this._deps.filesProviders[name] - } - - fileProviderOf(file) { - if (file.startsWith('localhost') || this.mode === 'localhost') { - return this._deps.filesProviders.localhost - } - if (file.startsWith('browser')) { - return this._deps.filesProviders.browser - } - return this._deps.filesProviders.workspace - } - - // returns the list of directories inside path - dirList(path) { - const dirPaths = [] - const collectList = (path) => { - return new Promise((resolve, reject) => { - this.readdir(path).then((ls) => { - const promises = Object.keys(ls).map((item, index) => { - if (ls[item].isDirectory && !dirPaths.includes(item)) { - dirPaths.push(item) - resolve(dirPaths) - } - return new Promise((resolve, reject) => { resolve(true) }) - }) - Promise.all(promises).then(() => { resolve(dirPaths) }) - }) - }) - } - return collectList(path) - } - - async fileList (dirPath) { - const paths: any = await this.readdir(dirPath) - for( const path in paths) - if(paths[path].isDirectory) delete paths[path] - return Object.keys(paths) - } - - isRemixDActive () { - return this.appManager.isActive('remixd') - } - - async saveCurrentFile() { - const currentFile = this._deps.config.get('currentFile') - if (currentFile && this.editor.current()) { - const input = this.editor.get(currentFile) - if ((input !== null) && (input !== undefined)) { - const provider = this.fileProviderOf(currentFile) - if (provider) { - // use old content as default if save operation fails. - provider.get(currentFile, (error, oldContent) => { - provider.set(currentFile, input, (error) => { - if (error) { - if (error.message ) this.call('notification', 'toast', - error.message.indexOf( - 'LocalStorage is full') !== -1 ? storageFullMessage() - : error.message - ) - provider.set(currentFile, oldContent) - return console.error(error) - } else { - this.emit('fileSaved', currentFile) - } - }) - }) - } else { - console.log('cannot save ' + currentFile + '. Does not belong to any explorer') - } + async getProviderByName(name) { + const cancall = await this.askUserPermission('getProviderByName') + if (cancall) { + return this.getProvider(name) + } + } + + getProvider(name) { + return this._deps.filesProviders[name] + } + + fileProviderOf(file) { + if (file.startsWith('localhost') || this.mode === 'localhost') { + return this._deps.filesProviders.localhost + } + if (file.startsWith('browser')) { + return this._deps.filesProviders.browser + } + return this._deps.filesProviders.workspace + } + + // returns the list of directories inside path + dirList(path) { + const dirPaths = [] + const collectList = (path) => { + return new Promise((resolve, reject) => { + this.readdir(path).then((ls) => { + const promises = Object.keys(ls).map((item, index) => { + if (ls[item].isDirectory && !dirPaths.includes(item)) { + dirPaths.push(item) + resolve(dirPaths) } - } - } - - async syncEditor(path) { - const currentFile = this._deps.config.get('currentFile') - if (path !== currentFile) return + return new Promise((resolve, reject) => { resolve(true) }) + }) + Promise.all(promises).then(() => { resolve(dirPaths) }) + }) + }) + } + return collectList(path) + } + + async fileList (dirPath) { + const paths: any = await this.readdir(dirPath) + for( const path in paths) + if(paths[path].isDirectory) delete paths[path] + return Object.keys(paths) + } + + isRemixDActive () { + return this.appManager.isActive('remixd') + } + + async saveCurrentFile() { + const currentFile = this._deps.config.get('currentFile') + if (currentFile && this.editor.current()) { + const input = this.editor.get(currentFile) + if ((input !== null) && (input !== undefined)) { const provider = this.fileProviderOf(currentFile) if (provider) { - try{ - const content = await provider.get(currentFile) - if(content) this.editor.setText(currentFile, content) - }catch(error){ - console.log(error) - } + // use old content as default if save operation fails. + provider.get(currentFile, (error, oldContent) => { + provider.set(currentFile, input, (error) => { + if (error) { + if (error.message ) this.call('notification', 'toast', + error.message.indexOf( + 'LocalStorage is full') !== -1 ? storageFullMessage() + : error.message + ) + provider.set(currentFile, oldContent) + return console.error(error) + } else { + this.emit('fileSaved', currentFile) + } + }) + }) } else { - console.log('cannot save ' + currentFile + '. Does not belong to any explorer') - } - } - - async setBatchFiles(filesSet, fileProvider, override, callback) { - const self = this - if (!fileProvider) fileProvider = 'workspace' - if (override === undefined) override = false - for (const file of Object.keys(filesSet)) { - if (override) { - try { - await self._deps.filesProviders[fileProvider].set(file, filesSet[file].content) - } catch (e) { - callback(e.message || e) - } - await self.syncEditor(fileProvider + file) - } else { - try{ - const name = await helper.createNonClashingNameAsync(file, self._deps.filesProviders[fileProvider]) - if (helper.checkSpecialChars(name)) { - this.call('notification', 'alert', { - id: 'fileManagerAlert', - message: 'Special characters are not allowed in file names.' - }) - } else { - try { - await self._deps.filesProviders[fileProvider].set(name, filesSet[file].content) - } catch (e) { - return callback(e.message || e) - } - self.syncEditor(fileProvider + name) - } - }catch(error){ - if (error) { - this.call('notification', 'alert', { - id: 'fileManagerAlert', - message: 'Unexpected error loading file ' + file + ': ' + error - }) - } - } + console.log('cannot save ' + currentFile + '. Does not belong to any explorer') + } + } + } + } + + async syncEditor(path) { + const currentFile = this._deps.config.get('currentFile') + if (path !== currentFile) return + const provider = this.fileProviderOf(currentFile) + if (provider) { + try{ + const content = await provider.get(currentFile) + if(content) this.editor.setText(currentFile, content) + }catch(error){ + console.log(error) + } + } else { + console.log('cannot save ' + currentFile + '. Does not belong to any explorer') + } + } + + async setBatchFiles(filesSet, fileProvider, override, callback) { + const self = this + if (!fileProvider) fileProvider = 'workspace' + if (override === undefined) override = false + for (const file of Object.keys(filesSet)) { + if (override) { + try { + await self._deps.filesProviders[fileProvider].set(file, filesSet[file].content) + } catch (e) { + callback(e.message || e) + } + await self.syncEditor(fileProvider + file) + } else { + try{ + const name = await helper.createNonClashingNameAsync(file, self._deps.filesProviders[fileProvider]) + if (helper.checkSpecialChars(name)) { + this.call('notification', 'alert', { + id: 'fileManagerAlert', + message: 'Special characters are not allowed in file names.' + }) + } else { + try { + await self._deps.filesProviders[fileProvider].set(name, filesSet[file].content) + } catch (e) { + return callback(e.message || e) } + self.syncEditor(fileProvider + name) + } + }catch(error){ + if (error) { + this.call('notification', 'alert', { + id: 'fileManagerAlert', + message: 'Unexpected error loading file ' + file + ': ' + error + }) + } } - callback() + } } + callback() + } - currentWorkspace() { - if (this.mode !== 'localhost') { - const file = this.currentFile() || '' - const provider = this.fileProviderOf(file) + currentWorkspace() { + if (this.mode !== 'localhost') { + const file = this.currentFile() || '' + const provider = this.fileProviderOf(file) - return provider.workspace - } + return provider.workspace } + } - async isGitRepo (): Promise { - const path = '.git' - const exists = await this.exists(path) + async isGitRepo (): Promise { + const path = '.git' + const exists = await this.exists(path) - return exists - } + return exists + } - /** + /** * Moves a file to a new folder * @param {string} src path of the source file * @param {string} dest path of the destrination file * @returns {void} */ - async moveFile(src: string, dest: string) { - try { - src = this.normalize(src) - dest = this.normalize(dest) - src = this.limitPluginScope(src) - dest = this.limitPluginScope(dest) - await this._handleExists(src, `Cannot move ${src}. Path does not exist.`) - await this._handleExists(dest, `Cannot move content into ${dest}. Path does not exist.`) - await this._handleIsFile(src, `Cannot move ${src}. Path is not a file.`) - await this._handleIsDir(dest, `Cannot move content into ${dest}. Path is not directory.`) - const fileName = helper.extractNameFromKey(src) + async moveFile(src: string, dest: string) { + try { + src = this.normalize(src) + dest = this.normalize(dest) + src = this.limitPluginScope(src) + dest = this.limitPluginScope(dest) + await this._handleExists(src, `Cannot move ${src}. Path does not exist.`) + await this._handleExists(dest, `Cannot move content into ${dest}. Path does not exist.`) + await this._handleIsFile(src, `Cannot move ${src}. Path is not a file.`) + await this._handleIsDir(dest, `Cannot move content into ${dest}. Path is not directory.`) + const fileName = helper.extractNameFromKey(src) - if (await this.exists(dest + '/' + fileName)) { - throw createError({ code: 'EEXIST', message: `Cannot move ${src}. File already exists at destination ${dest}`}) - } - await this.copyFile(src, dest, fileName) - await this.remove(src) - } catch (e) { - throw new Error(e) - } + if (await this.exists(dest + '/' + fileName)) { + throw createError({ code: 'EEXIST', message: `Cannot move ${src}. File already exists at destination ${dest}`}) + } + await this.copyFile(src, dest, fileName) + await this.remove(src) + } catch (e) { + throw new Error(e) } + } - /** + /** * Moves a folder to a new folder * @param {string} src path of the source folder * @param {string} dest path of the destination folder * @returns {void} */ - async moveDir(src: string, dest: string) { - try { - src = this.normalize(src) - dest = this.normalize(dest) - src = this.limitPluginScope(src) - dest = this.limitPluginScope(dest) - await this._handleExists(src, `Cannot move ${src}. Path does not exist.`) - await this._handleExists(dest, `Cannot move content into ${dest}. Path does not exist.`) - await this._handleIsDir(src, `Cannot move ${src}. Path is not directory.`) - await this._handleIsDir(dest, `Cannot move content into ${dest}. Path is not directory.`) - const dirName = helper.extractNameFromKey(src) - if (await this.exists(dest + '/' + dirName) || src === dest) { - throw createError({ code: 'EEXIST', message: `Cannot move ${src}. Folder already exists at destination ${dest}`}) - } - await this.copyDir(src, dest, dirName) - await this.remove(src) - - } catch (e) { - throw new Error(e) - } - } + async moveDir(src: string, dest: string) { + try { + src = this.normalize(src) + dest = this.normalize(dest) + src = this.limitPluginScope(src) + dest = this.limitPluginScope(dest) + await this._handleExists(src, `Cannot move ${src}. Path does not exist.`) + await this._handleExists(dest, `Cannot move content into ${dest}. Path does not exist.`) + await this._handleIsDir(src, `Cannot move ${src}. Path is not directory.`) + await this._handleIsDir(dest, `Cannot move content into ${dest}. Path is not directory.`) + const dirName = helper.extractNameFromKey(src) + if (await this.exists(dest + '/' + dirName) || src === dest) { + throw createError({ code: 'EEXIST', message: `Cannot move ${src}. Folder already exists at destination ${dest}`}) + } + await this.copyDir(src, dest, dirName) + await this.remove(src) + + } catch (e) { + throw new Error(e) + } + } } module.exports = FileManager diff --git a/apps/remix-ide/src/app/files/fileProvider.js b/apps/remix-ide/src/app/files/fileProvider.js index 9b7e63efdf..3c89cb921c 100644 --- a/apps/remix-ide/src/app/files/fileProvider.js +++ b/apps/remix-ide/src/app/files/fileProvider.js @@ -7,318 +7,318 @@ const pathModule = require('path') const Storage = remixLib.Storage class FileProvider { - constructor (name) { - this.event = new EventManager() - this.type = name - this.providerExternalsStorage = new Storage('providerExternals:') - this.externalFolders = [this.type + '/swarm', this.type + '/ipfs', this.type + '/github', this.type + '/gists', this.type + '/https'] - this.reverseKey = this.type + '-reverse-' - } - - addNormalizedName (path, url) { - if (this.type) path = this.type + '/' + path - this.providerExternalsStorage.set(path, url) - this.providerExternalsStorage.set(this.reverseKey + url, path) - } - - removeNormalizedName (path) { - const value = this.providerExternalsStorage.get(path) - this.providerExternalsStorage.remove(path) - this.providerExternalsStorage.remove(this.reverseKey + value) - } - - normalizedNameExists (path) { - return this.providerExternalsStorage.exists(path) - } - - getNormalizedName (path) { - return this.providerExternalsStorage.get(path) - } - - getPathFromUrl (url) { - return this.providerExternalsStorage.get(this.reverseKey + url) - } - - getUrlFromPath (path) { - if (!path.startsWith(this.type)) path = this.type + '/' + path - return this.providerExternalsStorage.get(path) - } - - isExternalFolder (path) { - return this.externalFolders.includes(path) - } - - async discardChanges (path, toastCb, modalCb) { - this.remove(path) - const compilerImport = new CompilerImports() - this.providerExternalsStorage.keys().map(value => { - if (value.indexOf(path) === 0) { - compilerImport.import( - this.getNormalizedName(value), - true, - (loadingMsg) => { toastCb(loadingMsg) }, - async (error, content, cleanUrl, type, url) => { - if (error) { - modalCb(error) - } else { - await this.addExternal(type + '/' + cleanUrl, content, url) - } - } - ) + constructor (name) { + this.event = new EventManager() + this.type = name + this.providerExternalsStorage = new Storage('providerExternals:') + this.externalFolders = [this.type + '/swarm', this.type + '/ipfs', this.type + '/github', this.type + '/gists', this.type + '/https'] + this.reverseKey = this.type + '-reverse-' + } + + addNormalizedName (path, url) { + if (this.type) path = this.type + '/' + path + this.providerExternalsStorage.set(path, url) + this.providerExternalsStorage.set(this.reverseKey + url, path) + } + + removeNormalizedName (path) { + const value = this.providerExternalsStorage.get(path) + this.providerExternalsStorage.remove(path) + this.providerExternalsStorage.remove(this.reverseKey + value) + } + + normalizedNameExists (path) { + return this.providerExternalsStorage.exists(path) + } + + getNormalizedName (path) { + return this.providerExternalsStorage.get(path) + } + + getPathFromUrl (url) { + return this.providerExternalsStorage.get(this.reverseKey + url) + } + + getUrlFromPath (path) { + if (!path.startsWith(this.type)) path = this.type + '/' + path + return this.providerExternalsStorage.get(path) + } + + isExternalFolder (path) { + return this.externalFolders.includes(path) + } + + async discardChanges (path, toastCb, modalCb) { + this.remove(path) + const compilerImport = new CompilerImports() + this.providerExternalsStorage.keys().map(value => { + if (value.indexOf(path) === 0) { + compilerImport.import( + this.getNormalizedName(value), + true, + (loadingMsg) => { toastCb(loadingMsg) }, + async (error, content, cleanUrl, type, url) => { + if (error) { + modalCb(error) + } else { + await this.addExternal(type + '/' + cleanUrl, content, url) } - }) + } + ) + } + }) + } + + async exists (path) { + // todo check the type (directory/file) as well #2386 + // currently it is not possible to have a file and folder with same path + const ret = await this._exists(path) + + return ret + } + + async _exists (path) { + path = this.getPathFromUrl(path) || path // ensure we actually use the normalized path from here + var unprefixedpath = this.removePrefix(path) + return path === this.type ? true : await window.remixFileSystem.exists(unprefixedpath) + } + + init (cb) { + cb() + } + + async get (path, cb) { + cb = cb || function () { /* do nothing. */ } + path = this.getPathFromUrl(path) || path // ensure we actually use the normalized path from here + var unprefixedpath = this.removePrefix(path) + try { + const content = await window.remixFileSystem.readFile(unprefixedpath, 'utf8') + if (cb) cb(null, content) + return content + } catch (err) { + if (cb) cb(err, null) + throw new Error(err) } - - async exists (path) { - // todo check the type (directory/file) as well #2386 - // currently it is not possible to have a file and folder with same path - const ret = await this._exists(path) - - return ret - } - - async _exists (path) { - path = this.getPathFromUrl(path) || path // ensure we actually use the normalized path from here - var unprefixedpath = this.removePrefix(path) - return path === this.type ? true : await window.remixFileSystem.exists(unprefixedpath) + } + + async set (path, content, cb) { + cb = cb || function () { /* do nothing. */ } + var unprefixedpath = this.removePrefix(path) + const exists = await window.remixFileSystem.exists(unprefixedpath) + if (exists && await window.remixFileSystem.readFile(unprefixedpath, 'utf8') === content) { + if (cb) cb() + return null } - - init (cb) { - cb() + await this.createDir(path.substr(0, path.lastIndexOf('/'))) + try { + await window.remixFileSystem.writeFile(unprefixedpath, content, 'utf8') + } catch (e) { + if (cb) cb(e) + return false } - - async get (path, cb) { - cb = cb || function () { /* do nothing. */ } - path = this.getPathFromUrl(path) || path // ensure we actually use the normalized path from here - var unprefixedpath = this.removePrefix(path) - try { - const content = await window.remixFileSystem.readFile(unprefixedpath, 'utf8') - if (cb) cb(null, content) - return content - } catch (err) { - if (cb) cb(err, null) - throw new Error(err) - } + if (!exists) { + this.event.emit('fileAdded', this._normalizePath(unprefixedpath), false) + } else { + this.event.emit('fileChanged', this._normalizePath(unprefixedpath)) } - - async set (path, content, cb) { - cb = cb || function () { /* do nothing. */ } - var unprefixedpath = this.removePrefix(path) - const exists = await window.remixFileSystem.exists(unprefixedpath) - if (exists && await window.remixFileSystem.readFile(unprefixedpath, 'utf8') === content) { - if (cb) cb() - return null - } - await this.createDir(path.substr(0, path.lastIndexOf('/'))) + if (cb) cb() + return true + } + + async createDir (path, cb) { + const unprefixedpath = this.removePrefix(path) + + await this.forceCreateDir(unprefixedpath) + if (cb) cb() + } + + async forceCreateDir (path) { + const paths = path.split('/') + if (paths.length && paths[0] === '') paths.shift() + let currentCheck = '' + for (const value of paths) { + currentCheck = currentCheck + '/' + value + if (!await window.remixFileSystem.exists(currentCheck)) { try { - await window.remixFileSystem.writeFile(unprefixedpath, content, 'utf8') - } catch (e) { - if (cb) cb(e) - return false - } - if (!exists) { - this.event.emit('fileAdded', this._normalizePath(unprefixedpath), false) - } else { - this.event.emit('fileChanged', this._normalizePath(unprefixedpath)) - } - if (cb) cb() - return true - } - - async createDir (path, cb) { - const unprefixedpath = this.removePrefix(path) - - await this.forceCreateDir(unprefixedpath) - if (cb) cb() - } - - async forceCreateDir (path) { - const paths = path.split('/') - if (paths.length && paths[0] === '') paths.shift() - let currentCheck = '' - for (const value of paths) { - currentCheck = currentCheck + '/' + value - if (!await window.remixFileSystem.exists(currentCheck)) { - try { - await window.remixFileSystem.mkdir(currentCheck) - this.event.emit('folderAdded', this._normalizePath(currentCheck)) - } catch (error) { - console.log(error) - } - } + await window.remixFileSystem.mkdir(currentCheck) + this.event.emit('folderAdded', this._normalizePath(currentCheck)) + } catch (error) { + console.log(error) } + } } - - // this will not add a folder as readonly but keep the original url to be able to restore it later - async addExternal (path, content, url) { - if (url) this.addNormalizedName(path, url) - return await this.set(path, content) - } - - isReadOnly (path) { - return false - } - - async isDirectory (path) { - const unprefixedpath = this.removePrefix(path) - return path === this.type ? true : (await window.remixFileSystem.stat(unprefixedpath)).isDirectory() - } - - async isFile (path) { - path = this.getPathFromUrl(path) || path // ensure we actually use the normalized path from here - path = this.removePrefix(path) - return (await window.remixFileSystem.stat(path)).isFile() - } - - /** + } + + // this will not add a folder as readonly but keep the original url to be able to restore it later + async addExternal (path, content, url) { + if (url) this.addNormalizedName(path, url) + return await this.set(path, content) + } + + isReadOnly (path) { + return false + } + + async isDirectory (path) { + const unprefixedpath = this.removePrefix(path) + return path === this.type ? true : (await window.remixFileSystem.stat(unprefixedpath)).isDirectory() + } + + async isFile (path) { + path = this.getPathFromUrl(path) || path // ensure we actually use the normalized path from here + path = this.removePrefix(path) + return (await window.remixFileSystem.stat(path)).isFile() + } + + /** * Removes the folder recursively * @param {*} path is the folder to be removed */ - async remove (path) { - path = this.removePrefix(path) - if (await window.remixFileSystem.exists(path)) { - const stat = await window.remixFileSystem.stat(path) - try { - if (!stat.isDirectory()) { - return await this.removeFile(path) - } else { - const items = await window.remixFileSystem.readdir(path) - if (items.length !== 0) { - for (const item of items) { - const curPath = `${path}${path.endsWith('/') ? '' : '/'}${item}` - if ((await window.remixFileSystem.stat(curPath)).isDirectory()) { // delete folder - await this.remove(curPath) - } else { // delete file - await this.removeFile(curPath) - } - } - await window.remixFileSystem.rmdir(path) - this.event.emit('fileRemoved', this._normalizePath(path)) - } else { - // folder is empty - await window.remixFileSystem.rmdir(path) - this.event.emit('fileRemoved', this._normalizePath(path)) - } - } - } catch (e) { - console.log(e) - return false + async remove (path) { + path = this.removePrefix(path) + if (await window.remixFileSystem.exists(path)) { + const stat = await window.remixFileSystem.stat(path) + try { + if (!stat.isDirectory()) { + return await this.removeFile(path) + } else { + const items = await window.remixFileSystem.readdir(path) + if (items.length !== 0) { + for (const item of items) { + const curPath = `${path}${path.endsWith('/') ? '' : '/'}${item}` + if ((await window.remixFileSystem.stat(curPath)).isDirectory()) { // delete folder + await this.remove(curPath) + } else { // delete file + await this.removeFile(curPath) + } } + await window.remixFileSystem.rmdir(path) + this.event.emit('fileRemoved', this._normalizePath(path)) + } else { + // folder is empty + await window.remixFileSystem.rmdir(path) + this.event.emit('fileRemoved', this._normalizePath(path)) + } } + } catch (e) { + console.log(e) + return false + } } + } - /** + /** * copy the folder recursively (internal use) * @param {string} path is the folder to be copied over * @param {Function} visitFile is a function called for each visited files * @param {Function} visitFolder is a function called for each visited folders */ - async _copyFolderToJsonInternal (path, visitFile, visitFolder) { - visitFile = visitFile || function () { /* do nothing. */ } - visitFolder = visitFolder || function () { /* do nothing. */ } - - const json = {} - path = this.removePrefix(path) - if (await window.remixFileSystem.exists(path)) { - try { - const items = await window.remixFileSystem.readdir(path) - visitFolder({ path }) - if (items.length !== 0) { - for (const item of items) { - const file = {} - const curPath = `${path}${path.endsWith('/') ? '' : '/'}${item}` - if ((await window.remixFileSystem.stat(curPath)).isDirectory()) { - file.children = await this._copyFolderToJsonInternal(curPath, visitFile, visitFolder) - } else { - file.content = await window.remixFileSystem.readFile(curPath, 'utf8') - visitFile({ path: curPath, content: file.content }) - } - json[curPath] = file - } - } - } catch (e) { - console.log(e) - throw new Error(e) + async _copyFolderToJsonInternal (path, visitFile, visitFolder) { + visitFile = visitFile || function () { /* do nothing. */ } + visitFolder = visitFolder || function () { /* do nothing. */ } + + const json = {} + path = this.removePrefix(path) + if (await window.remixFileSystem.exists(path)) { + try { + const items = await window.remixFileSystem.readdir(path) + visitFolder({ path }) + if (items.length !== 0) { + for (const item of items) { + const file = {} + const curPath = `${path}${path.endsWith('/') ? '' : '/'}${item}` + if ((await window.remixFileSystem.stat(curPath)).isDirectory()) { + file.children = await this._copyFolderToJsonInternal(curPath, visitFile, visitFolder) + } else { + file.content = await window.remixFileSystem.readFile(curPath, 'utf8') + visitFile({ path: curPath, content: file.content }) } + json[curPath] = file + } } - return json + } catch (e) { + console.log(e) + throw new Error(e) + } } + return json + } - /** + /** * copy the folder recursively * @param {string} path is the folder to be copied over * @param {Function} visitFile is a function called for each visited files * @param {Function} visitFolder is a function called for each visited folders */ - async copyFolderToJson (path, visitFile, visitFolder) { - visitFile = visitFile || function () { /* do nothing. */ } - visitFolder = visitFolder || function () { /* do nothing. */ } - return await this._copyFolderToJsonInternal(path, visitFile, visitFolder) - } - - async removeFile (path) { - path = this.removePrefix(path) - if (await window.remixFileSystem.exists(path) && !(await window.remixFileSystem.stat(path)).isDirectory()) { - await window.remixFileSystem.unlink(path) - this.event.emit('fileRemoved', this._normalizePath(path)) - return true - } else return false - } - - async rename (oldPath, newPath, isFolder) { - var unprefixedoldPath = this.removePrefix(oldPath) - var unprefixednewPath = this.removePrefix(newPath) - if (await this._exists(unprefixedoldPath)) { - await window.remixFileSystem.rename(unprefixedoldPath, unprefixednewPath) - this.event.emit('fileRenamed', - this._normalizePath(unprefixedoldPath), - this._normalizePath(unprefixednewPath), - isFolder - ) - return true - } - return false + async copyFolderToJson (path, visitFile, visitFolder) { + visitFile = visitFile || function () { /* do nothing. */ } + visitFolder = visitFolder || function () { /* do nothing. */ } + return await this._copyFolderToJsonInternal(path, visitFile, visitFolder) + } + + async removeFile (path) { + path = this.removePrefix(path) + if (await window.remixFileSystem.exists(path) && !(await window.remixFileSystem.stat(path)).isDirectory()) { + await window.remixFileSystem.unlink(path) + this.event.emit('fileRemoved', this._normalizePath(path)) + return true + } else return false + } + + async rename (oldPath, newPath, isFolder) { + var unprefixedoldPath = this.removePrefix(oldPath) + var unprefixednewPath = this.removePrefix(newPath) + if (await this._exists(unprefixedoldPath)) { + await window.remixFileSystem.rename(unprefixedoldPath, unprefixednewPath) + this.event.emit('fileRenamed', + this._normalizePath(unprefixedoldPath), + this._normalizePath(unprefixednewPath), + isFolder + ) + return true } - - async resolveDirectory (path, cb) { - path = this.removePrefix(path) - if (path.indexOf('/') !== 0) path = '/' + path - try { - const files = await window.remixFileSystem.readdir(path) - const ret = {} - if (files) { - for (let element of files) { - path = path.replace(/^\/|\/$/g, '') // remove first and last slash - element = element.replace(/^\/|\/$/g, '') // remove first and last slash - const absPath = (path === '/' ? '' : path) + '/' + element - ret[absPath.indexOf('/') === 0 ? absPath.substr(1, absPath.length) : absPath] = { isDirectory: (await window.remixFileSystem.stat(absPath)).isDirectory() } - // ^ ret does not accept path starting with '/' - } - } - if (cb) cb(null, ret) - return ret - } catch (error) { - if (cb) cb(error, null) + return false + } + + async resolveDirectory (path, cb) { + path = this.removePrefix(path) + if (path.indexOf('/') !== 0) path = '/' + path + try { + const files = await window.remixFileSystem.readdir(path) + const ret = {} + if (files) { + for (let element of files) { + path = path.replace(/^\/|\/$/g, '') // remove first and last slash + element = element.replace(/^\/|\/$/g, '') // remove first and last slash + const absPath = (path === '/' ? '' : path) + '/' + element + ret[absPath.indexOf('/') === 0 ? absPath.substr(1, absPath.length) : absPath] = { isDirectory: (await window.remixFileSystem.stat(absPath)).isDirectory() } + // ^ ret does not accept path starting with '/' } + } + if (cb) cb(null, ret) + return ret + } catch (error) { + if (cb) cb(error, null) } + } - removePrefix (path) { - path = path.indexOf(this.type) === 0 ? path.replace(this.type, '') : path - if (path === '') return '/' - return path - } + removePrefix (path) { + path = path.indexOf(this.type) === 0 ? path.replace(this.type, '') : path + if (path === '') return '/' + return path + } - _normalizePath (path) { - return this.type + path - } + _normalizePath (path) { + return this.type + path + } - isSubDirectory (parent, child) { - if (!parent) return false - if (parent === child) return true - const relative = pathModule.relative(parent, child) + isSubDirectory (parent, child) { + if (!parent) return false + if (parent === child) return true + const relative = pathModule.relative(parent, child) - return !!relative && relative.split(pathModule.sep)[0] !== '..' - } + return !!relative && relative.split(pathModule.sep)[0] !== '..' + } } module.exports = FileProvider diff --git a/apps/remix-ide/src/app/files/fileSystem.ts b/apps/remix-ide/src/app/files/fileSystem.ts index 80840f617f..5f2a42c8d9 100644 --- a/apps/remix-ide/src/app/files/fileSystem.ts +++ b/apps/remix-ide/src/app/files/fileSystem.ts @@ -1,71 +1,71 @@ export class fileSystem { - name: string - enabled: boolean - available: boolean - fs: any - fsCallBack: any; - hasWorkSpaces: boolean - loaded: boolean - load: () => Promise - test: () => Promise + name: string + enabled: boolean + available: boolean + fs: any + fsCallBack: any; + hasWorkSpaces: boolean + loaded: boolean + load: () => Promise + test: () => Promise - constructor() { - this.available = false - this.enabled = false - this.hasWorkSpaces = false - this.loaded = false - } + constructor() { + this.available = false + this.enabled = false + this.hasWorkSpaces = false + this.loaded = false + } - checkWorkspaces = async () => { - try { - await this.fs.stat('.workspaces') - this.hasWorkSpaces = true - } catch (e) { + checkWorkspaces = async () => { + try { + await this.fs.stat('.workspaces') + this.hasWorkSpaces = true + } catch (e) { - } } + } - set = async () => { - const w = (window as any) - if (!this.loaded) return false - w.remixFileSystem = this.fs - w.remixFileSystem.name = this.name - w.remixFileSystemCallback = this.fsCallBack - return true - } + set = async () => { + const w = (window as any) + if (!this.loaded) return false + w.remixFileSystem = this.fs + w.remixFileSystem.name = this.name + w.remixFileSystemCallback = this.fsCallBack + return true + } } export class fileSystems { - fileSystems: Record - constructor() { - this.fileSystems = {} - } + fileSystems: Record + constructor() { + this.fileSystems = {} + } - addFileSystem = async (fs: fileSystem): Promise => { - try { - this.fileSystems[fs.name] = fs - await fs.test() && await fs.load() - console.log(fs.name + ' is loaded...') - return true - } catch (e) { - console.log(fs.name + ' not available...') - return false - } + addFileSystem = async (fs: fileSystem): Promise => { + try { + this.fileSystems[fs.name] = fs + await fs.test() && await fs.load() + console.log(fs.name + ' is loaded...') + return true + } catch (e) { + console.log(fs.name + ' not available...') + return false } - /** + } + /** * sets filesystem using list as fallback * @param {string[]} names * @returns {Promise} */ - setFileSystem = async (filesystems?: fileSystem[]): Promise => { - for (const fs of filesystems) { - if (fs && this.fileSystems[fs.name]) { - const result = await this.fileSystems[fs.name].set() - if (result) return this.fileSystems[fs.name] - } - } - return null + setFileSystem = async (filesystems?: fileSystem[]): Promise => { + for (const fs of filesystems) { + if (fs && this.fileSystems[fs.name]) { + const result = await this.fileSystems[fs.name].set() + if (result) return this.fileSystems[fs.name] + } } + return null + } } diff --git a/apps/remix-ide/src/app/files/filesystems/fileSystemUtility.ts b/apps/remix-ide/src/app/files/filesystems/fileSystemUtility.ts index 9fc716b5c1..f0ac025aba 100644 --- a/apps/remix-ide/src/app/files/filesystems/fileSystemUtility.ts +++ b/apps/remix-ide/src/app/files/filesystems/fileSystemUtility.ts @@ -3,189 +3,189 @@ import JSZip from "jszip" import { fileSystem } from "../fileSystem" const _paq = window._paq = window._paq || [] export class fileSystemUtility { - migrate = async (fsFrom: fileSystem, fsTo: fileSystem) => { - try { - await fsFrom.checkWorkspaces() - await fsTo.checkWorkspaces() - - if (fsTo.hasWorkSpaces) { - console.log(`${fsTo.name} already has files`) - return true - } - - if (!fsFrom.hasWorkSpaces) { - console.log('no files to migrate') - return true - } - - const fromFiles = await this.copyFolderToJson('/', null, null, fsFrom.fs) - await this.populateWorkspace(fromFiles, fsTo.fs) - const toFiles = await this.copyFolderToJson('/', null, null, fsTo.fs) - - if (hashMessage(JSON.stringify(toFiles)) === hashMessage(JSON.stringify(fromFiles))) { - console.log('file migration successful') - return true - } else { - _paq.push(['trackEvent', 'Migrate', 'error', 'hash mismatch']) - console.log('file migration failed falling back to ' + fsFrom.name) - fsTo.loaded = false - return false - } - } catch (err) { - console.log(err) - _paq.push(['trackEvent', 'Migrate', 'error', err && err.message]) - console.log('file migration failed falling back to ' + fsFrom.name) - fsTo.loaded = false - return false - } + migrate = async (fsFrom: fileSystem, fsTo: fileSystem) => { + try { + await fsFrom.checkWorkspaces() + await fsTo.checkWorkspaces() + + if (fsTo.hasWorkSpaces) { + console.log(`${fsTo.name} already has files`) + return true + } + + if (!fsFrom.hasWorkSpaces) { + console.log('no files to migrate') + return true + } + + const fromFiles = await this.copyFolderToJson('/', null, null, fsFrom.fs) + await this.populateWorkspace(fromFiles, fsTo.fs) + const toFiles = await this.copyFolderToJson('/', null, null, fsTo.fs) + + if (hashMessage(JSON.stringify(toFiles)) === hashMessage(JSON.stringify(fromFiles))) { + console.log('file migration successful') + return true + } else { + _paq.push(['trackEvent', 'Migrate', 'error', 'hash mismatch']) + console.log('file migration failed falling back to ' + fsFrom.name) + fsTo.loaded = false + return false + } + } catch (err) { + console.log(err) + _paq.push(['trackEvent', 'Migrate', 'error', err && err.message]) + console.log('file migration failed falling back to ' + fsFrom.name) + fsTo.loaded = false + return false } - - downloadBackup = async (fs: fileSystem) => { - try { - 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.") - await fs.checkWorkspaces() - await this.copyFolderToJson('/', null, null, fs.fs, ({ path, content }) => { - zip.file(path, content) - }) - const blob = await zip.generateAsync({ type: 'blob' }) - const today = new Date() - const date = today.getFullYear() + '-' + (today.getMonth() + 1) + '-' + today.getDate() - const time = today.getHours() + 'h' + today.getMinutes() + 'min' - this.saveAs(blob, `remix-backup-at-${time}-${date}.zip`) - _paq.push(['trackEvent','Backup','download','preload']) - } catch (err) { - _paq.push(['trackEvent','Backup','error',err && err.message]) - console.log(err) - } + } + + downloadBackup = async (fs: fileSystem) => { + try { + 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.") + await fs.checkWorkspaces() + await this.copyFolderToJson('/', null, null, fs.fs, ({ path, content }) => { + zip.file(path, content) + }) + const blob = await zip.generateAsync({ type: 'blob' }) + const today = new Date() + const date = today.getFullYear() + '-' + (today.getMonth() + 1) + '-' + today.getDate() + const time = today.getHours() + 'h' + today.getMinutes() + 'min' + this.saveAs(blob, `remix-backup-at-${time}-${date}.zip`) + _paq.push(['trackEvent','Backup','download','preload']) + } catch (err) { + _paq.push(['trackEvent','Backup','error',err && err.message]) + console.log(err) } - - populateWorkspace = async (json, fs) => { - for (const item in json) { - const isFolder = json[item].content === undefined - if (isFolder) { - await this.createDir(item, fs) - await this.populateWorkspace(json[item].children, fs) - } else { - await fs.writeFile(item, json[item].content, 'utf8') - } - } + } + + populateWorkspace = async (json, fs) => { + for (const item in json) { + const isFolder = json[item].content === undefined + if (isFolder) { + await this.createDir(item, fs) + await this.populateWorkspace(json[item].children, fs) + } else { + await fs.writeFile(item, json[item].content, 'utf8') + } } + } - /** + /** * copy the folder recursively * @param {string} path is the folder to be copied over * @param {Function} visitFile is a function called for each visited files * @param {Function} visitFolder is a function called for each visited folders */ - copyFolderToJson = async (path, visitFile, visitFolder, fs, cb = null) => { - visitFile = visitFile || (() => { }) - visitFolder = visitFolder || (() => { }) - return await this._copyFolderToJsonInternal(path, visitFile, visitFolder, fs, cb) - } + copyFolderToJson = async (path, visitFile, visitFolder, fs, cb = null) => { + visitFile = visitFile || (() => { }) + visitFolder = visitFolder || (() => { }) + return await this._copyFolderToJsonInternal(path, visitFile, visitFolder, fs, cb) + } - /** + /** * copy the folder recursively (internal use) * @param {string} path is the folder to be copied over * @param {Function} visitFile is a function called for each visited files * @param {Function} visitFolder is a function called for each visited folders */ - async _copyFolderToJsonInternal(path, visitFile, visitFolder, fs, cb) { - visitFile = visitFile || function () { /* do nothing. */ } - visitFolder = visitFolder || function () { /* do nothing. */ } - - const json = {} - // path = this.removePrefix(path) - if (await fs.exists(path)) { - const items = await fs.readdir(path) - visitFolder({ path }) - if (items.length !== 0) { - for (const item of items) { - const file: any = {} - const curPath = `${path}${path.endsWith('/') ? '' : '/'}${item}` - if ((await fs.stat(curPath)).isDirectory()) { - file.children = await this._copyFolderToJsonInternal(curPath, visitFile, visitFolder, fs, cb) - } else { - file.content = await fs.readFile(curPath, 'utf8') - if (cb) cb({ path: curPath, content: file.content }) - visitFile({ path: curPath, content: file.content }) - - } - json[curPath] = file - } - } - } - return json - } - - createDir = async (path, fs) => { - const paths = path.split('/') - if (paths.length && paths[0] === '') paths.shift() - let currentCheck = '' - for (const value of paths) { - currentCheck = currentCheck + (currentCheck ? '/' : '') + value - if (!await fs.exists(currentCheck)) { - await fs.mkdir(currentCheck) - } + async _copyFolderToJsonInternal(path, visitFile, visitFolder, fs, cb) { + visitFile = visitFile || function () { /* do nothing. */ } + visitFolder = visitFolder || function () { /* do nothing. */ } + + const json = {} + // path = this.removePrefix(path) + if (await fs.exists(path)) { + const items = await fs.readdir(path) + visitFolder({ path }) + if (items.length !== 0) { + for (const item of items) { + const file: any = {} + const curPath = `${path}${path.endsWith('/') ? '' : '/'}${item}` + if ((await fs.stat(curPath)).isDirectory()) { + file.children = await this._copyFolderToJsonInternal(curPath, visitFile, visitFolder, fs, cb) + } else { + file.content = await fs.readFile(curPath, 'utf8') + if (cb) cb({ path: curPath, content: file.content }) + visitFile({ path: curPath, content: file.content }) + + } + json[curPath] = file } + } } - - 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 + return json + } + + createDir = async (path, fs) => { + const paths = path.split('/') + if (paths.length && paths[0] === '') paths.shift() + let currentCheck = '' + for (const value of paths) { + currentCheck = currentCheck + (currentCheck ? '/' : '') + value + if (!await fs.exists(currentCheck)) { + await fs.mkdir(currentCheck) + } } + } + + 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 + } } /* eslint-disable no-template-curly-in-string */ export const migrationTestData = { - '.workspaces': { + '.workspaces': { + children: { + '.workspaces/default_workspace': { children: { - '.workspaces/default_workspace': { - children: { - '.workspaces/default_workspace/README.txt': { - content: 'TEST README' - } - } - }, - '.workspaces/emptyspace': { + '.workspaces/default_workspace/README.txt': { + content: 'TEST README' + } + } + }, + '.workspaces/emptyspace': { - }, - '.workspaces/workspace_test': { + }, + '.workspaces/workspace_test': { + children: { + '.workspaces/workspace_test/TEST_README.txt': { + content: 'TEST README' + }, + '.workspaces/workspace_test/test_contracts': { + children: { + '.workspaces/workspace_test/test_contracts/1_Storage.sol': { + content: 'testing' + }, + '.workspaces/workspace_test/test_contracts/artifacts': { children: { - '.workspaces/workspace_test/TEST_README.txt': { - content: 'TEST README' - }, - '.workspaces/workspace_test/test_contracts': { - children: { - '.workspaces/workspace_test/test_contracts/1_Storage.sol': { - content: 'testing' - }, - '.workspaces/workspace_test/test_contracts/artifacts': { - children: { - '.workspaces/workspace_test/test_contracts/artifacts/Storage_metadata.json': { - content: '{ "test": "data" }' - } - } - } - } - } + '.workspaces/workspace_test/test_contracts/artifacts/Storage_metadata.json': { + content: '{ "test": "data" }' + } } + } } + } } + } } + } } \ No newline at end of file diff --git a/apps/remix-ide/src/app/files/filesystems/indexedDB.ts b/apps/remix-ide/src/app/files/filesystems/indexedDB.ts index aae0d6a5eb..714710b9d2 100644 --- a/apps/remix-ide/src/app/files/filesystems/indexedDB.ts +++ b/apps/remix-ide/src/app/files/filesystems/indexedDB.ts @@ -2,90 +2,90 @@ import LightningFS from "@isomorphic-git/lightning-fs" import { fileSystem } from "../fileSystem" export class IndexedDBStorage extends LightningFS { - base: LightningFS.PromisifedFS - addSlash: (file: string) => string - extended: { exists: (path: string) => Promise; rmdir: (path: any) => Promise; readdir: (path: any) => Promise; unlink: (path: any) => Promise; mkdir: (path: any) => Promise; readFile: (path: any, options: any) => Promise; rename: (from: any, to: any) => Promise; writeFile: (path: any, content: any, options: any) => Promise; stat: (path: any) => Promise; init(name: string, opt?: LightningFS.FSConstructorOptions): void; activate(): Promise; deactivate(): Promise; lstat(filePath: string): Promise; readlink(filePath: string): Promise; symlink(target: string, filePath: string): Promise } - constructor(name: string) { - super(name) - this.addSlash = (file) => { - if (!file.startsWith('/')) file = '/' + file - return file - } - this.base = this.promises - this.extended = { - ...this.promises, - exists: async (path: string) => { - return new Promise((resolve) => { - this.base.stat(this.addSlash(path)).then(() => resolve(true)).catch(() => resolve(false)) - }) - }, - rmdir: async (path) => { - return this.base.rmdir(this.addSlash(path)) - }, - readdir: async (path) => { - return this.base.readdir(this.addSlash(path)) - }, - unlink: async (path) => { - return this.base.unlink(this.addSlash(path)) - }, - mkdir: async (path) => { - return this.base.mkdir(this.addSlash(path)) - }, - readFile: async (path, options) => { - return this.base.readFile(this.addSlash(path), options) - }, - rename: async (from, to) => { - return this.base.rename(this.addSlash(from), this.addSlash(to)) - }, - writeFile: async (path, content, options) => { - return this.base.writeFile(this.addSlash(path), content, options) - }, - stat: async (path) => { - return this.base.stat(this.addSlash(path)) - } - } + base: LightningFS.PromisifedFS + addSlash: (file: string) => string + extended: { exists: (path: string) => Promise; rmdir: (path: any) => Promise; readdir: (path: any) => Promise; unlink: (path: any) => Promise; mkdir: (path: any) => Promise; readFile: (path: any, options: any) => Promise; rename: (from: any, to: any) => Promise; writeFile: (path: any, content: any, options: any) => Promise; stat: (path: any) => Promise; init(name: string, opt?: LightningFS.FSConstructorOptions): void; activate(): Promise; deactivate(): Promise; lstat(filePath: string): Promise; readlink(filePath: string): Promise; symlink(target: string, filePath: string): Promise } + constructor(name: string) { + super(name) + this.addSlash = (file) => { + if (!file.startsWith('/')) file = '/' + file + return file } + this.base = this.promises + this.extended = { + ...this.promises, + exists: async (path: string) => { + return new Promise((resolve) => { + this.base.stat(this.addSlash(path)).then(() => resolve(true)).catch(() => resolve(false)) + }) + }, + rmdir: async (path) => { + return this.base.rmdir(this.addSlash(path)) + }, + readdir: async (path) => { + return this.base.readdir(this.addSlash(path)) + }, + unlink: async (path) => { + return this.base.unlink(this.addSlash(path)) + }, + mkdir: async (path) => { + return this.base.mkdir(this.addSlash(path)) + }, + readFile: async (path, options) => { + return this.base.readFile(this.addSlash(path), options) + }, + rename: async (from, to) => { + return this.base.rename(this.addSlash(from), this.addSlash(to)) + }, + writeFile: async (path, content, options) => { + return this.base.writeFile(this.addSlash(path), content, options) + }, + stat: async (path) => { + return this.base.stat(this.addSlash(path)) + } + } + } } export class indexedDBFileSystem extends fileSystem { - constructor() { - super() - this.name = 'indexedDB' - } + constructor() { + super() + this.name = 'indexedDB' + } - load = async () => { - return new Promise((resolve, reject) => { - try { - const fs = new IndexedDBStorage('RemixFileSystem') - fs.init('RemixFileSystem') - this.fs = fs.extended - this.fsCallBack = fs - this.loaded = true - resolve(true) - } catch (e) { - reject(e) - } - }) - } + load = async () => { + return new Promise((resolve, reject) => { + try { + const fs = new IndexedDBStorage('RemixFileSystem') + fs.init('RemixFileSystem') + this.fs = fs.extended + this.fsCallBack = fs + this.loaded = true + resolve(true) + } catch (e) { + reject(e) + } + }) + } - test = async () => { - return new Promise((resolve, reject) => { - if (!window.indexedDB) { - this.available = false - reject('No indexedDB on window') - } - const request = window.indexedDB.open("RemixTestDataBase"); - request.onerror = () => { - this.available = false - reject('Error creating test database') - }; - request.onsuccess = () => { - window.indexedDB.deleteDatabase("RemixTestDataBase"); - this.available = true - resolve(true) - }; - }) - } + test = async () => { + return new Promise((resolve, reject) => { + if (!window.indexedDB) { + this.available = false + reject('No indexedDB on window') + } + const request = window.indexedDB.open("RemixTestDataBase"); + request.onerror = () => { + this.available = false + reject('Error creating test database') + }; + request.onsuccess = () => { + window.indexedDB.deleteDatabase("RemixTestDataBase"); + this.available = true + resolve(true) + }; + }) + } } \ No newline at end of file diff --git a/apps/remix-ide/src/app/files/filesystems/localStorage.ts b/apps/remix-ide/src/app/files/filesystems/localStorage.ts index d99c3ae296..2d266023f1 100644 --- a/apps/remix-ide/src/app/files/filesystems/localStorage.ts +++ b/apps/remix-ide/src/app/files/filesystems/localStorage.ts @@ -2,56 +2,56 @@ import { fileSystem } from "../fileSystem"; export class localStorageFS extends fileSystem { - constructor() { - super() - this.name = 'localstorage' - } - load = async () => { - const me = this - return new Promise((resolve, reject) => { - try { - const w = window as any - w.BrowserFS.install(window) - w.BrowserFS.configure({ - fs: 'LocalStorage' - }, async function (e) { - if (e) { - console.log('BrowserFS Error: ' + e) - reject(e) - } else { - me.fs = { ...window.require('fs') } - me.fsCallBack = window.require('fs') - me.fs.readdir = me.fs.readdirSync - me.fs.readFile = me.fs.readFileSync - me.fs.writeFile = me.fs.writeFileSync - me.fs.stat = me.fs.statSync - me.fs.unlink = me.fs.unlinkSync - me.fs.rmdir = me.fs.rmdirSync - me.fs.mkdir = me.fs.mkdirSync - me.fs.rename = me.fs.renameSync - me.fs.exists = me.fs.existsSync - me.loaded = true - resolve(true) - } - }) - } catch (e) { - console.log('BrowserFS is not ready!') - reject(e) - } + constructor() { + super() + this.name = 'localstorage' + } + load = async () => { + const me = this + return new Promise((resolve, reject) => { + try { + const w = window as any + w.BrowserFS.install(window) + w.BrowserFS.configure({ + fs: 'LocalStorage' + }, async function (e) { + if (e) { + console.log('BrowserFS Error: ' + e) + reject(e) + } else { + me.fs = { ...window.require('fs') } + me.fsCallBack = window.require('fs') + me.fs.readdir = me.fs.readdirSync + me.fs.readFile = me.fs.readFileSync + me.fs.writeFile = me.fs.writeFileSync + me.fs.stat = me.fs.statSync + me.fs.unlink = me.fs.unlinkSync + me.fs.rmdir = me.fs.rmdirSync + me.fs.mkdir = me.fs.mkdirSync + me.fs.rename = me.fs.renameSync + me.fs.exists = me.fs.existsSync + me.loaded = true + resolve(true) + } }) - } + } catch (e) { + console.log('BrowserFS is not ready!') + reject(e) + } + }) + } - test = async () => { - return new Promise((resolve, reject) => { - const test = 'test'; - try { - localStorage.setItem(test, test); - localStorage.removeItem(test); - resolve(true) - } catch(e) { - reject(e) - } - }) - } + test = async () => { + return new Promise((resolve, reject) => { + const test = 'test'; + try { + localStorage.setItem(test, test); + localStorage.removeItem(test); + resolve(true) + } catch(e) { + reject(e) + } + }) + } } \ No newline at end of file diff --git a/apps/remix-ide/src/app/files/foundry-handle.js b/apps/remix-ide/src/app/files/foundry-handle.js index 94efd7b04b..7bb959b493 100644 --- a/apps/remix-ide/src/app/files/foundry-handle.js +++ b/apps/remix-ide/src/app/files/foundry-handle.js @@ -2,25 +2,25 @@ import { WebsocketPlugin } from '@remixproject/engine-web' import * as packageJson from '../../../../../package.json' const profile = { - name: 'foundry', - displayName: 'Foundry', - url: 'ws://127.0.0.1:65525', - methods: ['sync'], - description: 'Using Remixd daemon, allow to access foundry API', - kind: 'other', - version: packageJson.version + name: 'foundry', + displayName: 'Foundry', + url: 'ws://127.0.0.1:65525', + methods: ['sync'], + description: 'Using Remixd daemon, allow to access foundry API', + kind: 'other', + version: packageJson.version } export class FoundryHandle extends WebsocketPlugin { - constructor () { - super(profile) - } + constructor () { + super(profile) + } - callPluginMethod(key, payload = []) { - if (this.socket.readyState !== this.socket.OPEN) { - console.log(`${this.profile.name} connection is not opened.`) - return false - } - return super.callPluginMethod(key, payload) + callPluginMethod(key, payload = []) { + if (this.socket.readyState !== this.socket.OPEN) { + console.log(`${this.profile.name} connection is not opened.`) + return false } + return super.callPluginMethod(key, payload) + } } diff --git a/apps/remix-ide/src/app/files/git-handle.js b/apps/remix-ide/src/app/files/git-handle.js index 596bf77015..239b6568b0 100644 --- a/apps/remix-ide/src/app/files/git-handle.js +++ b/apps/remix-ide/src/app/files/git-handle.js @@ -2,25 +2,25 @@ import { WebsocketPlugin } from '@remixproject/engine-web' import * as packageJson from '../../../../../package.json' const profile = { - name: 'git', - displayName: 'Git', - url: 'ws://127.0.0.1:65521', - methods: ['execute'], - description: 'Using Remixd daemon, allow to access git API', - kind: 'other', - version: packageJson.version + name: 'git', + displayName: 'Git', + url: 'ws://127.0.0.1:65521', + methods: ['execute'], + description: 'Using Remixd daemon, allow to access git API', + kind: 'other', + version: packageJson.version } export class GitHandle extends WebsocketPlugin { - constructor () { - super(profile) - } + constructor () { + super(profile) + } - callPluginMethod(key, payload = []) { - if (this.socket.readyState !== this.socket.OPEN) { - console.log(`${this.profile.name} connection is not opened.`) - return false - } - return super.callPluginMethod(key, payload) + callPluginMethod(key, payload = []) { + if (this.socket.readyState !== this.socket.OPEN) { + console.log(`${this.profile.name} connection is not opened.`) + return false } + return super.callPluginMethod(key, payload) + } } diff --git a/apps/remix-ide/src/app/files/hardhat-handle.js b/apps/remix-ide/src/app/files/hardhat-handle.js index 84a2c1d28a..4dbe9438e8 100644 --- a/apps/remix-ide/src/app/files/hardhat-handle.js +++ b/apps/remix-ide/src/app/files/hardhat-handle.js @@ -2,25 +2,25 @@ import { WebsocketPlugin } from '@remixproject/engine-web' import * as packageJson from '../../../../../package.json' const profile = { - name: 'hardhat', - displayName: 'Hardhat', - url: 'ws://127.0.0.1:65522', - methods: ['compile', 'sync'], - description: 'Using Remixd daemon, allow to access hardhat API', - kind: 'other', - version: packageJson.version + name: 'hardhat', + displayName: 'Hardhat', + url: 'ws://127.0.0.1:65522', + methods: ['compile', 'sync'], + description: 'Using Remixd daemon, allow to access hardhat API', + kind: 'other', + version: packageJson.version } export class HardhatHandle extends WebsocketPlugin { - constructor () { - super(profile) - } + constructor () { + super(profile) + } - callPluginMethod(key, payload = []) { - if (this.socket.readyState !== this.socket.OPEN) { - console.log(`${this.profile.name} connection is not opened.`) - return false - } - return super.callPluginMethod(key, payload) + callPluginMethod(key, payload = []) { + if (this.socket.readyState !== this.socket.OPEN) { + console.log(`${this.profile.name} connection is not opened.`) + return false } + return super.callPluginMethod(key, payload) + } } diff --git a/apps/remix-ide/src/app/files/remixDProvider.js b/apps/remix-ide/src/app/files/remixDProvider.js index c52055e04f..cea884b341 100644 --- a/apps/remix-ide/src/app/files/remixDProvider.js +++ b/apps/remix-ide/src/app/files/remixDProvider.js @@ -2,209 +2,209 @@ const FileProvider = require('./fileProvider') module.exports = class RemixDProvider extends FileProvider { - constructor (appManager) { - super('localhost') - this._appManager = appManager - this.error = { EEXIST: 'File already exists' } - this._isReady = false - this._readOnlyFiles = {} - this._readOnlyMode = false - this.filesContent = {} - this.files = {} - } - - _registerEvent () { - var remixdEvents = ['connecting', 'connected', 'errored', 'closed'] - remixdEvents.forEach((event) => { - this._appManager.on('remixd', event, (value) => { - this.event.emit(event, value) - }) - }) - - this._appManager.on('remixd', 'folderAdded', (path) => { - this.event.emit('folderAdded', path) - }) - - this._appManager.on('remixd', 'fileAdded', (path) => { - this.event.emit('fileAdded', path) - }) - - this._appManager.on('remixd', 'fileChanged', (path) => { - this.event.emit('fileChanged', path) - }) - - this._appManager.on('remixd', 'fileRemoved', (path) => { - this.event.emit('fileRemoved', path) - }) - - this._appManager.on('remixd', 'fileRenamed', (oldPath, newPath) => { - this.event.emit('fileRenamed', oldPath, newPath) - }) - - this._appManager.on('remixd', 'rootFolderChanged', (path) => { - this.event.emit('rootFolderChanged', path) + constructor (appManager) { + super('localhost') + this._appManager = appManager + this.error = { EEXIST: 'File already exists' } + this._isReady = false + this._readOnlyFiles = {} + this._readOnlyMode = false + this.filesContent = {} + this.files = {} + } + + _registerEvent () { + var remixdEvents = ['connecting', 'connected', 'errored', 'closed'] + remixdEvents.forEach((event) => { + this._appManager.on('remixd', event, (value) => { + this.event.emit(event, value) + }) + }) + + this._appManager.on('remixd', 'folderAdded', (path) => { + this.event.emit('folderAdded', path) + }) + + this._appManager.on('remixd', 'fileAdded', (path) => { + this.event.emit('fileAdded', path) + }) + + this._appManager.on('remixd', 'fileChanged', (path) => { + this.event.emit('fileChanged', path) + }) + + this._appManager.on('remixd', 'fileRemoved', (path) => { + this.event.emit('fileRemoved', path) + }) + + this._appManager.on('remixd', 'fileRenamed', (oldPath, newPath) => { + this.event.emit('fileRenamed', oldPath, newPath) + }) + + this._appManager.on('remixd', 'rootFolderChanged', (path) => { + this.event.emit('rootFolderChanged', path) + }) + + this._appManager.on('remixd', 'removed', (path) => { + this.event.emit('fileRemoved', path) + }) + + this._appManager.on('remixd', 'changed', (path) => { + this.get(path, (_error, content) => { + this.event.emit('fileExternallyChanged', path, content) + }) + }) + } + + isConnected () { + return this._isReady + } + + close (cb) { + this._isReady = false + cb() + this.event.emit('disconnected') + } + + preInit () { + this.event.emit('loadingLocalhost') + } + + init (cb) { + if (this._isReady) return cb && cb() + this._appManager.call('remixd', 'folderIsReadOnly', {}) + .then((result) => { + this._isReady = true + this._readOnlyMode = result + this.event.emit('readOnlyModeChanged', result) + this._registerEvent() + this.event.emit('connected') + cb && cb() + }).catch((error) => { + cb && cb(error) + }) + } + + exists (path) { + if (!this._isReady) throw new Error('provider not ready') + const unprefixedpath = this.removePrefix(path) + + return this._appManager.call('remixd', 'exists', { path: unprefixedpath }) + .then((result) => { + return result + }) + .catch((error) => { + throw new Error(error) + }) + } + + async get (path, cb) { + if (!this._isReady) return cb && cb('provider not ready') + var unprefixedpath = this.removePrefix(path) + try{ + const file = await this._appManager.call('remixd', 'get', { path: unprefixedpath }) + this.filesContent[path] = file.content + if (file.readonly) { this._readOnlyFiles[path] = 1 } + if(cb) cb(null, file.content) + return file.content + } catch(error) { + if (error) console.log(error) + if(cb) return cb(null, this.filesContent[path]) + } + } + + async set (path, content, cb) { + if (!this._isReady) return cb && cb('provider not ready') + const unprefixedpath = this.removePrefix(path) + + return this._appManager.call('remixd', 'set', { path: unprefixedpath, content: content }).then(async (result) => { + if (cb) return cb(null, result) + }).catch((error) => { + if (cb) return cb(error) + throw new Error(error) + }) + } + + async createDir (path, cb) { + if (!this._isReady) return cb && cb('provider not ready') + const unprefixedpath = this.removePrefix(path) + + return this._appManager.call('remixd', 'createDir', { path: unprefixedpath }) + } + + isReadOnly (path) { + return this._readOnlyMode || this._readOnlyFiles[path] === 1 + } + + remove (path) { + return new Promise((resolve, reject) => { + if (!this._isReady) return reject(new Error('provider not ready')) + const unprefixedpath = this.removePrefix(path) + this._appManager.call('remixd', 'remove', { path: unprefixedpath }) + .then(result => { + const path = unprefixedpath + delete this.filesContent[path] + resolve(true) + this.init() + }).catch(error => { + if (error) console.log(error) + resolve(false) }) - - this._appManager.on('remixd', 'removed', (path) => { - this.event.emit('fileRemoved', path) - }) - - this._appManager.on('remixd', 'changed', (path) => { - this.get(path, (_error, content) => { - this.event.emit('fileExternallyChanged', path, content) - }) + }) + } + + rename (oldPath, newPath, isFolder) { + const unprefixedoldPath = this.removePrefix(oldPath) + const unprefixednewPath = this.removePrefix(newPath) + if (!this._isReady) return new Promise((resolve, reject) => reject(new Error('provider not ready'))) + return this._appManager.call('remixd', 'rename', { oldPath: unprefixedoldPath, newPath: unprefixednewPath }) + .then(result => { + const newPath = unprefixednewPath + const oldPath = unprefixedoldPath + + this.filesContent[newPath] = this.filesContent[oldPath] + delete this.filesContent[oldPath] + this.init(() => { + this.event.emit('fileRenamed', oldPath, newPath, isFolder) }) - } - - isConnected () { - return this._isReady - } - - close (cb) { - this._isReady = false - cb() - this.event.emit('disconnected') - } - - preInit () { - this.event.emit('loadingLocalhost') - } - - init (cb) { - if (this._isReady) return cb && cb() - this._appManager.call('remixd', 'folderIsReadOnly', {}) - .then((result) => { - this._isReady = true - this._readOnlyMode = result - this.event.emit('readOnlyModeChanged', result) - this._registerEvent() - this.event.emit('connected') - cb && cb() - }).catch((error) => { - cb && cb(error) - }) - } - - exists (path) { - if (!this._isReady) throw new Error('provider not ready') - const unprefixedpath = this.removePrefix(path) - - return this._appManager.call('remixd', 'exists', { path: unprefixedpath }) - .then((result) => { - return result - }) - .catch((error) => { - throw new Error(error) - }) - } - - async get (path, cb) { - if (!this._isReady) return cb && cb('provider not ready') - var unprefixedpath = this.removePrefix(path) - try{ - const file = await this._appManager.call('remixd', 'get', { path: unprefixedpath }) - this.filesContent[path] = file.content - if (file.readonly) { this._readOnlyFiles[path] = 1 } - if(cb) cb(null, file.content) - return file.content - } catch(error) { - if (error) console.log(error) - if(cb) return cb(null, this.filesContent[path]) - } - } - - async set (path, content, cb) { - if (!this._isReady) return cb && cb('provider not ready') - const unprefixedpath = this.removePrefix(path) - - return this._appManager.call('remixd', 'set', { path: unprefixedpath, content: content }).then(async (result) => { - if (cb) return cb(null, result) - }).catch((error) => { - if (cb) return cb(error) - throw new Error(error) - }) - } - - async createDir (path, cb) { - if (!this._isReady) return cb && cb('provider not ready') - const unprefixedpath = this.removePrefix(path) - - return this._appManager.call('remixd', 'createDir', { path: unprefixedpath }) - } - - isReadOnly (path) { - return this._readOnlyMode || this._readOnlyFiles[path] === 1 - } - - remove (path) { - return new Promise((resolve, reject) => { - if (!this._isReady) return reject(new Error('provider not ready')) - const unprefixedpath = this.removePrefix(path) - this._appManager.call('remixd', 'remove', { path: unprefixedpath }) - .then(result => { - const path = unprefixedpath - delete this.filesContent[path] - resolve(true) - this.init() - }).catch(error => { - if (error) console.log(error) - resolve(false) - }) - }) - } - - rename (oldPath, newPath, isFolder) { - const unprefixedoldPath = this.removePrefix(oldPath) - const unprefixednewPath = this.removePrefix(newPath) - if (!this._isReady) return new Promise((resolve, reject) => reject(new Error('provider not ready'))) - return this._appManager.call('remixd', 'rename', { oldPath: unprefixedoldPath, newPath: unprefixednewPath }) - .then(result => { - const newPath = unprefixednewPath - const oldPath = unprefixedoldPath - - this.filesContent[newPath] = this.filesContent[oldPath] - delete this.filesContent[oldPath] - this.init(() => { - this.event.emit('fileRenamed', oldPath, newPath, isFolder) - }) - return result - }).catch(error => { - console.log(error) - if (this.error[error.code]) error = this.error[error.code] - this.event.emit('fileRenamedError', this.error[error.code]) - }) - } - - isExternalFolder (path) { - return false - } - - removePrefix (path) { - path = path.indexOf(this.type) === 0 ? path.replace(this.type, '') : path - if (path[0] === '/') return path.substring(1) - if (path === '') return '/' - return path - } - - resolveDirectory (path, callback) { - if (path[0] === '/') path = path.substring(1) - const unprefixedpath = this.removePrefix(path) - - if (!this._isReady) return callback && callback('provider not ready') - this._appManager.call('remixd', 'resolveDirectory', { path: unprefixedpath }).then((result) => { - callback(null, result) - }).catch(callback) - } - - async isDirectory (path) { - const unprefixedpath = this.removePrefix(path) - if (!this._isReady) throw new Error('provider not ready') - return await this._appManager.call('remixd', 'isDirectory', { path: unprefixedpath }) - } - - async isFile (path) { - const unprefixedpath = this.removePrefix(path) - if (!this._isReady) throw new Error('provider not ready') - return await this._appManager.call('remixd', 'isFile', { path: unprefixedpath }) - } + return result + }).catch(error => { + console.log(error) + if (this.error[error.code]) error = this.error[error.code] + this.event.emit('fileRenamedError', this.error[error.code]) + }) + } + + isExternalFolder (path) { + return false + } + + removePrefix (path) { + path = path.indexOf(this.type) === 0 ? path.replace(this.type, '') : path + if (path[0] === '/') return path.substring(1) + if (path === '') return '/' + return path + } + + resolveDirectory (path, callback) { + if (path[0] === '/') path = path.substring(1) + const unprefixedpath = this.removePrefix(path) + + if (!this._isReady) return callback && callback('provider not ready') + this._appManager.call('remixd', 'resolveDirectory', { path: unprefixedpath }).then((result) => { + callback(null, result) + }).catch(callback) + } + + async isDirectory (path) { + const unprefixedpath = this.removePrefix(path) + if (!this._isReady) throw new Error('provider not ready') + return await this._appManager.call('remixd', 'isDirectory', { path: unprefixedpath }) + } + + async isFile (path) { + const unprefixedpath = this.removePrefix(path) + if (!this._isReady) throw new Error('provider not ready') + return await this._appManager.call('remixd', 'isFile', { path: unprefixedpath }) + } } diff --git a/apps/remix-ide/src/app/files/slither-handle.js b/apps/remix-ide/src/app/files/slither-handle.js index 27c7b3cbd8..a4953dd4d0 100644 --- a/apps/remix-ide/src/app/files/slither-handle.js +++ b/apps/remix-ide/src/app/files/slither-handle.js @@ -2,26 +2,26 @@ import { WebsocketPlugin } from '@remixproject/engine-web' import * as packageJson from '../../../../../package.json' const profile = { - name: 'slither', - displayName: 'Slither', - url: 'ws://127.0.0.1:65523', - methods: ['analyse'], - description: 'Using Remixd daemon, run slither static analysis', - kind: 'other', - version: packageJson.version, - documentation: 'https://remix-ide.readthedocs.io/en/latest/slither.html' + name: 'slither', + displayName: 'Slither', + url: 'ws://127.0.0.1:65523', + methods: ['analyse'], + description: 'Using Remixd daemon, run slither static analysis', + kind: 'other', + version: packageJson.version, + documentation: 'https://remix-ide.readthedocs.io/en/latest/slither.html' } export class SlitherHandle extends WebsocketPlugin { - constructor () { - super(profile) - } + constructor () { + super(profile) + } - callPluginMethod(key, payload = []) { - if (this.socket.readyState !== this.socket.OPEN) { - console.log(`${this.profile.name} connection is not opened.`) - return false - } - return super.callPluginMethod(key, payload) + callPluginMethod(key, payload = []) { + if (this.socket.readyState !== this.socket.OPEN) { + console.log(`${this.profile.name} connection is not opened.`) + return false } + return super.callPluginMethod(key, payload) + } } diff --git a/apps/remix-ide/src/app/files/truffle-handle.js b/apps/remix-ide/src/app/files/truffle-handle.js index 1111cba60d..3737d09db5 100644 --- a/apps/remix-ide/src/app/files/truffle-handle.js +++ b/apps/remix-ide/src/app/files/truffle-handle.js @@ -2,26 +2,26 @@ import { WebsocketPlugin } from '@remixproject/engine-web' import * as packageJson from '../../../../../package.json' const profile = { - name: 'truffle', - displayName: 'truffle', - url: 'ws://127.0.0.1:65524', - methods: ['compile', 'sync'], - description: 'Using Remixd daemon, allow to access truffle API', - kind: 'other', - version: packageJson.version + name: 'truffle', + displayName: 'truffle', + url: 'ws://127.0.0.1:65524', + methods: ['compile', 'sync'], + description: 'Using Remixd daemon, allow to access truffle API', + kind: 'other', + version: packageJson.version } export class TruffleHandle extends WebsocketPlugin { - constructor () { - super(profile) - } + constructor () { + super(profile) + } - callPluginMethod(key, payload = []) { - if (this.socket.readyState !== this.socket.OPEN) { - console.log(`${this.profile.name} connection is not opened.`) - return false - } - return super.callPluginMethod(key, payload) + callPluginMethod(key, payload = []) { + if (this.socket.readyState !== this.socket.OPEN) { + console.log(`${this.profile.name} connection is not opened.`) + return false } + return super.callPluginMethod(key, payload) + } } diff --git a/apps/remix-ide/src/app/files/workspaceFileProvider.js b/apps/remix-ide/src/app/files/workspaceFileProvider.js index 6193e7b486..5a96844adb 100644 --- a/apps/remix-ide/src/app/files/workspaceFileProvider.js +++ b/apps/remix-ide/src/app/files/workspaceFileProvider.js @@ -4,84 +4,84 @@ const EventManager = require('events') const FileProvider = require('./fileProvider') class WorkspaceFileProvider extends FileProvider { - constructor () { - super('') - this.workspacesPath = '.workspaces' - this.workspace = null - this.event = new EventManager() - } + constructor () { + super('') + this.workspacesPath = '.workspaces' + this.workspace = null + this.event = new EventManager() + } - setWorkspace (workspace) { - if (!workspace) return - workspace = workspace.replace(/^\/|\/$/g, '') // remove first and last slash - this.workspace = workspace - } + setWorkspace (workspace) { + if (!workspace) return + workspace = workspace.replace(/^\/|\/$/g, '') // remove first and last slash + this.workspace = workspace + } - getWorkspace () { - return this.workspace - } + getWorkspace () { + return this.workspace + } - isReady () { - return this.workspace !== null - } + isReady () { + return this.workspace !== null + } - clearWorkspace () { - this.workspace = null - } + clearWorkspace () { + this.workspace = null + } - removePrefix (path) { - if (!path) path = '/' - path = path.replace(/^\/|\/$/g, '') // remove first and last slash - path = path.replace(/^\.\/+/, '') // remove ./ from start of string - if (path.startsWith(this.workspacesPath + '/' + this.workspace)) return path - path = super.removePrefix(path) - let ret = this.workspacesPath + '/' + this.workspace + '/' + (path === '/' ? '' : path) + removePrefix (path) { + if (!path) path = '/' + path = path.replace(/^\/|\/$/g, '') // remove first and last slash + path = path.replace(/^\.\/+/, '') // remove ./ from start of string + if (path.startsWith(this.workspacesPath + '/' + this.workspace)) return path + path = super.removePrefix(path) + let ret = this.workspacesPath + '/' + this.workspace + '/' + (path === '/' ? '' : path) - ret = ret.replace(/^\/|\/$/g, '') - if (!this.isSubDirectory(this.workspacesPath + '/' + this.workspace, ret)) throw new Error('Cannot read/write to path outside workspace') - return ret - } + ret = ret.replace(/^\/|\/$/g, '') + if (!this.isSubDirectory(this.workspacesPath + '/' + this.workspace, ret)) throw new Error('Cannot read/write to path outside workspace') + return ret + } - resolveDirectory (path, callback) { - super.resolveDirectory(path, (error, files) => { - if (error) return callback(error) - const unscoped = {} - for (const file in files) { - unscoped[file.replace(this.workspacesPath + '/' + this.workspace + '/', '')] = files[file] - } - callback(null, unscoped) - }) - } + resolveDirectory (path, callback) { + super.resolveDirectory(path, (error, files) => { + if (error) return callback(error) + const unscoped = {} + for (const file in files) { + unscoped[file.replace(this.workspacesPath + '/' + this.workspace + '/', '')] = files[file] + } + callback(null, unscoped) + }) + } - async copyFolderToJson (directory, visitFile, visitFolder) { - visitFile = visitFile || function () { /* do nothing. */ } - visitFolder = visitFolder || function () { /* do nothing. */ } - const regex = new RegExp(`.workspaces/${this.workspace}/`, 'g') - let json = await super._copyFolderToJsonInternal(directory, ({ path, content }) => { - visitFile({ path: path.replace(regex, ''), content }) - }, ({ path }) => { - visitFolder({ path: path.replace(regex, '') }) - }) - json = JSON.stringify(json).replace(regex, '') - return JSON.parse(json) - } + async copyFolderToJson (directory, visitFile, visitFolder) { + visitFile = visitFile || function () { /* do nothing. */ } + visitFolder = visitFolder || function () { /* do nothing. */ } + const regex = new RegExp(`.workspaces/${this.workspace}/`, 'g') + let json = await super._copyFolderToJsonInternal(directory, ({ path, content }) => { + visitFile({ path: path.replace(regex, ''), content }) + }, ({ path }) => { + visitFolder({ path: path.replace(regex, '') }) + }) + json = JSON.stringify(json).replace(regex, '') + return JSON.parse(json) + } - _normalizePath (path) { - return path.replace(this.workspacesPath + '/' + this.workspace + '/', '') - } + _normalizePath (path) { + return path.replace(this.workspacesPath + '/' + this.workspace + '/', '') + } - async createWorkspace (name) { - try { - if (!name) name = 'default_workspace' - const path = this.workspacesPath + '/' + name + async createWorkspace (name) { + try { + if (!name) name = 'default_workspace' + const path = this.workspacesPath + '/' + name - await super.forceCreateDir(path) - this.setWorkspace(name) - this.event.emit('createWorkspace', name) - } catch (e) { - throw new Error(e) - } + await super.forceCreateDir(path) + this.setWorkspace(name) + this.event.emit('createWorkspace', name) + } catch (e) { + throw new Error(e) } + } } module.exports = WorkspaceFileProvider diff --git a/apps/remix-ide/src/app/panels/file-panel.js b/apps/remix-ide/src/app/panels/file-panel.js index b391daf5bd..51167d5e69 100644 --- a/apps/remix-ide/src/app/panels/file-panel.js +++ b/apps/remix-ide/src/app/panels/file-panel.js @@ -28,43 +28,43 @@ const { SlitherHandle } = require('../files/slither-handle.js') */ const profile = { - name: 'filePanel', - displayName: 'File explorer', - methods: ['createNewFile', 'uploadFile', 'getCurrentWorkspace', 'getAvailableWorkspaceName', 'getWorkspaces', 'createWorkspace', 'setWorkspace', 'registerContextMenuItem', 'renameWorkspace', 'deleteWorkspace'], - events: ['setWorkspace', 'workspaceRenamed', 'workspaceDeleted', 'workspaceCreated'], - icon: 'assets/img/fileManager.webp', - description: 'Remix IDE file explorer', - kind: 'fileexplorer', - location: 'sidePanel', - documentation: 'https://remix-ide.readthedocs.io/en/latest/file_explorer.html', - version: packageJson.version, - maintainedBy: 'Remix' + name: 'filePanel', + displayName: 'File explorer', + methods: ['createNewFile', 'uploadFile', 'getCurrentWorkspace', 'getAvailableWorkspaceName', 'getWorkspaces', 'createWorkspace', 'setWorkspace', 'registerContextMenuItem', 'renameWorkspace', 'deleteWorkspace'], + events: ['setWorkspace', 'workspaceRenamed', 'workspaceDeleted', 'workspaceCreated'], + icon: 'assets/img/fileManager.webp', + description: 'Remix IDE file explorer', + kind: 'fileexplorer', + location: 'sidePanel', + documentation: 'https://remix-ide.readthedocs.io/en/latest/file_explorer.html', + version: packageJson.version, + maintainedBy: 'Remix' } module.exports = class Filepanel extends ViewPlugin { - constructor (appManager) { - super(profile) - this.registry = Registry.getInstance() - this.fileProviders = this.registry.get('fileproviders').api - this.fileManager = this.registry.get('filemanager').api - - this.el = document.createElement('div') - this.el.setAttribute('id', 'fileExplorerView') - - this.remixdHandle = new RemixdHandle(this.fileProviders.localhost, appManager) - this.hardhatHandle = new HardhatHandle() - this.foundryHandle = new FoundryHandle() - this.truffleHandle = new TruffleHandle() - this.slitherHandle = new SlitherHandle() - this.workspaces = [] - this.appManager = appManager - this.currentWorkspaceMetadata = null - } - - render () { - return
- } - - /** + constructor (appManager) { + super(profile) + this.registry = Registry.getInstance() + this.fileProviders = this.registry.get('fileproviders').api + this.fileManager = this.registry.get('filemanager').api + + this.el = document.createElement('div') + this.el.setAttribute('id', 'fileExplorerView') + + this.remixdHandle = new RemixdHandle(this.fileProviders.localhost, appManager) + this.hardhatHandle = new HardhatHandle() + this.foundryHandle = new FoundryHandle() + this.truffleHandle = new TruffleHandle() + this.slitherHandle = new SlitherHandle() + this.workspaces = [] + this.appManager = appManager + this.currentWorkspaceMetadata = null + } + + render () { + return
+ } + + /** * @param item { id: string, name: string, type?: string[], path?: string[], extension?: string[], pattern?: string[] } * typically: * group 0 for file manipulations @@ -77,112 +77,112 @@ module.exports = class Filepanel extends ViewPlugin { * group 7 for generating resource files (UML, documentation, ...) * @param callback (...args) => void */ - registerContextMenuItem (item) { - return new Promise((resolve, reject) => { - this.emit('registerContextMenuItemReducerEvent', item, (err, data) => { - if (err) reject(err) - else resolve(data) - }) - }) - } - - removePluginActions (plugin) { - return new Promise((resolve, reject) => { - this.emit('removePluginActionsReducerEvent', plugin, (err, data) => { - if (err) reject(err) - else resolve(data) - }) - }) - } - - getCurrentWorkspace () { - return this.currentWorkspaceMetadata - } - - getWorkspaces () { - return this.workspaces - } - - getAvailableWorkspaceName (name) { - if(!this.workspaces) return name - let index = 1 - let workspace = this.workspaces.find(workspace => workspace.name === name + ' - ' + index) - while (workspace) { - index++ - workspace = this.workspaces.find(workspace => workspace.name === name + ' - ' + index) - } - return name + ' - ' + index - } - - setWorkspaces (workspaces) { - this.workspaces = workspaces - } - - createNewFile () { - return new Promise((resolve, reject) => { - this.emit('createNewFileInputReducerEvent', '/', (err, data) => { - if (err) reject(err) - else resolve(data) - }) - }) - } - - uploadFile (target) { - return new Promise((resolve, reject) => { - return this.emit('uploadFileReducerEvent', '/', target, (err, data) => { - if (err) reject(err) - else resolve(data) - }) - }) - } - - createWorkspace (workspaceName, workspaceTemplateName, isEmpty) { - return new Promise((resolve, reject) => { - this.emit('createWorkspaceReducerEvent', workspaceName, workspaceTemplateName, isEmpty, (err, data) => { - if (err) reject(err) - else resolve(data || true) - }) - }) - } - - renameWorkspace (oldName, workspaceName) { - return new Promise((resolve, reject) => { - this.emit('renameWorkspaceReducerEvent', oldName, workspaceName, (err, data) => { - if (err) reject(err) - else resolve(data || true) - }) - }) - } - - deleteWorkspace (workspaceName) { - return new Promise((resolve, reject) => { - this.emit('deleteWorkspaceReducerEvent', workspaceName, (err, data) => { - if (err) reject(err) - else resolve(data || true) - }) - }) - } - - setWorkspace (workspace) { - const workspaceProvider = this.fileProviders.workspace - - this.currentWorkspaceMetadata = { name: workspace.name, isLocalhost: workspace.isLocalhost, absolutePath: `${workspaceProvider.workspacesPath}/${workspace.name}` } - if (workspace.name !== " - connect to localhost - ") { - localStorage.setItem('currentWorkspace', workspace.name) - } - this.emit('setWorkspace', workspace) - } - - workspaceRenamed (oldName, workspaceName) { - this.emit('workspaceRenamed', oldName, workspaceName) - } - - workspaceDeleted (workspace) { - this.emit('workspaceDeleted', workspace) - } - - workspaceCreated (workspace) { - this.emit('workspaceCreated', workspace) - } - /** end section */ + registerContextMenuItem (item) { + return new Promise((resolve, reject) => { + this.emit('registerContextMenuItemReducerEvent', item, (err, data) => { + if (err) reject(err) + else resolve(data) + }) + }) + } + + removePluginActions (plugin) { + return new Promise((resolve, reject) => { + this.emit('removePluginActionsReducerEvent', plugin, (err, data) => { + if (err) reject(err) + else resolve(data) + }) + }) + } + + getCurrentWorkspace () { + return this.currentWorkspaceMetadata + } + + getWorkspaces () { + return this.workspaces + } + + getAvailableWorkspaceName (name) { + if(!this.workspaces) return name + let index = 1 + let workspace = this.workspaces.find(workspace => workspace.name === name + ' - ' + index) + while (workspace) { + index++ + workspace = this.workspaces.find(workspace => workspace.name === name + ' - ' + index) + } + return name + ' - ' + index + } + + setWorkspaces (workspaces) { + this.workspaces = workspaces + } + + createNewFile () { + return new Promise((resolve, reject) => { + this.emit('createNewFileInputReducerEvent', '/', (err, data) => { + if (err) reject(err) + else resolve(data) + }) + }) + } + + uploadFile (target) { + return new Promise((resolve, reject) => { + return this.emit('uploadFileReducerEvent', '/', target, (err, data) => { + if (err) reject(err) + else resolve(data) + }) + }) + } + + createWorkspace (workspaceName, workspaceTemplateName, isEmpty) { + return new Promise((resolve, reject) => { + this.emit('createWorkspaceReducerEvent', workspaceName, workspaceTemplateName, isEmpty, (err, data) => { + if (err) reject(err) + else resolve(data || true) + }) + }) + } + + renameWorkspace (oldName, workspaceName) { + return new Promise((resolve, reject) => { + this.emit('renameWorkspaceReducerEvent', oldName, workspaceName, (err, data) => { + if (err) reject(err) + else resolve(data || true) + }) + }) + } + + deleteWorkspace (workspaceName) { + return new Promise((resolve, reject) => { + this.emit('deleteWorkspaceReducerEvent', workspaceName, (err, data) => { + if (err) reject(err) + else resolve(data || true) + }) + }) + } + + setWorkspace (workspace) { + const workspaceProvider = this.fileProviders.workspace + + this.currentWorkspaceMetadata = { name: workspace.name, isLocalhost: workspace.isLocalhost, absolutePath: `${workspaceProvider.workspacesPath}/${workspace.name}` } + if (workspace.name !== " - connect to localhost - ") { + localStorage.setItem('currentWorkspace', workspace.name) + } + this.emit('setWorkspace', workspace) + } + + workspaceRenamed (oldName, workspaceName) { + this.emit('workspaceRenamed', oldName, workspaceName) + } + + workspaceDeleted (workspace) { + this.emit('workspaceDeleted', workspace) + } + + workspaceCreated (workspace) { + this.emit('workspaceCreated', workspace) + } + /** end section */ } diff --git a/apps/remix-ide/src/app/panels/layout.ts b/apps/remix-ide/src/app/panels/layout.ts index 3a44d1f83b..a42c15b5b7 100644 --- a/apps/remix-ide/src/app/panels/layout.ts +++ b/apps/remix-ide/src/app/panels/layout.ts @@ -4,9 +4,9 @@ import { EventEmitter } from 'events' import { QueryParams } from '@remix-project/remix-lib' const profile: Profile = { - name: 'layout', - description: 'layout', - methods: ['minimize', 'maximiseSidePanel', 'resetSidePanel'] + name: 'layout', + description: 'layout', + methods: ['minimize', 'maximiseSidePanel', 'resetSidePanel'] } interface panelState { @@ -28,90 +28,90 @@ export type PanelConfiguration = { } export class Layout extends Plugin { - event: any - panels: panels - maximised: { [key: string]: boolean } - constructor () { - super(profile) - this.maximised = {} - this.event = new EventEmitter() - } + event: any + panels: panels + maximised: { [key: string]: boolean } + constructor () { + super(profile) + this.maximised = {} + this.event = new EventEmitter() + } - async onActivation (): Promise { - this.on('fileManager', 'currentFileChanged', () => { - this.panels.editor.active = true - this.panels.main.active = false - this.event.emit('change', null) - }) - this.on('tabs', 'openFile', () => { - this.panels.editor.active = true - this.panels.main.active = false - this.event.emit('change', null) - }) - this.on('tabs', 'switchApp', (name: string) => { - this.call('mainPanel', 'showContent', name) - this.panels.editor.active = false - this.panels.main.active = true - this.event.emit('change', null) - }) - this.on('tabs', 'closeApp', (name: string) => { - this.panels.editor.active = true - this.panels.main.active = false - this.event.emit('change', null) - }) - this.on('manager', 'activate', (profile: Profile) => { - switch (profile.name) { - case 'filePanel': - this.call('menuicons', 'select', 'filePanel') - break - } - }) - this.on('sidePanel', 'focusChanged', async (name) => { - const current = await this.call('sidePanel', 'currentFocus') - if (this.maximised[current]) { - this.event.emit('maximisesidepanel') - } else { - this.event.emit('resetsidepanel') - } - }) - document.addEventListener('keypress', e => { - if (e.shiftKey && e.ctrlKey) { - if (e.code === 'KeyF') { - // Ctrl+Shift+F - this.call('menuicons', 'select', 'filePanel') - } else if (e.code === 'KeyA') { - // Ctrl+Shift+A - this.call('menuicons', 'select', 'pluginManager') - } - e.preventDefault() - } - }) - const queryParams = new QueryParams() - const params = queryParams.get() as PanelConfiguration - if (params.minimizeterminal || params.embed) { - this.panels.terminal.minimized = true - this.event.emit('change', this.panels) - this.emit('change', this.panels) - } - if (params.minimizesidepanel || params.embed) { - this.event.emit('minimizesidepanel') + async onActivation (): Promise { + this.on('fileManager', 'currentFileChanged', () => { + this.panels.editor.active = true + this.panels.main.active = false + this.event.emit('change', null) + }) + this.on('tabs', 'openFile', () => { + this.panels.editor.active = true + this.panels.main.active = false + this.event.emit('change', null) + }) + this.on('tabs', 'switchApp', (name: string) => { + this.call('mainPanel', 'showContent', name) + this.panels.editor.active = false + this.panels.main.active = true + this.event.emit('change', null) + }) + this.on('tabs', 'closeApp', (name: string) => { + this.panels.editor.active = true + this.panels.main.active = false + this.event.emit('change', null) + }) + this.on('manager', 'activate', (profile: Profile) => { + switch (profile.name) { + case 'filePanel': + this.call('menuicons', 'select', 'filePanel') + break + } + }) + this.on('sidePanel', 'focusChanged', async (name) => { + const current = await this.call('sidePanel', 'currentFocus') + if (this.maximised[current]) { + this.event.emit('maximisesidepanel') + } else { + this.event.emit('resetsidepanel') + } + }) + document.addEventListener('keypress', e => { + if (e.shiftKey && e.ctrlKey) { + if (e.code === 'KeyF') { + // Ctrl+Shift+F + this.call('menuicons', 'select', 'filePanel') + } else if (e.code === 'KeyA') { + // Ctrl+Shift+A + this.call('menuicons', 'select', 'pluginManager') } + e.preventDefault() + } + }) + const queryParams = new QueryParams() + const params = queryParams.get() as PanelConfiguration + if (params.minimizeterminal || params.embed) { + this.panels.terminal.minimized = true + this.event.emit('change', this.panels) + this.emit('change', this.panels) } - - minimize (name: string, minimized:boolean): void { - this.panels[name].minimized = minimized - this.event.emit('change', null) + if (params.minimizesidepanel || params.embed) { + this.event.emit('minimizesidepanel') } + } - async maximiseSidePanel () { - this.event.emit('maximisesidepanel') - const current = await this.call('sidePanel', 'currentFocus') - this.maximised[current] = true - } + minimize (name: string, minimized:boolean): void { + this.panels[name].minimized = minimized + this.event.emit('change', null) + } - async resetSidePanel () { - this.event.emit('resetsidepanel') - const current = await this.call('sidePanel', 'currentFocus') - this.maximised[current] = false - } + async maximiseSidePanel () { + this.event.emit('maximisesidepanel') + const current = await this.call('sidePanel', 'currentFocus') + this.maximised[current] = true + } + + async resetSidePanel () { + this.event.emit('resetsidepanel') + const current = await this.call('sidePanel', 'currentFocus') + this.maximised[current] = false + } } diff --git a/apps/remix-ide/src/app/panels/tab-proxy.js b/apps/remix-ide/src/app/panels/tab-proxy.js index 984efe7534..fec10ca82c 100644 --- a/apps/remix-ide/src/app/panels/tab-proxy.js +++ b/apps/remix-ide/src/app/panels/tab-proxy.js @@ -5,359 +5,359 @@ import { PluginViewWrapper, getPathIcon } from '@remix-ui/helper' const EventEmitter = require('events') const profile = { - name: 'tabs', - methods: ['focus'], - kind: 'other' + name: 'tabs', + methods: ['focus'], + kind: 'other' } export class TabProxy extends Plugin { - constructor (fileManager, editor) { - super(profile) - this.event = new EventEmitter() - this.fileManager = fileManager - this.editor = editor - this.data = {} - this._view = {} - this._handlers = {} - this.loadedTabs = [] - this.dispatch = null - this.themeQuality = 'dark' - } - - async onActivation () { - this.on('theme', 'themeChanged', (theme) => { - this.themeQuality = theme.quality - // update invert for all icons - this.renderComponent() - }) - - this.on('fileManager', 'filesAllClosed', () => { - this.call('manager', 'activatePlugin', 'home') - this.focus('home') - }) - - this.on('fileManager', 'fileRemoved', (name) => { - const workspace = this.fileManager.currentWorkspace() - - if (this.fileManager.mode === 'browser') { - name = name.startsWith(workspace + '/') ? name : workspace + '/' + name - // If deleted file is not current file and not an active tab in editor, - // ensure current file is active in the editor - if (this.fileManager.currentFile() && name !== this.fileManager.currentFile()) { - const currentFile = this.fileManager.currentFile() - const currentFileTabPath = currentFile.startsWith(workspace + '/') ? currentFile : workspace + '/' + currentFile - this.removeTab(name, { name: currentFileTabPath }) - } else this.removeTab(name) - } else { - name = name.startsWith(this.fileManager.mode + '/') ? name : this.fileManager.mode + '/' + name - this.removeTab(name) - } + constructor (fileManager, editor) { + super(profile) + this.event = new EventEmitter() + this.fileManager = fileManager + this.editor = editor + this.data = {} + this._view = {} + this._handlers = {} + this.loadedTabs = [] + this.dispatch = null + this.themeQuality = 'dark' + } + + async onActivation () { + this.on('theme', 'themeChanged', (theme) => { + this.themeQuality = theme.quality + // update invert for all icons + this.renderComponent() + }) + + this.on('fileManager', 'filesAllClosed', () => { + this.call('manager', 'activatePlugin', 'home') + this.focus('home') + }) + + this.on('fileManager', 'fileRemoved', (name) => { + const workspace = this.fileManager.currentWorkspace() + + if (this.fileManager.mode === 'browser') { + name = name.startsWith(workspace + '/') ? name : workspace + '/' + name + // If deleted file is not current file and not an active tab in editor, + // ensure current file is active in the editor + if (this.fileManager.currentFile() && name !== this.fileManager.currentFile()) { + const currentFile = this.fileManager.currentFile() + const currentFileTabPath = currentFile.startsWith(workspace + '/') ? currentFile : workspace + '/' + currentFile + this.removeTab(name, { name: currentFileTabPath }) + } else this.removeTab(name) + } else { + name = name.startsWith(this.fileManager.mode + '/') ? name : this.fileManager.mode + '/' + name + this.removeTab(name) + } + }) + + this.on('fileManager', 'fileClosed', (name) => { + const workspace = this.fileManager.currentWorkspace() + if (this.fileManager.mode === 'browser') { + name = name.startsWith(workspace + '/') ? name : workspace + '/' + name + let tabIndex = this.loadedTabs.findIndex(tab => tab.name === name) + + // If tab doesn't exist, check if tab is opened because of abrupt disconnection with remixd + if (tabIndex === -1) { + const nameArray = name.split('/') + nameArray.shift() + name = 'localhost' + '/' + nameArray.join('/') + tabIndex = this.loadedTabs.findIndex(tab => tab.name === name) + if(tabIndex !== -1) this.removeTab(name) + } else this.removeTab(name) + } else { + name = name.startsWith(this.fileManager.mode + '/') ? name : this.fileManager.mode + '/' + name + this.removeTab(name) + } + }) + + this.on('fileManager', 'currentFileChanged', (file) => { + const workspace = this.fileManager.currentWorkspace() + + if (this.fileManager.mode === 'browser') { + const workspacePath = workspace + '/' + file + + if (this._handlers[workspacePath]) { + this.tabsApi.activateTab(workspacePath) + return + } + this.addTab(workspacePath, '', async () => { + await this.fileManager.open(file) + this.event.emit('openFile', file) + this.emit('openFile', file) + }, + async () => { + await this.fileManager.closeFile(file) + this.event.emit('closeFile', file) + this.emit('closeFile', file) }) + this.tabsApi.activateTab(workspacePath) + } else { + const path = file.startsWith(this.fileManager.mode + '/') ? file : this.fileManager.mode + '/' + file - this.on('fileManager', 'fileClosed', (name) => { - const workspace = this.fileManager.currentWorkspace() - if (this.fileManager.mode === 'browser') { - name = name.startsWith(workspace + '/') ? name : workspace + '/' + name - let tabIndex = this.loadedTabs.findIndex(tab => tab.name === name) - - // If tab doesn't exist, check if tab is opened because of abrupt disconnection with remixd - if (tabIndex === -1) { - const nameArray = name.split('/') - nameArray.shift() - name = 'localhost' + '/' + nameArray.join('/') - tabIndex = this.loadedTabs.findIndex(tab => tab.name === name) - if(tabIndex !== -1) this.removeTab(name) - } else this.removeTab(name) - } else { - name = name.startsWith(this.fileManager.mode + '/') ? name : this.fileManager.mode + '/' + name - this.removeTab(name) - } + if (this._handlers[path]) { + this.tabsApi.activateTab(path) + return + } + this.addTab(path, '', async () => { + await this.fileManager.open(file) + this.event.emit('openFile', file) + this.emit('openFile', file) + }, + async () => { + await this.fileManager.closeFile(file) + this.event.emit('closeFile', file) + this.emit('closeFile', file) }) - - this.on('fileManager', 'currentFileChanged', (file) => { - const workspace = this.fileManager.currentWorkspace() - - if (this.fileManager.mode === 'browser') { - const workspacePath = workspace + '/' + file - - if (this._handlers[workspacePath]) { - this.tabsApi.activateTab(workspacePath) - return - } - this.addTab(workspacePath, '', async () => { - await this.fileManager.open(file) - this.event.emit('openFile', file) - this.emit('openFile', file) - }, - async () => { - await this.fileManager.closeFile(file) - this.event.emit('closeFile', file) - this.emit('closeFile', file) - }) - this.tabsApi.activateTab(workspacePath) - } else { - const path = file.startsWith(this.fileManager.mode + '/') ? file : this.fileManager.mode + '/' + file - - if (this._handlers[path]) { - this.tabsApi.activateTab(path) - return - } - this.addTab(path, '', async () => { - await this.fileManager.open(file) - this.event.emit('openFile', file) - this.emit('openFile', file) - }, - async () => { - await this.fileManager.closeFile(file) - this.event.emit('closeFile', file) - this.emit('closeFile', file) - }) - this.tabsApi.activateTab(path) + this.tabsApi.activateTab(path) + } + }) + + this.on('fileManager', 'fileRenamed', (oldName, newName, isFolder) => { + const workspace = this.fileManager.currentWorkspace() + + if (this.fileManager.mode === 'browser') { + if (isFolder) { + for (const tab of this.loadedTabs) { + if (tab.name.indexOf(workspace + '/' + oldName + '/') === 0) { + const newTabName = workspace + '/' + newName + tab.name.slice(workspace + '/' + oldName.length, tab.name.length) + this.renameTab(tab.name, newTabName) } - }) - - this.on('fileManager', 'fileRenamed', (oldName, newName, isFolder) => { - const workspace = this.fileManager.currentWorkspace() - - if (this.fileManager.mode === 'browser') { - if (isFolder) { - for (const tab of this.loadedTabs) { - if (tab.name.indexOf(workspace + '/' + oldName + '/') === 0) { - const newTabName = workspace + '/' + newName + tab.name.slice(workspace + '/' + oldName.length, tab.name.length) - this.renameTab(tab.name, newTabName) - } - } - return - } - // should change the tab title too - this.renameTab(workspace + '/' + oldName, workspace + '/' + newName) - } else { - if (isFolder) { - for (const tab of this.loadedTabs) { - if (tab.name.indexOf(this.fileManager.mode + '/' + oldName + '/') === 0) { - const newTabName = this.fileManager.mode + '/' + newName + tab.name.slice(this.fileManager.mode + '/' + oldName.length, tab.name.length) - this.renameTab(tab.name, newTabName) - } - } - return - } - // should change the tab title too - this.renameTab(this.fileManager.mode + '/' + oldName, this.fileManager.mode + '/' + newName) + } + return + } + // should change the tab title too + this.renameTab(workspace + '/' + oldName, workspace + '/' + newName) + } else { + if (isFolder) { + for (const tab of this.loadedTabs) { + if (tab.name.indexOf(this.fileManager.mode + '/' + oldName + '/') === 0) { + const newTabName = this.fileManager.mode + '/' + newName + tab.name.slice(this.fileManager.mode + '/' + oldName.length, tab.name.length) + this.renameTab(tab.name, newTabName) } - }) - - this.on('manager', 'pluginActivated', ({ name, location, displayName, icon, description }) => { - if (location === 'mainPanel') { - this.addTab( - name, - displayName, - () => this.emit('switchApp', name), - () => { - if (name === 'home' && this.loadedTabs.length === 1 && this.loadedTabs[0].id === "home") { - const files = Object.keys(this.editor.sessions) - files.forEach(filepath => this.editor.discard(filepath)) - } - this.emit('closeApp', name) - this.call('manager', 'deactivatePlugin', name) - }, - icon, - description - ) - this.switchTab(name) + } + return + } + // should change the tab title too + this.renameTab(this.fileManager.mode + '/' + oldName, this.fileManager.mode + '/' + newName) + } + }) + + this.on('manager', 'pluginActivated', ({ name, location, displayName, icon, description }) => { + if (location === 'mainPanel') { + this.addTab( + name, + displayName, + () => this.emit('switchApp', name), + () => { + if (name === 'home' && this.loadedTabs.length === 1 && this.loadedTabs[0].id === "home") { + const files = Object.keys(this.editor.sessions) + files.forEach(filepath => this.editor.discard(filepath)) } - }) - - this.on('manager', 'pluginDeactivated', (profile) => { - this.removeTab(profile.name) - }) - - this.on('fileDecorator', 'fileDecoratorsChanged', async (items) => { - this.tabsApi.setFileDecorations(items) - }) + this.emit('closeApp', name) + this.call('manager', 'deactivatePlugin', name) + }, + icon, + description + ) + this.switchTab(name) + } + }) + + this.on('manager', 'pluginDeactivated', (profile) => { + this.removeTab(profile.name) + }) + + this.on('fileDecorator', 'fileDecoratorsChanged', async (items) => { + this.tabsApi.setFileDecorations(items) + }) - try { - this.themeQuality = (await this.call('theme', 'currentTheme') ).quality - } catch (e) { - console.log('theme plugin has an issue: ', e) - } - this.renderComponent() + try { + this.themeQuality = (await this.call('theme', 'currentTheme') ).quality + } catch (e) { + console.log('theme plugin has an issue: ', e) } - - focus (name) { - this.emit('switchApp', name) - this.tabsApi.activateTab(name) + this.renderComponent() + } + + focus (name) { + this.emit('switchApp', name) + this.tabsApi.activateTab(name) + } + + switchTab (tabName) { + if (this._handlers[tabName]) { + this._handlers[tabName].switchTo() + this.tabsApi.activateTab(tabName) } - - switchTab (tabName) { - if (this._handlers[tabName]) { - this._handlers[tabName].switchTo() - this.tabsApi.activateTab(tabName) - } + } + + switchNextTab () { + const active = this.tabsApi.active() + if (active && this._handlers[active]) { + const handlers = Object.keys(this._handlers) + let i = handlers.indexOf(active) + if (i >= 0) { + i = handlers[i + 1] ? i + 1 : 0 + this.switchTab(handlers[i]) + } } - - switchNextTab () { - const active = this.tabsApi.active() - if (active && this._handlers[active]) { - const handlers = Object.keys(this._handlers) - let i = handlers.indexOf(active) - if (i >= 0) { - i = handlers[i + 1] ? i + 1 : 0 - this.switchTab(handlers[i]) - } - } + } + + switchPreviousTab () { + const active = this.tabsApi.active() + if (active && this._handlers[active]) { + const handlers = Object.keys(this._handlers) + let i = handlers.indexOf(active) + if (i >= 0) { + i = handlers[i - 1] ? i - 1 : handlers.length - 1 + this.switchTab(handlers[i]) + } } - - switchPreviousTab () { - const active = this.tabsApi.active() - if (active && this._handlers[active]) { - const handlers = Object.keys(this._handlers) - let i = handlers.indexOf(active) - if (i >= 0) { - i = handlers[i - 1] ? i - 1 : handlers.length - 1 - this.switchTab(handlers[i]) - } - } - } - - renameTab (oldName, newName) { - // The new tab is being added by FileManager - this.removeTab(oldName) - } - - addTab (name, title, switchTo, close, icon, description = '') { - if (this._handlers[name]) return this.renderComponent() - - var slash = name.split('/') - const tabPath = slash.reverse() - const tempTitle = [] - - if (!title) { - for (let i = 0; i < tabPath.length; i++) { - tempTitle.push(tabPath[i]) - const formatPath = [...tempTitle].reverse() - const index = this.loadedTabs.findIndex(({ title }) => title === formatPath.join('/')) - - if (index === -1) { - title = formatPath.join('/') - const titleLength = formatPath.length - this.loadedTabs.push({ - id: name, - name, - title, - icon, - tooltip: name, - iconClass: getPathIcon(name) - }) - formatPath.shift() - if (formatPath.length > 0) { - const index = this.loadedTabs.findIndex(({ title }) => title === formatPath.join('/')) - if (index > -1) { - const duplicateTabName = this.loadedTabs[index].name - const duplicateTabTooltip = this.loadedTabs[index].description - const duplicateTabPath = duplicateTabName.split('/') - const duplicateTabFormatPath = [...duplicateTabPath].reverse() - const duplicateTabTitle = duplicateTabFormatPath.slice(0, titleLength).reverse().join('/') - this.loadedTabs[index] = { - id: duplicateTabName, - name: duplicateTabName, - title: duplicateTabTitle, - icon, - tooltip: duplicateTabTooltip || duplicateTabTitle, - iconClass: getPathIcon(duplicateTabName) - } - } - } - break - } - } - } else { - this.loadedTabs.push({ - id: name, - name, - title, + } + + renameTab (oldName, newName) { + // The new tab is being added by FileManager + this.removeTab(oldName) + } + + addTab (name, title, switchTo, close, icon, description = '') { + if (this._handlers[name]) return this.renderComponent() + + var slash = name.split('/') + const tabPath = slash.reverse() + const tempTitle = [] + + if (!title) { + for (let i = 0; i < tabPath.length; i++) { + tempTitle.push(tabPath[i]) + const formatPath = [...tempTitle].reverse() + const index = this.loadedTabs.findIndex(({ title }) => title === formatPath.join('/')) + + if (index === -1) { + title = formatPath.join('/') + const titleLength = formatPath.length + this.loadedTabs.push({ + id: name, + name, + title, + icon, + tooltip: name, + iconClass: getPathIcon(name) + }) + formatPath.shift() + if (formatPath.length > 0) { + const index = this.loadedTabs.findIndex(({ title }) => title === formatPath.join('/')) + if (index > -1) { + const duplicateTabName = this.loadedTabs[index].name + const duplicateTabTooltip = this.loadedTabs[index].description + const duplicateTabPath = duplicateTabName.split('/') + const duplicateTabFormatPath = [...duplicateTabPath].reverse() + const duplicateTabTitle = duplicateTabFormatPath.slice(0, titleLength).reverse().join('/') + this.loadedTabs[index] = { + id: duplicateTabName, + name: duplicateTabName, + title: duplicateTabTitle, icon, - tooltip: description || title, - iconClass: getPathIcon(name) - }) - } - - this.renderComponent() - this._handlers[name] = { switchTo, close } - } - - removeTab (name, currentFileTab) { - delete this._handlers[name] - let previous = currentFileTab - this.loadedTabs = this.loadedTabs.filter((tab, index) => { - if (!previous && tab.name === name) { - if(index - 1 >= 0 && this.loadedTabs[index - 1]) - previous = this.loadedTabs[index - 1] - else if (index + 1 && this.loadedTabs[index + 1]) - previous = this.loadedTabs[index + 1] + tooltip: duplicateTabTooltip || duplicateTabTitle, + iconClass: getPathIcon(duplicateTabName) + } } - return tab.name !== name - }) - this.renderComponent() - if (previous) this.switchTab(previous.name) - } - - addHandler (type, fn) { - this.handlers[type] = fn - } - - setDispatch (dispatch) { - this.dispatch = dispatch - this.renderComponent() + } + break + } + } + } else { + this.loadedTabs.push({ + id: name, + name, + title, + icon, + tooltip: description || title, + iconClass: getPathIcon(name) + }) } - updateComponent(state) { - return + this.renderComponent() + this._handlers[name] = { switchTo, close } + } + + removeTab (name, currentFileTab) { + delete this._handlers[name] + let previous = currentFileTab + this.loadedTabs = this.loadedTabs.filter((tab, index) => { + if (!previous && tab.name === name) { + if(index - 1 >= 0 && this.loadedTabs[index - 1]) + previous = this.loadedTabs[index - 1] + else if (index + 1 && this.loadedTabs[index + 1]) + previous = this.loadedTabs[index + 1] + } + return tab.name !== name + }) + this.renderComponent() + if (previous) this.switchTab(previous.name) + } + + addHandler (type, fn) { + this.handlers[type] = fn + } + + setDispatch (dispatch) { + this.dispatch = dispatch + this.renderComponent() + } + + updateComponent(state) { + return + } + + renderComponent () { + const onSelect = (index) => { + if (this.loadedTabs[index]) { + const name = this.loadedTabs[index].name + if (this._handlers[name]) this._handlers[name].switchTo() + this.emit('tabCountChanged', this.loadedTabs.length) + } } - renderComponent () { - const onSelect = (index) => { - if (this.loadedTabs[index]) { - const name = this.loadedTabs[index].name - if (this._handlers[name]) this._handlers[name].switchTo() - this.emit('tabCountChanged', this.loadedTabs.length) - } - } - - const onClose = (index) => { - if (this.loadedTabs[index]) { - const name = this.loadedTabs[index].name - if (this._handlers[name]) this._handlers[name].close() - this.emit('tabCountChanged', this.loadedTabs.length) - } - } - - const onZoomIn = () => this.editor.editorFontSize(1) - const onZoomOut = () => this.editor.editorFontSize(-1) - - const onReady = (api) => { this.tabsApi = api } - - this.dispatch({ - plugin: this, - loadedTabs: this.loadedTabs, - onSelect, - onClose, - onZoomIn, - onZoomOut, - onReady, - themeQuality: this.themeQuality - }) + const onClose = (index) => { + if (this.loadedTabs[index]) { + const name = this.loadedTabs[index].name + if (this._handlers[name]) this._handlers[name].close() + this.emit('tabCountChanged', this.loadedTabs.length) + } } - renderTabsbar () { - return
- } + const onZoomIn = () => this.editor.editorFontSize(1) + const onZoomOut = () => this.editor.editorFontSize(-1) + + const onReady = (api) => { this.tabsApi = api } + + this.dispatch({ + plugin: this, + loadedTabs: this.loadedTabs, + onSelect, + onClose, + onZoomIn, + onZoomOut, + onReady, + themeQuality: this.themeQuality + }) + } + + renderTabsbar () { + return
+ } } diff --git a/apps/remix-ide/src/app/panels/terminal.js b/apps/remix-ide/src/app/panels/terminal.js index 83620b9264..93f283aec7 100644 --- a/apps/remix-ide/src/app/panels/terminal.js +++ b/apps/remix-ide/src/app/panels/terminal.js @@ -16,122 +16,122 @@ const KONSOLES = [] function register (api) { KONSOLES.push(api) } const profile = { - displayName: 'Terminal', - name: 'terminal', - methods: ['log', 'logHtml'], - events: [], - description: 'Remix IDE terminal', - version: packageJson.version + displayName: 'Terminal', + name: 'terminal', + methods: ['log', 'logHtml'], + events: [], + description: 'Remix IDE terminal', + version: packageJson.version } class Terminal extends Plugin { - constructor (opts, api) { - super(profile) - this.fileImport = new CompilerImports() - this.event = new EventManager() - this.globalRegistry = Registry.getInstance() - this.element = document.createElement('div') - this.element.setAttribute('class', 'panel') - this.element.setAttribute('id', 'terminal-view') - this.element.setAttribute('data-id', 'terminalContainer-view') - this.eventsDecoder = this.globalRegistry.get('eventsDecoder').api - this.txListener = this.globalRegistry.get('txlistener').api - this._deps = { - fileManager: this.globalRegistry.get('filemanager').api, - editor: this.globalRegistry.get('editor').api, - compilersArtefacts: this.globalRegistry.get('compilersartefacts').api, - offsetToLineColumnConverter: this.globalRegistry.get('offsettolinecolumnconverter').api - } - this.commandHelp = { - 'remix.loadgist(id)': 'Load a gist in the file explorer.', - 'remix.loadurl(url)': 'Load the given url in the file explorer. The url can be of type github, swarm, ipfs or raw http', - 'remix.execute(filepath)': 'Run the script specified by file path. If filepath is empty, script currently displayed in the editor is executed.', - 'remix.exeCurrent()': 'Run the script currently displayed in the editor', - 'remix.help()': 'Display this help message' - } - this.blockchain = opts.blockchain - this.vm = vm - this._api = api - this._opts = opts - this.config = this.globalRegistry.get('config').api - this.version = packageJson.version - this.data = { - lineLength: opts.lineLength || 80, // ???? - session: [], - activeFilters: { commands: {}, input: '' }, - filterFns: {} - } - this._view = { el: null, bar: null, input: null, term: null, journal: null, cli: null } - this._components = {} - this._commands = {} - this.commands = {} - this._JOURNAL = [] - this._jobs = [] - this._INDEX = {} - this._INDEX.all = [] - this._INDEX.allMain = [] - this._INDEX.commands = {} - this._INDEX.commandsMain = {} - if (opts.shell) this._shell = opts.shell // ??? - register(this) - this.event.register('debuggingRequested', async (hash) => { - // TODO should probably be in the run module - if (!await this._opts.appManager.isActive('debugger')) await this._opts.appManager.activatePlugin('debugger') - this.call('menuicons', 'select', 'debugger') - this.call('debugger', 'debug', hash) - }) - this.dispatch = null - - } - - - onActivation() { - this.renderComponent() - } - - onDeactivation () { - this.off('scriptRunner', 'log') - this.off('scriptRunner', 'info') - this.off('scriptRunner', 'warn') - this.off('scriptRunner', 'error') + constructor (opts, api) { + super(profile) + this.fileImport = new CompilerImports() + this.event = new EventManager() + this.globalRegistry = Registry.getInstance() + this.element = document.createElement('div') + this.element.setAttribute('class', 'panel') + this.element.setAttribute('id', 'terminal-view') + this.element.setAttribute('data-id', 'terminalContainer-view') + this.eventsDecoder = this.globalRegistry.get('eventsDecoder').api + this.txListener = this.globalRegistry.get('txlistener').api + this._deps = { + fileManager: this.globalRegistry.get('filemanager').api, + editor: this.globalRegistry.get('editor').api, + compilersArtefacts: this.globalRegistry.get('compilersartefacts').api, + offsetToLineColumnConverter: this.globalRegistry.get('offsettolinecolumnconverter').api } - - logHtml (html) { - this.terminalApi.logHtml(html) + this.commandHelp = { + 'remix.loadgist(id)': 'Load a gist in the file explorer.', + 'remix.loadurl(url)': 'Load the given url in the file explorer. The url can be of type github, swarm, ipfs or raw http', + 'remix.execute(filepath)': 'Run the script specified by file path. If filepath is empty, script currently displayed in the editor is executed.', + 'remix.exeCurrent()': 'Run the script currently displayed in the editor', + 'remix.help()': 'Display this help message' } - - log (message, type) { - this.terminalApi.log(message, type) - } - - setDispatch(dispatch) { - this.dispatch = dispatch - } - - render () { - return
- } - - updateComponent(state) { - return - } - - renderComponent () { - const onReady = (api) => { this.terminalApi = api } - this.dispatch({ - plugin: this, - onReady: onReady - }) + this.blockchain = opts.blockchain + this.vm = vm + this._api = api + this._opts = opts + this.config = this.globalRegistry.get('config').api + this.version = packageJson.version + this.data = { + lineLength: opts.lineLength || 80, // ???? + session: [], + activeFilters: { commands: {}, input: '' }, + filterFns: {} } + this._view = { el: null, bar: null, input: null, term: null, journal: null, cli: null } + this._components = {} + this._commands = {} + this.commands = {} + this._JOURNAL = [] + this._jobs = [] + this._INDEX = {} + this._INDEX.all = [] + this._INDEX.allMain = [] + this._INDEX.commands = {} + this._INDEX.commandsMain = {} + if (opts.shell) this._shell = opts.shell // ??? + register(this) + this.event.register('debuggingRequested', async (hash) => { + // TODO should probably be in the run module + if (!await this._opts.appManager.isActive('debugger')) await this._opts.appManager.activatePlugin('debugger') + this.call('menuicons', 'select', 'debugger') + this.call('debugger', 'debug', hash) + }) + this.dispatch = null + + } + - scroll2bottom () { - setTimeout(function () { - // do nothing. - }, 0) - } + onActivation() { + this.renderComponent() + } + + onDeactivation () { + this.off('scriptRunner', 'log') + this.off('scriptRunner', 'info') + this.off('scriptRunner', 'warn') + this.off('scriptRunner', 'error') + } + + logHtml (html) { + this.terminalApi.logHtml(html) + } + + log (message, type) { + this.terminalApi.log(message, type) + } + + setDispatch(dispatch) { + this.dispatch = dispatch + } + + render () { + return
+ } + + updateComponent(state) { + return + } + + renderComponent () { + const onReady = (api) => { this.terminalApi = api } + this.dispatch({ + plugin: this, + onReady: onReady + }) + } + + scroll2bottom () { + setTimeout(function () { + // do nothing. + }, 0) + } } module.exports = Terminal diff --git a/apps/remix-ide/src/app/plugins/code-format.ts b/apps/remix-ide/src/app/plugins/code-format.ts index 04266c1615..d56f842bd8 100644 --- a/apps/remix-ide/src/app/plugins/code-format.ts +++ b/apps/remix-ide/src/app/plugins/code-format.ts @@ -8,279 +8,279 @@ import toml from 'toml' import { filePathFilter, AnyFilter } from '@jsdevtools/file-path-filter' const profile = { - name: 'codeFormatter', - desciption: 'prettier plugin for Remix', - methods: ['format'], - events: [''], - version: '0.0.1' + name: 'codeFormatter', + desciption: 'prettier plugin for Remix', + methods: ['format'], + events: [''], + version: '0.0.1' } const defaultOptions = { - "overrides": [ - { - "files": "*.sol", - "options": { - "printWidth": 80, - "tabWidth": 4, - "useTabs": false, - "singleQuote": false, - "bracketSpacing": false, - } - }, - { - "files": "*.yml", - "options": { - } - }, - { - "files": "*.yaml", - "options": { - } - }, - { - "files": "*.toml", - "options": { - } - }, - { - "files": "*.json", - "options": { - } - }, - { - "files": "*.js", - "options": { - } - }, - { - "files": "*.ts", - "options": { - } - } - ] + "overrides": [ + { + "files": "*.sol", + "options": { + "printWidth": 80, + "tabWidth": 4, + "useTabs": false, + "singleQuote": false, + "bracketSpacing": false, + } + }, + { + "files": "*.yml", + "options": { + } + }, + { + "files": "*.yaml", + "options": { + } + }, + { + "files": "*.toml", + "options": { + } + }, + { + "files": "*.json", + "options": { + } + }, + { + "files": "*.js", + "options": { + } + }, + { + "files": "*.ts", + "options": { + } + } + ] } export class CodeFormat extends Plugin { - prettier: any - ts: any - babel: any - espree: any - yml: any - sol: any + prettier: any + ts: any + babel: any + espree: any + yml: any + sol: any - constructor() { - super(profile) - } + constructor() { + super(profile) + } - async format(file: string) { + async format(file: string) { - // lazy load - if (!this.prettier) { - this.prettier = await import('prettier/standalone') - this.ts = await import('prettier/parser-typescript') - this.babel = await import('prettier/parser-babel') - this.espree = await import('prettier/parser-espree') - this.yml = await import('prettier/parser-yaml') - } + // lazy load + if (!this.prettier) { + this.prettier = await import('prettier/standalone') + this.ts = await import('prettier/parser-typescript') + this.babel = await import('prettier/parser-babel') + this.espree = await import('prettier/parser-espree') + this.yml = await import('prettier/parser-yaml') + } - try { - const content = await this.call('fileManager', 'readFile', file) - if (!content) return - let parserName = '' - let options: Options = { - } - switch (path.extname(file)) { - case '.sol': - parserName = 'solidity-parse' - break - case '.ts': - parserName = 'typescript' - options = { - ...options, - trailingComma: 'all', - semi: false, - singleQuote: true, - quoteProps: 'as-needed', - bracketSpacing: true, - arrowParens: 'always', - } - break - case '.js': - parserName = "espree" - options = { - ...options, - semi: false, - singleQuote: true, - } - break - case '.json': - parserName = 'json' - break - case '.yml': - parserName = 'yaml' - break - case '.yaml': - parserName = 'yaml' - break - } + try { + const content = await this.call('fileManager', 'readFile', file) + if (!content) return + let parserName = '' + let options: Options = { + } + switch (path.extname(file)) { + case '.sol': + parserName = 'solidity-parse' + break + case '.ts': + parserName = 'typescript' + options = { + ...options, + trailingComma: 'all', + semi: false, + singleQuote: true, + quoteProps: 'as-needed', + bracketSpacing: true, + arrowParens: 'always', + } + break + case '.js': + parserName = "espree" + options = { + ...options, + semi: false, + singleQuote: true, + } + break + case '.json': + parserName = 'json' + break + case '.yml': + parserName = 'yaml' + break + case '.yaml': + parserName = 'yaml' + break + } - if (file === '.prettierrc') { - parserName = 'json' - } + if (file === '.prettierrc') { + parserName = 'json' + } - const possibleFileNames = [ - '.prettierrc', - '.prettierrc.json', - '.prettierrc.yaml', - '.prettierrc.yml', - '.prettierrc.toml', - '.prettierrc.js', - '.prettierrc.cjs', - 'prettier.config.js', - 'prettier.config.cjs', - '.prettierrc.json5', - ] + const possibleFileNames = [ + '.prettierrc', + '.prettierrc.json', + '.prettierrc.yaml', + '.prettierrc.yml', + '.prettierrc.toml', + '.prettierrc.js', + '.prettierrc.cjs', + 'prettier.config.js', + 'prettier.config.cjs', + '.prettierrc.json5', + ] - const prettierConfigFile = await findAsync(possibleFileNames, async (fileName) => { - const exists = await this.call('fileManager', 'exists', fileName) - return exists - }) + const prettierConfigFile = await findAsync(possibleFileNames, async (fileName) => { + const exists = await this.call('fileManager', 'exists', fileName) + return exists + }) - let parsed = null - if (prettierConfigFile) { - let prettierConfig = await this.call('fileManager', 'readFile', prettierConfigFile) - if (prettierConfig) { - if (prettierConfigFile.endsWith('.yaml') || prettierConfigFile.endsWith('.yml')) { - try { - parsed = yaml.load(prettierConfig) - } catch (e) { - // do nothing - } - } else if (prettierConfigFile.endsWith('.toml')) { - try { - parsed = toml.parse(prettierConfig) - } catch (e) { - // do nothing - } - } else if (prettierConfigFile.endsWith('.json') || prettierConfigFile.endsWith('.json5')) { - try { - parsed = JSON.parse(prettierConfig) - } catch (e) { - // do nothing - } - } else if (prettierConfigFile === '.prettierrc') { - try { - parsed = JSON.parse(prettierConfig) - } catch (e) { - // do nothing - } - if (!parsed) { - try { - parsed = yaml.load(prettierConfig) - } catch (e) { - // do nothing - } - } - } else if (prettierConfigFile.endsWith('.js') || prettierConfigFile.endsWith('.cjs')) { - // remove any comments - prettierConfig = prettierConfig.replace(/\/\*[\s\S]*?\*\/|([^\\:]|^)\/\/.*$/gm, '') - // add quotes to keys - prettierConfig = prettierConfig.replace(/([a-zA-Z0-9_]+)(\s*):/g, '"$1"$2:') - // remove comma from last key - prettierConfig = prettierConfig.replace(/,(\s*})/g, '$1') - // remove any semi-colons - prettierConfig = prettierConfig.replace(/;/g, '') - // convert single quotes to double quotes - prettierConfig = prettierConfig.replace(/'/g, '"') - try { - parsed = JSON.parse(prettierConfig.replace('module.exports = ', '').replace('module.exports=', '')) - } catch (e) { - // do nothing - } - } - } - } else { - parsed = defaultOptions - await this.call('fileManager', 'writeFile', '.prettierrc.json', JSON.stringify(parsed, null, 2)) - await this.call('notification', 'toast', 'A prettier config file has been created in the workspace.') + let parsed = null + if (prettierConfigFile) { + let prettierConfig = await this.call('fileManager', 'readFile', prettierConfigFile) + if (prettierConfig) { + if (prettierConfigFile.endsWith('.yaml') || prettierConfigFile.endsWith('.yml')) { + try { + parsed = yaml.load(prettierConfig) + } catch (e) { + // do nothing } - - if (!parsed && prettierConfigFile) { - this.call('notification', 'toast', `Error parsing prettier config file: ${prettierConfigFile}`) + } else if (prettierConfigFile.endsWith('.toml')) { + try { + parsed = toml.parse(prettierConfig) + } catch (e) { + // do nothing + } + } else if (prettierConfigFile.endsWith('.json') || prettierConfigFile.endsWith('.json5')) { + try { + parsed = JSON.parse(prettierConfig) + } catch (e) { + // do nothing + } + } else if (prettierConfigFile === '.prettierrc') { + try { + parsed = JSON.parse(prettierConfig) + } catch (e) { + // do nothing + } + if (!parsed) { + try { + parsed = yaml.load(prettierConfig) + } catch (e) { + // do nothing + } + } + } else if (prettierConfigFile.endsWith('.js') || prettierConfigFile.endsWith('.cjs')) { + // remove any comments + prettierConfig = prettierConfig.replace(/\/\*[\s\S]*?\*\/|([^\\:]|^)\/\/.*$/gm, '') + // add quotes to keys + prettierConfig = prettierConfig.replace(/([a-zA-Z0-9_]+)(\s*):/g, '"$1"$2:') + // remove comma from last key + prettierConfig = prettierConfig.replace(/,(\s*})/g, '$1') + // remove any semi-colons + prettierConfig = prettierConfig.replace(/;/g, '') + // convert single quotes to double quotes + prettierConfig = prettierConfig.replace(/'/g, '"') + try { + parsed = JSON.parse(prettierConfig.replace('module.exports = ', '').replace('module.exports=', '')) + } catch (e) { + // do nothing } + } + } + } else { + parsed = defaultOptions + await this.call('fileManager', 'writeFile', '.prettierrc.json', JSON.stringify(parsed, null, 2)) + await this.call('notification', 'toast', 'A prettier config file has been created in the workspace.') + } + if (!parsed && prettierConfigFile) { + this.call('notification', 'toast', `Error parsing prettier config file: ${prettierConfigFile}`) + } - // merge options - if (parsed) { - options = { - ...options, - ...parsed, - } - } - // search for overrides - if (parsed && parsed.overrides) { - const override = parsed.overrides.find((override) => { - if (override.files) { - const pathFilter: AnyFilter = {} - pathFilter.include = setGlobalExpression(override.files) - const filteredFiles = [file] - .filter(filePathFilter(pathFilter)) - if (filteredFiles.length) { - return true - } - } - }) - const validParsers = ['typescript', 'babel', 'espree', 'solidity-parse', 'json', 'yaml', 'solidity-parse'] - if (override && override.options && override.options.parser) { - if (validParsers.includes(override.options.parser)) { - parserName = override.options.parser - } else { - this.call('notification', 'toast', `Invalid parser: ${override.options.parser}! Valid options are ${validParsers.join(', ')}`) - } - delete override.options.parser - } + // merge options + if (parsed) { + options = { + ...options, + ...parsed, + } + } - if (override) { - options = { - ...options, - ...override.options, - } - } + // search for overrides + if (parsed && parsed.overrides) { + const override = parsed.overrides.find((override) => { + if (override.files) { + const pathFilter: AnyFilter = {} + pathFilter.include = setGlobalExpression(override.files) + const filteredFiles = [file] + .filter(filePathFilter(pathFilter)) + if (filteredFiles.length) { + return true } + } + }) + const validParsers = ['typescript', 'babel', 'espree', 'solidity-parse', 'json', 'yaml', 'solidity-parse'] + if (override && override.options && override.options.parser) { + if (validParsers.includes(override.options.parser)) { + parserName = override.options.parser + } else { + this.call('notification', 'toast', `Invalid parser: ${override.options.parser}! Valid options are ${validParsers.join(', ')}`) + } + delete override.options.parser + } - - const result = this.prettier.format(content, { - plugins: [sol as any, this.ts, this.babel, this.espree, this.yml], - parser: parserName, - ...options - }) - await this.call('fileManager', 'writeFile', file, result) - } catch (e) { - // do nothing + if (override) { + options = { + ...options, + ...override.options, + } } + } + + + const result = this.prettier.format(content, { + plugins: [sol as any, this.ts, this.babel, this.espree, this.yml], + parser: parserName, + ...options + }) + await this.call('fileManager', 'writeFile', file, result) + } catch (e) { + // do nothing } + } } //*.sol, **/*.txt, contracts/* const setGlobalExpression = (paths: string) => { - const results = [] - paths.split(',').forEach(path => { - path = path.trim() - if (path.startsWith('*.')) path = path.replace(/(\*\.)/g, '**/*.') - if (path.endsWith('/*') && !path.endsWith('/**/*')) - path = path.replace(/(\*)/g, '**/*.*') - results.push(path) - }) - return results + const results = [] + paths.split(',').forEach(path => { + path = path.trim() + if (path.startsWith('*.')) path = path.replace(/(\*\.)/g, '**/*.') + if (path.endsWith('/*') && !path.endsWith('/**/*')) + path = path.replace(/(\*)/g, '**/*.*') + results.push(path) + }) + return results } async function findAsync(arr, asyncCallback) { - const promises = arr.map(asyncCallback); - const results = await Promise.all(promises); - const index = results.findIndex(result => result); - return arr[index]; + const promises = arr.map(asyncCallback); + const results = await Promise.all(promises); + const index = results.findIndex(result => result); + return arr[index]; } diff --git a/apps/remix-ide/src/app/plugins/code-format/index.ts b/apps/remix-ide/src/app/plugins/code-format/index.ts index 32f785574c..fa2c32ce22 100644 --- a/apps/remix-ide/src/app/plugins/code-format/index.ts +++ b/apps/remix-ide/src/app/plugins/code-format/index.ts @@ -8,54 +8,54 @@ import { parse } from './parser' // https://prettier.io/docs/en/plugins.html#languages // https://github.com/ikatyang/linguist-languages/blob/master/data/Solidity.json const languages = [ - { - linguistLanguageId: 237469032, - name: 'Solidity', - type: 'programming', - color: '#AA6746', - aceMode: 'text', - tmScope: 'source.solidity', - extensions: ['.sol'], - parsers: ['solidity-parse'], - vscodeLanguageIds: ['solidity'] - } + { + linguistLanguageId: 237469032, + name: 'Solidity', + type: 'programming', + color: '#AA6746', + aceMode: 'text', + tmScope: 'source.solidity', + extensions: ['.sol'], + parsers: ['solidity-parse'], + vscodeLanguageIds: ['solidity'] + } ]; // https://prettier.io/docs/en/plugins.html#parsers const parser = { astFormat: 'solidity-ast', parse, ...loc }; const parsers = { - 'solidity-parse': parser + 'solidity-parse': parser }; const canAttachComment = (node) => - node.type && node.type !== 'BlockComment' && node.type !== 'LineComment'; + node.type && node.type !== 'BlockComment' && node.type !== 'LineComment'; // https://prettier.io/docs/en/plugins.html#printers const printers = { - 'solidity-ast': { - canAttachComment, - handleComments: { - ownLine: handleComments.handleOwnLineComment, - endOfLine: handleComments.handleEndOfLineComment, - remaining: handleComments.handleRemainingComment - }, - isBlockComment: handleComments.isBlockComment, - massageAstNode, - print, - printComment - } + 'solidity-ast': { + canAttachComment, + handleComments: { + ownLine: handleComments.handleOwnLineComment, + endOfLine: handleComments.handleEndOfLineComment, + remaining: handleComments.handleRemainingComment + }, + isBlockComment: handleComments.isBlockComment, + massageAstNode, + print, + printComment + } }; // https://prettier.io/docs/en/plugins.html#defaultoptions const defaultOptions = { - bracketSpacing: false, - tabWidth: 4 + bracketSpacing: false, + tabWidth: 4 }; export default { - languages, - parsers, - printers, - options, - defaultOptions + languages, + parsers, + printers, + options, + defaultOptions }; diff --git a/apps/remix-ide/src/app/plugins/code-format/parser.ts b/apps/remix-ide/src/app/plugins/code-format/parser.ts index 0fb913fed7..53f485d015 100644 --- a/apps/remix-ide/src/app/plugins/code-format/parser.ts +++ b/apps/remix-ide/src/app/plugins/code-format/parser.ts @@ -5,193 +5,193 @@ const parser = (window as any).SolidityParser import semver from 'semver'; const tryHug = (node, operators) => { - if (node.type === 'BinaryOperation' && operators.includes(node.operator)) - return { - type: 'TupleExpression', - components: [node], - isArray: false - }; - return node; + if (node.type === 'BinaryOperation' && operators.includes(node.operator)) + return { + type: 'TupleExpression', + components: [node], + isArray: false + }; + return node; }; export function parse(text, _parsers, options) { - const compiler = semver.coerce(options.compiler); - const parsed = parser.parse(text, { loc: true, range: true }); - parsed.comments = extractComments(text) + const compiler = semver.coerce(options.compiler); + const parsed = parser.parse(text, { loc: true, range: true }); + parsed.comments = extractComments(text) - parser.visit(parsed, { - PragmaDirective(ctx) { - // if the pragma is not for solidity we leave. - if (ctx.name !== 'solidity') return; - // if the compiler option has not been provided we leave. - if (!compiler) return; - // we make a check against each pragma directive in the document. - if (!semver.satisfies(compiler, ctx.value)) { - // @TODO: investigate the best way to warn that would apply to - // different editors. - // eslint-disable-next-line no-console - console.warn( - `[prettier-solidity] The compiler option is set to '${options.compiler}', which does not satisfy 'pragma solidity ${ctx.value}'.` - ); - } - }, - ModifierDefinition(ctx) { - if (!ctx.parameters) { - ctx.parameters = []; - } - }, - FunctionDefinition(ctx) { - if (!ctx.isConstructor) { - ctx.modifiers.forEach((modifier) => { - if (modifier.arguments && modifier.arguments.length === 0) { - // eslint-disable-next-line no-param-reassign - modifier.arguments = null; - } - }); - } - }, - ForStatement(ctx) { - if (ctx.initExpression) { - ctx.initExpression.omitSemicolon = true; - } - ctx.loopExpression.omitSemicolon = true; - }, - HexLiteral(ctx) { - ctx.value = options.singleQuote - ? `hex'${ctx.value.slice(4, -1)}'` - : `hex"${ctx.value.slice(4, -1)}"`; - }, - ElementaryTypeName(ctx) { - // if the compiler is below 0.8.0 we will recognize the type 'byte' as an - // alias of 'bytes1'. Otherwise we will ignore this and enforce always - // 'bytes1'. - const pre080 = compiler && semver.satisfies(compiler, '<0.8.0'); - if (!pre080 && ctx.name === 'byte') ctx.name = 'bytes1'; + parser.visit(parsed, { + PragmaDirective(ctx) { + // if the pragma is not for solidity we leave. + if (ctx.name !== 'solidity') return; + // if the compiler option has not been provided we leave. + if (!compiler) return; + // we make a check against each pragma directive in the document. + if (!semver.satisfies(compiler, ctx.value)) { + // @TODO: investigate the best way to warn that would apply to + // different editors. + // eslint-disable-next-line no-console + console.warn( + `[prettier-solidity] The compiler option is set to '${options.compiler}', which does not satisfy 'pragma solidity ${ctx.value}'.` + ); + } + }, + ModifierDefinition(ctx) { + if (!ctx.parameters) { + ctx.parameters = []; + } + }, + FunctionDefinition(ctx) { + if (!ctx.isConstructor) { + ctx.modifiers.forEach((modifier) => { + if (modifier.arguments && modifier.arguments.length === 0) { + // eslint-disable-next-line no-param-reassign + modifier.arguments = null; + } + }); + } + }, + ForStatement(ctx) { + if (ctx.initExpression) { + ctx.initExpression.omitSemicolon = true; + } + ctx.loopExpression.omitSemicolon = true; + }, + HexLiteral(ctx) { + ctx.value = options.singleQuote + ? `hex'${ctx.value.slice(4, -1)}'` + : `hex"${ctx.value.slice(4, -1)}"`; + }, + ElementaryTypeName(ctx) { + // if the compiler is below 0.8.0 we will recognize the type 'byte' as an + // alias of 'bytes1'. Otherwise we will ignore this and enforce always + // 'bytes1'. + const pre080 = compiler && semver.satisfies(compiler, '<0.8.0'); + if (!pre080 && ctx.name === 'byte') ctx.name = 'bytes1'; - if (options.explicitTypes === 'always') { - if (ctx.name === 'uint') ctx.name = 'uint256'; - if (ctx.name === 'int') ctx.name = 'int256'; - if (pre080 && ctx.name === 'byte') ctx.name = 'bytes1'; - } else if (options.explicitTypes === 'never') { - if (ctx.name === 'uint256') ctx.name = 'uint'; - if (ctx.name === 'int256') ctx.name = 'int'; - if (pre080 && ctx.name === 'bytes1') ctx.name = 'byte'; - } - }, - BinaryOperation(ctx) { - switch (ctx.operator) { - case '+': - case '-': - ctx.left = tryHug(ctx.left, ['%']); - ctx.right = tryHug(ctx.right, ['%']); - break; - case '*': - ctx.left = tryHug(ctx.left, ['/', '%']); - break; - case '/': - ctx.left = tryHug(ctx.left, ['*', '%']); - break; - case '%': - ctx.left = tryHug(ctx.left, ['*', '/', '%']); - break; - case '**': - // If the compiler has not been given as an option using we leave a**b**c. - if (!compiler) break; + if (options.explicitTypes === 'always') { + if (ctx.name === 'uint') ctx.name = 'uint256'; + if (ctx.name === 'int') ctx.name = 'int256'; + if (pre080 && ctx.name === 'byte') ctx.name = 'bytes1'; + } else if (options.explicitTypes === 'never') { + if (ctx.name === 'uint256') ctx.name = 'uint'; + if (ctx.name === 'int256') ctx.name = 'int'; + if (pre080 && ctx.name === 'bytes1') ctx.name = 'byte'; + } + }, + BinaryOperation(ctx) { + switch (ctx.operator) { + case '+': + case '-': + ctx.left = tryHug(ctx.left, ['%']); + ctx.right = tryHug(ctx.right, ['%']); + break; + case '*': + ctx.left = tryHug(ctx.left, ['/', '%']); + break; + case '/': + ctx.left = tryHug(ctx.left, ['*', '%']); + break; + case '%': + ctx.left = tryHug(ctx.left, ['*', '/', '%']); + break; + case '**': + // If the compiler has not been given as an option using we leave a**b**c. + if (!compiler) break; - if (semver.satisfies(compiler, '<0.8.0')) { - // If the compiler is less than 0.8.0 then a**b**c is formatted as - // (a**b)**c. - ctx.left = tryHug(ctx.left, ['**']); - break; - } - if ( - ctx.left.type === 'BinaryOperation' && + if (semver.satisfies(compiler, '<0.8.0')) { + // If the compiler is less than 0.8.0 then a**b**c is formatted as + // (a**b)**c. + ctx.left = tryHug(ctx.left, ['**']); + break; + } + if ( + ctx.left.type === 'BinaryOperation' && ctx.left.operator === '**' - ) { - // the parser still organizes the a**b**c as (a**b)**c so we need - // to restructure it. - ctx.right = { - type: 'TupleExpression', - components: [ - { - type: 'BinaryOperation', - operator: '**', - left: ctx.left.right, - right: ctx.right - } - ], - isArray: false - }; - ctx.left = ctx.left.left; - } - break; - case '<<': - case '>>': - ctx.left = tryHug(ctx.left, ['+', '-', '*', '/', '**', '<<', '>>']); - ctx.right = tryHug(ctx.right, ['+', '-', '*', '/', '**']); - break; - case '&': - ctx.left = tryHug(ctx.left, ['+', '-', '*', '/', '**', '<<', '>>']); - ctx.right = tryHug(ctx.right, ['+', '-', '*', '/', '**', '<<', '>>']); - break; - case '|': - ctx.left = tryHug(ctx.left, [ - '+', - '-', - '*', - '/', - '**', - '<<', - '>>', - '&', - '^' - ]); - ctx.right = tryHug(ctx.right, [ - '+', - '-', - '*', - '/', - '**', - '<<', - '>>', - '&', - '^' - ]); - break; - case '^': - ctx.left = tryHug(ctx.left, [ - '+', - '-', - '*', - '/', - '**', - '<<', - '>>', - '&' - ]); - ctx.right = tryHug(ctx.right, [ - '+', - '-', - '*', - '/', - '**', - '<<', - '>>', - '&' - ]); - break; - case '||': - ctx.left = tryHug(ctx.left, ['&&']); - ctx.right = tryHug(ctx.right, ['&&']); - break; - case '&&': - default: - break; - } + ) { + // the parser still organizes the a**b**c as (a**b)**c so we need + // to restructure it. + ctx.right = { + type: 'TupleExpression', + components: [ + { + type: 'BinaryOperation', + operator: '**', + left: ctx.left.right, + right: ctx.right + } + ], + isArray: false + }; + ctx.left = ctx.left.left; } - }); + break; + case '<<': + case '>>': + ctx.left = tryHug(ctx.left, ['+', '-', '*', '/', '**', '<<', '>>']); + ctx.right = tryHug(ctx.right, ['+', '-', '*', '/', '**']); + break; + case '&': + ctx.left = tryHug(ctx.left, ['+', '-', '*', '/', '**', '<<', '>>']); + ctx.right = tryHug(ctx.right, ['+', '-', '*', '/', '**', '<<', '>>']); + break; + case '|': + ctx.left = tryHug(ctx.left, [ + '+', + '-', + '*', + '/', + '**', + '<<', + '>>', + '&', + '^' + ]); + ctx.right = tryHug(ctx.right, [ + '+', + '-', + '*', + '/', + '**', + '<<', + '>>', + '&', + '^' + ]); + break; + case '^': + ctx.left = tryHug(ctx.left, [ + '+', + '-', + '*', + '/', + '**', + '<<', + '>>', + '&' + ]); + ctx.right = tryHug(ctx.right, [ + '+', + '-', + '*', + '/', + '**', + '<<', + '>>', + '&' + ]); + break; + case '||': + ctx.left = tryHug(ctx.left, ['&&']); + ctx.right = tryHug(ctx.right, ['&&']); + break; + case '&&': + default: + break; + } + } + }); - return parsed; + return parsed; } diff --git a/apps/remix-ide/src/app/plugins/config.ts b/apps/remix-ide/src/app/plugins/config.ts index b4ff5fe30b..eb1b7e6562 100644 --- a/apps/remix-ide/src/app/plugins/config.ts +++ b/apps/remix-ide/src/app/plugins/config.ts @@ -3,30 +3,30 @@ import { QueryParams } from '@remix-project/remix-lib' import Registry from '../state/registry' const profile = { - name: 'config', - displayName: 'Config', - description: 'Config', - methods: ['getAppParameter', 'setAppParameter'], - events: ['configChanged'] + name: 'config', + displayName: 'Config', + description: 'Config', + methods: ['getAppParameter', 'setAppParameter'], + events: ['configChanged'] } export class ConfigPlugin extends Plugin { - constructor () { - super(profile) - } + constructor () { + super(profile) + } - getAppParameter (name: string) { - const queryParams = new QueryParams() - const params = queryParams.get() - const config = Registry.getInstance().get('config').api - const param = params[name] || config.get(name) || config.get('settings/' + name) - if (param === 'true') return true - if (param === 'false') return false - return param - } + getAppParameter (name: string) { + const queryParams = new QueryParams() + const params = queryParams.get() + const config = Registry.getInstance().get('config').api + const param = params[name] || config.get(name) || config.get('settings/' + name) + if (param === 'true') return true + if (param === 'false') return false + return param + } - setAppParameter (name: string, value: any) { - const config = Registry.getInstance().get('config').api - config.set(name, value) - } + setAppParameter (name: string, value: any) { + const config = Registry.getInstance().get('config').api + config.set(name, value) + } } diff --git a/apps/remix-ide/src/app/plugins/contractFlattener.tsx b/apps/remix-ide/src/app/plugins/contractFlattener.tsx index 4f7cad9212..144777b166 100644 --- a/apps/remix-ide/src/app/plugins/contractFlattener.tsx +++ b/apps/remix-ide/src/app/plugins/contractFlattener.tsx @@ -6,74 +6,74 @@ import { concatSourceFiles, getDependencyGraph, normalizeContractPath } from '@r const _paq = window._paq = window._paq || [] const profile = { - name: 'contractflattener', - displayName: 'Contract Flattener', - description: 'Flatten solidity contracts', - methods: ['flattenAContract', 'flattenContract'], - events: [], - maintainedBy: 'Remix', + name: 'contractflattener', + displayName: 'Contract Flattener', + description: 'Flatten solidity contracts', + methods: ['flattenAContract', 'flattenContract'], + events: [], + maintainedBy: 'Remix', } export class ContractFlattener extends Plugin { - triggerFlattenContract: boolean = false - constructor() { - super(profile) - } + triggerFlattenContract: boolean = false + constructor() { + super(profile) + } - onActivation(): void { - this.on('solidity', 'compilationFinished', async (file, source, languageVersion, data, input, version) => { - if(data.sources && Object.keys(data.sources).length > 1) { - if(this.triggerFlattenContract) { - this.triggerFlattenContract = false - await this.flattenContract(source, file, data) - } - } - }) - _paq.push(['trackEvent', 'plugin', 'activated', 'contractFlattener']) - } + onActivation(): void { + this.on('solidity', 'compilationFinished', async (file, source, languageVersion, data, input, version) => { + if(data.sources && Object.keys(data.sources).length > 1) { + if(this.triggerFlattenContract) { + this.triggerFlattenContract = false + await this.flattenContract(source, file, data) + } + } + }) + _paq.push(['trackEvent', 'plugin', 'activated', 'contractFlattener']) + } - onDeactivation(): void { - this.off('solidity', 'compilationFinished') - } + onDeactivation(): void { + this.off('solidity', 'compilationFinished') + } - async flattenAContract(action: customAction) { - this.triggerFlattenContract = true - await this.call('solidity', 'compile', action.path[0]) - } + async flattenAContract(action: customAction) { + this.triggerFlattenContract = true + await this.call('solidity', 'compile', action.path[0]) + } - /** + /** * Takes currently compiled contract that has a bunch of imports at the top * and flattens them ready, ideally for UML creation or for other purposes. * Takes the flattened result, writes it to a file and returns the result. * @returns {Promise} */ - async flattenContract (source: { sources: any, target: string }, - filePath: string, data: { contracts: any, sources: any }): Promise { - const appendage = '_flattened.sol' - const normalized = normalizeContractPath(filePath) - const path = `${normalized[normalized.length - 2]}${appendage}` - const ast = data.sources - let dependencyGraph - let sorted - let result - let sources - try{ - dependencyGraph = getDependencyGraph(ast, filePath) - sorted = dependencyGraph.isEmpty() - ? [filePath] - : dependencyGraph.sort().reverse() - sources = source.sources - result = concatSourceFiles(sorted, sources) - }catch(err){ - console.warn(err) - } - await this.call('fileManager', 'writeFile', path , result) - _paq.push(['trackEvent', 'plugin', 'contractFlattener', 'flattenAContract']) - // clean up memory references & return result - sorted = null - sources = null - dependencyGraph = null - return result + async flattenContract (source: { sources: any, target: string }, + filePath: string, data: { contracts: any, sources: any }): Promise { + const appendage = '_flattened.sol' + const normalized = normalizeContractPath(filePath) + const path = `${normalized[normalized.length - 2]}${appendage}` + const ast = data.sources + let dependencyGraph + let sorted + let result + let sources + try{ + dependencyGraph = getDependencyGraph(ast, filePath) + sorted = dependencyGraph.isEmpty() + ? [filePath] + : dependencyGraph.sort().reverse() + sources = source.sources + result = concatSourceFiles(sorted, sources) + }catch(err){ + console.warn(err) } + await this.call('fileManager', 'writeFile', path , result) + _paq.push(['trackEvent', 'plugin', 'contractFlattener', 'flattenAContract']) + // clean up memory references & return result + sorted = null + sources = null + dependencyGraph = null + return result + } } \ No newline at end of file diff --git a/apps/remix-ide/src/app/plugins/file-decorator.ts b/apps/remix-ide/src/app/plugins/file-decorator.ts index 34829b027b..0f55c2551d 100644 --- a/apps/remix-ide/src/app/plugins/file-decorator.ts +++ b/apps/remix-ide/src/app/plugins/file-decorator.ts @@ -6,79 +6,79 @@ import { Plugin } from '@remixproject/engine' import { fileDecoration } from '@remix-ui/file-decorators' const profile = { - name: 'fileDecorator', - desciption: 'Keeps decorators of the files', - methods: ['setFileDecorators', 'clearFileDecorators', 'clearAllFileDecorators'], - events: ['fileDecoratorsChanged'], - version: '0.0.1' + name: 'fileDecorator', + desciption: 'Keeps decorators of the files', + methods: ['setFileDecorators', 'clearFileDecorators', 'clearAllFileDecorators'], + events: ['fileDecoratorsChanged'], + version: '0.0.1' } export class FileDecorator extends Plugin { - private _fileStates: fileDecoration[] = [] - constructor() { - super(profile) - } + private _fileStates: fileDecoration[] = [] + constructor() { + super(profile) + } - onActivation(): void { - this.on('filePanel', 'setWorkspace', async () => { - await this.clearAllFileDecorators() - }) - } + onActivation(): void { + this.on('filePanel', 'setWorkspace', async () => { + await this.clearAllFileDecorators() + }) + } - /** + /** * @param fileStates Array of file states */ - async setFileDecorators(fileStates: fileDecoration[] | fileDecoration) { - const { from } = this.currentRequest - const workspace = await this.call('filePanel', 'getCurrentWorkspace') - const fileStatesPayload = Array.isArray(fileStates) ? fileStates : [fileStates] - // clear all file states in the previous state of this owner on the files called - fileStatesPayload.forEach((state) => { - state.workspace = workspace - state.owner = from - }) - const filteredState = this._fileStates.filter((state) => { - const index = fileStatesPayload.findIndex((payloadFileState: fileDecoration) => { - return from == state.owner && payloadFileState.path == state.path - }) - return index == -1 - }) - const newState = [...filteredState, ...fileStatesPayload].sort(sortByPath) + async setFileDecorators(fileStates: fileDecoration[] | fileDecoration) { + const { from } = this.currentRequest + const workspace = await this.call('filePanel', 'getCurrentWorkspace') + const fileStatesPayload = Array.isArray(fileStates) ? fileStates : [fileStates] + // clear all file states in the previous state of this owner on the files called + fileStatesPayload.forEach((state) => { + state.workspace = workspace + state.owner = from + }) + const filteredState = this._fileStates.filter((state) => { + const index = fileStatesPayload.findIndex((payloadFileState: fileDecoration) => { + return from == state.owner && payloadFileState.path == state.path + }) + return index == -1 + }) + const newState = [...filteredState, ...fileStatesPayload].sort(sortByPath) - if (!deepequal(newState, this._fileStates)) { - this._fileStates = newState - this.emit('fileDecoratorsChanged', this._fileStates) - } + if (!deepequal(newState, this._fileStates)) { + this._fileStates = newState + this.emit('fileDecoratorsChanged', this._fileStates) } + } - async clearFileDecorators(path?: string) { - const { from } = this.currentRequest - if (!from) return + async clearFileDecorators(path?: string) { + const { from } = this.currentRequest + if (!from) return - const filteredState = this._fileStates.filter((state) => { - if(state.owner != from) return true - if(path && state.path != path) return true - }) - const newState = [...filteredState].sort(sortByPath) - - if (!deepequal(newState, this._fileStates)) { - this._fileStates = newState - this.emit('fileDecoratorsChanged', this._fileStates) - } + const filteredState = this._fileStates.filter((state) => { + if(state.owner != from) return true + if(path && state.path != path) return true + }) + const newState = [...filteredState].sort(sortByPath) + if (!deepequal(newState, this._fileStates)) { + this._fileStates = newState + this.emit('fileDecoratorsChanged', this._fileStates) } - async clearAllFileDecorators() { - this._fileStates = [] - this.emit('fileDecoratorsChanged', []) - } + } + + async clearAllFileDecorators() { + this._fileStates = [] + this.emit('fileDecoratorsChanged', []) + } } const sortByPath = (a: fileDecoration, b: fileDecoration) => { - if (a.path < b.path) { - return -1; - } - if (a.path > b.path) { - return 1; - } - return 0; + if (a.path < b.path) { + return -1; + } + if (a.path > b.path) { + return 1; + } + return 0; } \ No newline at end of file diff --git a/apps/remix-ide/src/app/plugins/notification.tsx b/apps/remix-ide/src/app/plugins/notification.tsx index 83754e8b38..dc3a367b37 100644 --- a/apps/remix-ide/src/app/plugins/notification.tsx +++ b/apps/remix-ide/src/app/plugins/notification.tsx @@ -14,31 +14,31 @@ interface INotificationApi { } const profile:LibraryProfile = { - name: 'notification', - displayName: 'Notification', - description: 'Displays notifications', - methods: ['modal', 'alert', 'toast'] + name: 'notification', + displayName: 'Notification', + description: 'Displays notifications', + methods: ['modal', 'alert', 'toast'] } export class NotificationPlugin extends Plugin implements MethodApi { - dispatcher: dispatchModalInterface - constructor () { - super(profile) - } + dispatcher: dispatchModalInterface + constructor () { + super(profile) + } - setDispatcher (dispatcher: dispatchModalInterface) { - this.dispatcher = dispatcher - } + setDispatcher (dispatcher: dispatchModalInterface) { + this.dispatcher = dispatcher + } - async modal (args: AppModal) { - return this.dispatcher.modal(args) - } + async modal (args: AppModal) { + return this.dispatcher.modal(args) + } - async alert (args: AlertModal) { - return this.dispatcher.alert(args) - } + async alert (args: AlertModal) { + return this.dispatcher.alert(args) + } - async toast (message: string | JSX.Element) { - this.dispatcher.toast(message) - } + async toast (message: string | JSX.Element) { + this.dispatcher.toast(message) + } } diff --git a/apps/remix-ide/src/app/plugins/parser/code-parser.tsx b/apps/remix-ide/src/app/plugins/parser/code-parser.tsx index b3e0955ece..99f8c9b82d 100644 --- a/apps/remix-ide/src/app/plugins/parser/code-parser.tsx +++ b/apps/remix-ide/src/app/plugins/parser/code-parser.tsx @@ -15,14 +15,14 @@ import { antlr } from './types' import { ParseResult } from './types/antlr-types' const profile: Profile = { - name: 'codeParser', - methods: ['nodesAtPosition', 'getContractNodes', 'getCurrentFileNodes', 'getLineColumnOfNode', 'getLineColumnOfPosition', 'getFunctionParamaters', 'getDeclaration', 'getFunctionReturnParameters', 'getVariableDeclaration', 'getNodeDocumentation', 'getNodeLink', 'listAstNodes', 'getANTLRBlockAtPosition', 'getLastNodeInLine', 'resolveImports', 'parseSolidity', 'getNodesWithScope', 'getNodesWithName', 'getNodes', 'compile', 'getNodeById', 'getLastCompilationResult', 'positionOfDefinition', 'definitionAtPosition', 'jumpToDefinition', 'referrencesAtPosition', 'referencesOf', 'getActiveHighlights', 'gasEstimation', 'declarationOf', 'getGasEstimates', 'getImports'], - events: [], - version: '0.0.1' + name: 'codeParser', + methods: ['nodesAtPosition', 'getContractNodes', 'getCurrentFileNodes', 'getLineColumnOfNode', 'getLineColumnOfPosition', 'getFunctionParamaters', 'getDeclaration', 'getFunctionReturnParameters', 'getVariableDeclaration', 'getNodeDocumentation', 'getNodeLink', 'listAstNodes', 'getANTLRBlockAtPosition', 'getLastNodeInLine', 'resolveImports', 'parseSolidity', 'getNodesWithScope', 'getNodesWithName', 'getNodes', 'compile', 'getNodeById', 'getLastCompilationResult', 'positionOfDefinition', 'definitionAtPosition', 'jumpToDefinition', 'referrencesAtPosition', 'referencesOf', 'getActiveHighlights', 'gasEstimation', 'declarationOf', 'getGasEstimates', 'getImports'], + events: [], + version: '0.0.1' } export function isNodeDefinition(node: genericASTNode) { - return node.nodeType === 'ContractDefinition' || + return node.nodeType === 'ContractDefinition' || node.nodeType === 'FunctionDefinition' || node.nodeType === 'ModifierDefinition' || node.nodeType === 'VariableDeclaration' || @@ -58,640 +58,640 @@ interface codeParserIndex { export class CodeParser extends Plugin { - compilerAbstract: CompilerAbstract - currentFile: string - nodeIndex: codeParserIndex - astWalker: any - errorState: boolean = false - - gastEstimateTimeOut: any - - gasService: CodeParserGasService - compilerService: CodeParserCompiler - antlrService: CodeParserAntlrService - importService: CodeParserImports - - parseSolidity: (text: string) => Promise - getLastNodeInLine: (ast: string) => Promise - listAstNodes: () => Promise - getANTLRBlockAtPosition: (position: any, text?: string) => Promise - setCurrentFileAST: (text?: string) => Promise - getImports: () => Promise - - debuggerIsOn: boolean = false - - - constructor(astWalker: any) { - super(profile) - this.astWalker = astWalker - this.nodeIndex = { - declarations: [[]], - flatReferences: [], - nodesPerFile: {} - } - } - - async handleChangeEvents() { - const completionSettings = await this.call('config', 'getAppParameter', 'auto-completion') - if (completionSettings) { - this.antlrService.enableWorker() - } else { - this.antlrService.disableWorker() - } - const showGasSettings = await this.call('config', 'getAppParameter', 'show-gas') - const showErrorSettings = await this.call('config', 'getAppParameter', 'display-errors') - if (showGasSettings || showErrorSettings || completionSettings || this.debuggerIsOn) { - await this.compilerService.compile() - } - } - - async onActivation() { - - this.gasService = new CodeParserGasService(this) - this.compilerService = new CodeParserCompiler(this) - this.antlrService = new CodeParserAntlrService(this) - this.importService = new CodeParserImports(this) - - this.parseSolidity = this.antlrService.parseSolidity.bind(this.antlrService) - this.getLastNodeInLine = this.antlrService.getLastNodeInLine.bind(this.antlrService) - this.listAstNodes = this.antlrService.listAstNodes.bind(this.antlrService) - this.getANTLRBlockAtPosition = this.antlrService.getANTLRBlockAtPosition.bind(this.antlrService) - this.setCurrentFileAST = this.antlrService.setCurrentFileAST.bind(this.antlrService) - this.getImports = this.importService.getImports.bind(this.importService) - - this.on('editor', 'didChangeFile', async (file) => { - await this.call('editor', 'discardLineTexts') - await this.handleChangeEvents() - }) - - this.on('filePanel', 'setWorkspace', async () => { - await this.call('fileDecorator', 'clearFileDecorators') - await this.importService.setFileTree() - }) - - this.on('fileManager', 'fileAdded', async () => { - await this.importService.setFileTree() - }) - this.on('fileManager', 'fileRemoved', async () => { - await this.importService.setFileTree() - }) - - this.on('fileManager', 'currentFileChanged', async () => { - await this.call('editor', 'discardLineTexts') - const completionSettings = await this.call('config', 'getAppParameter', 'auto-completion') - if (completionSettings) { - this.antlrService.setCurrentFileAST() - } - await this.handleChangeEvents() - }) - - this.on('solidity', 'loadingCompiler', async (url) => { - this.compilerService.compiler.loadVersion(true, `${url}?t=${Date.now()}`) - }) - - this.on('config', 'configChanged', async (config) => { - await this.reload() - }) - - this.on('settings', 'configChanged', async (config) => { - await this.reload() - }) - - await this.compilerService.init() - this.on('solidity', 'compilerLoaded', async () => { - await this.reload() - }) - - this.on('debugger', 'startDebugging', async () => { - this.debuggerIsOn = true - await this.reload() - }) - - this.on('debugger', 'stopDebugging', async () => { - this.debuggerIsOn = false - await this.reload() - }) - } - - async reload(){ - await this.call('editor', 'discardLineTexts') - await this.call('fileDecorator', 'clearFileDecorators') - await this.call('editor', 'clearErrorMarkers', [this.currentFile]) - await this.handleChangeEvents() - } - - /** + compilerAbstract: CompilerAbstract + currentFile: string + nodeIndex: codeParserIndex + astWalker: any + errorState: boolean = false + + gastEstimateTimeOut: any + + gasService: CodeParserGasService + compilerService: CodeParserCompiler + antlrService: CodeParserAntlrService + importService: CodeParserImports + + parseSolidity: (text: string) => Promise + getLastNodeInLine: (ast: string) => Promise + listAstNodes: () => Promise + getANTLRBlockAtPosition: (position: any, text?: string) => Promise + setCurrentFileAST: (text?: string) => Promise + getImports: () => Promise + + debuggerIsOn: boolean = false + + + constructor(astWalker: any) { + super(profile) + this.astWalker = astWalker + this.nodeIndex = { + declarations: [[]], + flatReferences: [], + nodesPerFile: {} + } + } + + async handleChangeEvents() { + const completionSettings = await this.call('config', 'getAppParameter', 'auto-completion') + if (completionSettings) { + this.antlrService.enableWorker() + } else { + this.antlrService.disableWorker() + } + const showGasSettings = await this.call('config', 'getAppParameter', 'show-gas') + const showErrorSettings = await this.call('config', 'getAppParameter', 'display-errors') + if (showGasSettings || showErrorSettings || completionSettings || this.debuggerIsOn) { + await this.compilerService.compile() + } + } + + async onActivation() { + + this.gasService = new CodeParserGasService(this) + this.compilerService = new CodeParserCompiler(this) + this.antlrService = new CodeParserAntlrService(this) + this.importService = new CodeParserImports(this) + + this.parseSolidity = this.antlrService.parseSolidity.bind(this.antlrService) + this.getLastNodeInLine = this.antlrService.getLastNodeInLine.bind(this.antlrService) + this.listAstNodes = this.antlrService.listAstNodes.bind(this.antlrService) + this.getANTLRBlockAtPosition = this.antlrService.getANTLRBlockAtPosition.bind(this.antlrService) + this.setCurrentFileAST = this.antlrService.setCurrentFileAST.bind(this.antlrService) + this.getImports = this.importService.getImports.bind(this.importService) + + this.on('editor', 'didChangeFile', async (file) => { + await this.call('editor', 'discardLineTexts') + await this.handleChangeEvents() + }) + + this.on('filePanel', 'setWorkspace', async () => { + await this.call('fileDecorator', 'clearFileDecorators') + await this.importService.setFileTree() + }) + + this.on('fileManager', 'fileAdded', async () => { + await this.importService.setFileTree() + }) + this.on('fileManager', 'fileRemoved', async () => { + await this.importService.setFileTree() + }) + + this.on('fileManager', 'currentFileChanged', async () => { + await this.call('editor', 'discardLineTexts') + const completionSettings = await this.call('config', 'getAppParameter', 'auto-completion') + if (completionSettings) { + this.antlrService.setCurrentFileAST() + } + await this.handleChangeEvents() + }) + + this.on('solidity', 'loadingCompiler', async (url) => { + this.compilerService.compiler.loadVersion(true, `${url}?t=${Date.now()}`) + }) + + this.on('config', 'configChanged', async (config) => { + await this.reload() + }) + + this.on('settings', 'configChanged', async (config) => { + await this.reload() + }) + + await this.compilerService.init() + this.on('solidity', 'compilerLoaded', async () => { + await this.reload() + }) + + this.on('debugger', 'startDebugging', async () => { + this.debuggerIsOn = true + await this.reload() + }) + + this.on('debugger', 'stopDebugging', async () => { + this.debuggerIsOn = false + await this.reload() + }) + } + + async reload(){ + await this.call('editor', 'discardLineTexts') + await this.call('fileDecorator', 'clearFileDecorators') + await this.call('editor', 'clearErrorMarkers', [this.currentFile]) + await this.handleChangeEvents() + } + + /** * * @returns */ - async getLastCompilationResult() { - return this.compilerAbstract - } + async getLastCompilationResult() { + return this.compilerAbstract + } - getSubNodes(node: T): number[] { - return node.nodeType == "ContractDefinition" && node.contractDependencies; - } + getSubNodes(node: T): number[] { + return node.nodeType == "ContractDefinition" && node.contractDependencies; + } - /** + /** * Builds a flat index and declarations of all the nodes in the compilation result * @param compilationResult * @param source */ - _buildIndex(compilationResult: CompilationResult, source) { - if (compilationResult && compilationResult.sources) { - const callback = (node: genericASTNode) => { - if (node && ("referencedDeclaration" in node) && node.referencedDeclaration) { - if (!this.nodeIndex.declarations[node.referencedDeclaration]) { - this.nodeIndex.declarations[node.referencedDeclaration] = [] - } - this.nodeIndex.declarations[node.referencedDeclaration].push(node) - } - this.nodeIndex.flatReferences[node.id] = node - } - for (const s in compilationResult.sources) { - this.astWalker.walkFull(compilationResult.sources[s].ast, callback) - } - + _buildIndex(compilationResult: CompilationResult, source) { + if (compilationResult && compilationResult.sources) { + const callback = (node: genericASTNode) => { + if (node && ("referencedDeclaration" in node) && node.referencedDeclaration) { + if (!this.nodeIndex.declarations[node.referencedDeclaration]) { + this.nodeIndex.declarations[node.referencedDeclaration] = [] + } + this.nodeIndex.declarations[node.referencedDeclaration].push(node) } + this.nodeIndex.flatReferences[node.id] = node + } + for (const s in compilationResult.sources) { + this.astWalker.walkFull(compilationResult.sources[s].ast, callback) + } } - // NODE HELPERS + } - _getInputParams(node: FunctionDefinitionAstNode) { - const params = [] - const target = node.parameters - if (target) { - const children = target.parameters - for (const j in children) { - if (children[j].nodeType === 'VariableDeclaration') { - params.push(children[j].typeDescriptions.typeString) - } - } + // NODE HELPERS + + _getInputParams(node: FunctionDefinitionAstNode) { + const params = [] + const target = node.parameters + if (target) { + const children = target.parameters + for (const j in children) { + if (children[j].nodeType === 'VariableDeclaration') { + params.push(children[j].typeDescriptions.typeString) } - return '(' + params.toString() + ')' + } } + return '(' + params.toString() + ')' + } - _flatNodeList(contractNode: ContractDefinitionAstNode, fileName: string, inScope: boolean, compilatioResult: any) { - const index = {} - const contractName: string = contractNode.name - const callback = (node) => { - if (inScope && node.scope !== contractNode.id + _flatNodeList(contractNode: ContractDefinitionAstNode, fileName: string, inScope: boolean, compilatioResult: any) { + const index = {} + const contractName: string = contractNode.name + const callback = (node) => { + if (inScope && node.scope !== contractNode.id && !(node.nodeType === 'EnumDefinition' || node.nodeType === 'EventDefinition' || node.nodeType === 'ModifierDefinition')) - return - if (inScope) node.isClassNode = true; - node.gasEstimate = this._getContractGasEstimate(node, contractName, fileName, compilatioResult) - node.functionName = node.name + this._getInputParams(node) - node.contractName = contractName - node.contractId = contractNode.id - index[node.id] = node - } - this.astWalker.walkFull(contractNode, callback) - return index - } - - _extractFileNodes(fileName: string, compilationResult: lastCompilationResult) { - if (compilationResult && compilationResult.data.sources && compilationResult.data.sources[fileName]) { - const source = compilationResult.data.sources[fileName] - const nodesByContract: any = {} - nodesByContract.imports = {} - nodesByContract.contracts = {} - this.astWalker.walkFull(source.ast, async (node) => { - if (node.nodeType === 'ContractDefinition') { - const flatNodes = this._flatNodeList(node, fileName, false, compilationResult) - node.gasEstimate = this._getContractGasEstimate(node, node.name, fileName, compilationResult) - nodesByContract.contracts[node.name] = { contractDefinition: node, contractNodes: flatNodes } - const baseNodes = {} - const baseNodesWithBaseContractScope = {} - if (node.linearizedBaseContracts) { - for (const id of node.linearizedBaseContracts) { - if (id !== node.id) { - const baseContract = await this.getNodeById(id) - const callback = (node) => { - node.contractName = (baseContract as any).name - node.contractId = (baseContract as any).id - node.isBaseNode = true; - baseNodes[node.id] = node - if ((node.scope && node.scope === baseContract.id) + return + if (inScope) node.isClassNode = true; + node.gasEstimate = this._getContractGasEstimate(node, contractName, fileName, compilatioResult) + node.functionName = node.name + this._getInputParams(node) + node.contractName = contractName + node.contractId = contractNode.id + index[node.id] = node + } + this.astWalker.walkFull(contractNode, callback) + return index + } + + _extractFileNodes(fileName: string, compilationResult: lastCompilationResult) { + if (compilationResult && compilationResult.data.sources && compilationResult.data.sources[fileName]) { + const source = compilationResult.data.sources[fileName] + const nodesByContract: any = {} + nodesByContract.imports = {} + nodesByContract.contracts = {} + this.astWalker.walkFull(source.ast, async (node) => { + if (node.nodeType === 'ContractDefinition') { + const flatNodes = this._flatNodeList(node, fileName, false, compilationResult) + node.gasEstimate = this._getContractGasEstimate(node, node.name, fileName, compilationResult) + nodesByContract.contracts[node.name] = { contractDefinition: node, contractNodes: flatNodes } + const baseNodes = {} + const baseNodesWithBaseContractScope = {} + if (node.linearizedBaseContracts) { + for (const id of node.linearizedBaseContracts) { + if (id !== node.id) { + const baseContract = await this.getNodeById(id) + const callback = (node) => { + node.contractName = (baseContract as any).name + node.contractId = (baseContract as any).id + node.isBaseNode = true; + baseNodes[node.id] = node + if ((node.scope && node.scope === baseContract.id) || node.nodeType === 'EnumDefinition' || node.nodeType === 'EventDefinition' - ) { - baseNodesWithBaseContractScope[node.id] = node - } - if (node.members) { - for (const member of node.members) { - member.contractName = (baseContract as any).name - member.contractId = (baseContract as any).id - member.isBaseNode = true; - } - } - } - this.astWalker.walkFull(baseContract, callback) - } - } + ) { + baseNodesWithBaseContractScope[node.id] = node + } + if (node.members) { + for (const member of node.members) { + member.contractName = (baseContract as any).name + member.contractId = (baseContract as any).id + member.isBaseNode = true; } - nodesByContract.contracts[node.name].baseNodes = baseNodes - nodesByContract.contracts[node.name].baseNodesWithBaseContractScope = baseNodesWithBaseContractScope - nodesByContract.contracts[node.name].contractScopeNodes = this._flatNodeList(node, fileName, true, compilationResult) + } } - if (node.nodeType === 'ImportDirective') { + this.astWalker.walkFull(baseContract, callback) + } + } + } + nodesByContract.contracts[node.name].baseNodes = baseNodes + nodesByContract.contracts[node.name].baseNodesWithBaseContractScope = baseNodesWithBaseContractScope + nodesByContract.contracts[node.name].contractScopeNodes = this._flatNodeList(node, fileName, true, compilationResult) + } + if (node.nodeType === 'ImportDirective') { - const imported = await this.resolveImports(node, {}) + const imported = await this.resolveImports(node, {}) - for (const importedNode of (Object.values(imported) as any)) { - if (importedNode.nodes) - for (const subNode of importedNode.nodes) { - nodesByContract.imports[subNode.id] = subNode - } - } - } - }) - return nodesByContract + for (const importedNode of (Object.values(imported) as any)) { + if (importedNode.nodes) + for (const subNode of importedNode.nodes) { + nodesByContract.imports[subNode.id] = subNode + } + } } - } - - _getContractGasEstimate(node: ContractDefinitionAstNode | FunctionDefinitionAstNode, contractName: string, fileName: string, compilationResult: lastCompilationResult) { - - const contracts = compilationResult.data.contracts && compilationResult.data.contracts[this.currentFile] - for (const name in contracts) { - if (name === contractName) { - const contract = contracts[name] - const estimationObj = contract.evm && contract.evm.gasEstimates - - let executionCost = null - if (node.nodeType === 'FunctionDefinition') { - const visibility = node.visibility - if (node.kind !== 'constructor') { - const fnName = node.name - const fn = fnName + this._getInputParams(node) - - if (visibility === 'public' || visibility === 'external') { - executionCost = estimationObj === null ? '-' : estimationObj.external[fn] - } else if (visibility === 'private' || visibility === 'internal') { - executionCost = estimationObj === null ? '-' : estimationObj.internal[fn] - } - return { executionCost } - } else { - return { - creationCost: estimationObj === null ? '-' : estimationObj.creation.totalCost, - codeDepositCost: estimationObj === null ? '-' : estimationObj.creation.codeDepositCost, - } - } - } + }) + return nodesByContract + } + } + + _getContractGasEstimate(node: ContractDefinitionAstNode | FunctionDefinitionAstNode, contractName: string, fileName: string, compilationResult: lastCompilationResult) { + + const contracts = compilationResult.data.contracts && compilationResult.data.contracts[this.currentFile] + for (const name in contracts) { + if (name === contractName) { + const contract = contracts[name] + const estimationObj = contract.evm && contract.evm.gasEstimates + + let executionCost = null + if (node.nodeType === 'FunctionDefinition') { + const visibility = node.visibility + if (node.kind !== 'constructor') { + const fnName = node.name + const fn = fnName + this._getInputParams(node) + + if (visibility === 'public' || visibility === 'external') { + executionCost = estimationObj === null ? '-' : estimationObj.external[fn] + } else if (visibility === 'private' || visibility === 'internal') { + executionCost = estimationObj === null ? '-' : estimationObj.internal[fn] + } + return { executionCost } + } else { + return { + creationCost: estimationObj === null ? '-' : estimationObj.creation.totalCost, + codeDepositCost: estimationObj === null ? '-' : estimationObj.creation.codeDepositCost, } + } } + } } + } - /** + /** * Nodes at position where position is a number, offset * @param position * @param type * @returns */ - async nodesAtPosition(position: number, type = ''): Promise { - let lastCompilationResult = this.compilerAbstract - if(this.debuggerIsOn) { - lastCompilationResult = await this.call('compilerArtefacts', 'get', '__last') - this.currentFile = await this.call('fileManager', 'file') - } - if (!lastCompilationResult) return [] - const urlFromPath = await this.call('fileManager', 'getUrlFromPath', this.currentFile) - if (lastCompilationResult && lastCompilationResult.languageversion.indexOf('soljson') === 0 && lastCompilationResult.data && lastCompilationResult.data.sources && lastCompilationResult.data.sources[this.currentFile]) { - const nodes: genericASTNode[] = sourceMappingDecoder.nodesAtPosition(type, position, lastCompilationResult.data.sources[this.currentFile] || lastCompilationResult.data.sources[urlFromPath.file]) - return nodes - } - return [] - } - - /** + async nodesAtPosition(position: number, type = ''): Promise { + let lastCompilationResult = this.compilerAbstract + if(this.debuggerIsOn) { + lastCompilationResult = await this.call('compilerArtefacts', 'get', '__last') + this.currentFile = await this.call('fileManager', 'file') + } + if (!lastCompilationResult) return [] + const urlFromPath = await this.call('fileManager', 'getUrlFromPath', this.currentFile) + if (lastCompilationResult && lastCompilationResult.languageversion.indexOf('soljson') === 0 && lastCompilationResult.data && lastCompilationResult.data.sources && lastCompilationResult.data.sources[this.currentFile]) { + const nodes: genericASTNode[] = sourceMappingDecoder.nodesAtPosition(type, position, lastCompilationResult.data.sources[this.currentFile] || lastCompilationResult.data.sources[urlFromPath.file]) + return nodes + } + return [] + } + + /** * * @param id * @returns */ - async getNodeById(id: number) { - for (const key in this.nodeIndex.flatReferences) { - if (this.nodeIndex.flatReferences[key].id === id) { - return this.nodeIndex.flatReferences[key] - } - } + async getNodeById(id: number) { + for (const key in this.nodeIndex.flatReferences) { + if (this.nodeIndex.flatReferences[key].id === id) { + return this.nodeIndex.flatReferences[key] + } } + } - /** + /** * * @param id * @returns */ - async getDeclaration(id: number) { - if (this.nodeIndex.declarations && this.nodeIndex.declarations[id]) return this.nodeIndex.declarations[id] - } + async getDeclaration(id: number) { + if (this.nodeIndex.declarations && this.nodeIndex.declarations[id]) return this.nodeIndex.declarations[id] + } - /** + /** * * @param scope * @returns */ - async getNodesWithScope(scope: number) { - const nodes = [] - for (const node of Object.values(this.nodeIndex.flatReferences)) { - if (node.scope === scope) nodes.push(node) - } - return nodes + async getNodesWithScope(scope: number) { + const nodes = [] + for (const node of Object.values(this.nodeIndex.flatReferences)) { + if (node.scope === scope) nodes.push(node) } + return nodes + } - /** + /** * * @param name * @returns */ - async getNodesWithName(name: string) { - const nodes: genericASTNode[] = [] - for (const node of Object.values(this.nodeIndex.flatReferences)) { - if (node.name === name) nodes.push(node) - } - return nodes - } - /** + async getNodesWithName(name: string) { + const nodes: genericASTNode[] = [] + for (const node of Object.values(this.nodeIndex.flatReferences)) { + if (node.name === name) nodes.push(node) + } + return nodes + } + /** * * @param node * @returns */ - declarationOf(node: T) { - if (node && ('referencedDeclaration' in node) && node.referencedDeclaration) { - return this.nodeIndex.flatReferences[node.referencedDeclaration] - } - return null + declarationOf(node: T) { + if (node && ('referencedDeclaration' in node) && node.referencedDeclaration) { + return this.nodeIndex.flatReferences[node.referencedDeclaration] } + return null + } - /** + /** * * @param position * @returns */ - async definitionAtPosition(position: number) { - const nodes = await this.nodesAtPosition(position) - let nodeDefinition: any - let node: genericASTNode - if (nodes && nodes.length && !this.errorState) { - node = nodes[nodes.length - 1] - nodeDefinition = node - if (!isNodeDefinition(node)) { - nodeDefinition = await this.declarationOf(node) || node - } - if (node.nodeType === 'ImportDirective') { - for (const key in this.nodeIndex.flatReferences) { - if (this.nodeIndex.flatReferences[key].id === node.sourceUnit) { - nodeDefinition = this.nodeIndex.flatReferences[key] - } - } - } - return nodeDefinition - } else { - const astNodes = await this.antlrService.listAstNodes() - if (astNodes && astNodes.length) { - for (const node of astNodes) { - if (node.range[0] <= position && node.range[1] >= position) { - if (nodeDefinition && nodeDefinition.range[0] < node.range[0]) { - nodeDefinition = node - } - if (!nodeDefinition) nodeDefinition = node - } - } - if (nodeDefinition && nodeDefinition.type && nodeDefinition.type === 'Identifier') { - const nodeForIdentifier = await this.findIdentifier(nodeDefinition) - if (nodeForIdentifier) nodeDefinition = nodeForIdentifier - } - return nodeDefinition + async definitionAtPosition(position: number) { + const nodes = await this.nodesAtPosition(position) + let nodeDefinition: any + let node: genericASTNode + if (nodes && nodes.length && !this.errorState) { + node = nodes[nodes.length - 1] + nodeDefinition = node + if (!isNodeDefinition(node)) { + nodeDefinition = await this.declarationOf(node) || node + } + if (node.nodeType === 'ImportDirective') { + for (const key in this.nodeIndex.flatReferences) { + if (this.nodeIndex.flatReferences[key].id === node.sourceUnit) { + nodeDefinition = this.nodeIndex.flatReferences[key] + } + } + } + return nodeDefinition + } else { + const astNodes = await this.antlrService.listAstNodes() + if (astNodes && astNodes.length) { + for (const node of astNodes) { + if (node.range[0] <= position && node.range[1] >= position) { + if (nodeDefinition && nodeDefinition.range[0] < node.range[0]) { + nodeDefinition = node } + if (!nodeDefinition) nodeDefinition = node + } } - + if (nodeDefinition && nodeDefinition.type && nodeDefinition.type === 'Identifier') { + const nodeForIdentifier = await this.findIdentifier(nodeDefinition) + if (nodeForIdentifier) nodeDefinition = nodeForIdentifier + } + return nodeDefinition + } } - async getContractNodes(contractName: string) { - if (this.nodeIndex.nodesPerFile + } + + async getContractNodes(contractName: string) { + if (this.nodeIndex.nodesPerFile && this.nodeIndex.nodesPerFile[this.currentFile] && this.nodeIndex.nodesPerFile[this.currentFile].contracts[contractName] && this.nodeIndex.nodesPerFile[this.currentFile].contracts[contractName].contractNodes) { - return this.nodeIndex.nodesPerFile[this.currentFile].contracts[contractName] - } - return false + return this.nodeIndex.nodesPerFile[this.currentFile].contracts[contractName] } + return false + } - async getCurrentFileNodes() { - if (this.nodeIndex.nodesPerFile + async getCurrentFileNodes() { + if (this.nodeIndex.nodesPerFile && this.nodeIndex.nodesPerFile[this.currentFile]) { - return this.nodeIndex.nodesPerFile[this.currentFile] - } - return false + return this.nodeIndex.nodesPerFile[this.currentFile] } + return false + } - /** + /** * * @param identifierNode * @returns */ - async findIdentifier(identifierNode: any) { - const astNodes = await this.antlrService.listAstNodes() - for (const node of astNodes) { - if (node.name === identifierNode.name && node.nodeType !== 'Identifier') { - return node - } - } + async findIdentifier(identifierNode: any) { + const astNodes = await this.antlrService.listAstNodes() + for (const node of astNodes) { + if (node.name === identifierNode.name && node.nodeType !== 'Identifier') { + return node + } } + } - /** + /** * * @param node * @returns */ - async positionOfDefinition(node: genericASTNode): Promise { - if (node) { - if (node.src) { - const position = sourceMappingDecoder.decode(node.src) - if (position) { - return position - } - } + async positionOfDefinition(node: genericASTNode): Promise { + if (node) { + if (node.src) { + const position = sourceMappingDecoder.decode(node.src) + if (position) { + return position } - return null + } } + return null + } - /** + /** * * @param node * @param imported * @returns */ - async resolveImports(node: any, imported = {}) { - if (node.nodeType === 'ImportDirective' && !imported[node.sourceUnit]) { - const importNode: any = await this.getNodeById(node.sourceUnit) - imported[importNode.id] = importNode - if (importNode.nodes) { - for (const child of importNode.nodes) { - imported = await this.resolveImports(child, imported) - } - } + async resolveImports(node: any, imported = {}) { + if (node.nodeType === 'ImportDirective' && !imported[node.sourceUnit]) { + const importNode: any = await this.getNodeById(node.sourceUnit) + imported[importNode.id] = importNode + if (importNode.nodes) { + for (const child of importNode.nodes) { + imported = await this.resolveImports(child, imported) } - return imported + } } + return imported + } - /** + /** * * @param node * @returns */ - referencesOf(node: genericASTNode) { - const results: genericASTNode[] = [] - const highlights = (id: number) => { - if (this.nodeIndex.declarations && this.nodeIndex.declarations[id]) { - const refs = this.nodeIndex.declarations[id] - for (const ref in refs) { - const node = refs[ref] - results.push(node) - } - } - } - if (node && ("referencedDeclaration" in node) && node.referencedDeclaration) { - highlights(node.referencedDeclaration) - const current = this.nodeIndex.flatReferences[node.referencedDeclaration] - results.push(current) - } else { - highlights(node.id) + referencesOf(node: genericASTNode) { + const results: genericASTNode[] = [] + const highlights = (id: number) => { + if (this.nodeIndex.declarations && this.nodeIndex.declarations[id]) { + const refs = this.nodeIndex.declarations[id] + for (const ref in refs) { + const node = refs[ref] + results.push(node) } - return results + } + } + if (node && ("referencedDeclaration" in node) && node.referencedDeclaration) { + highlights(node.referencedDeclaration) + const current = this.nodeIndex.flatReferences[node.referencedDeclaration] + results.push(current) + } else { + highlights(node.id) } + return results + } - /** + /** * * @param position * @returns */ - async referrencesAtPosition(position: any): Promise { - const nodes = await this.nodesAtPosition(position) - if (nodes && nodes.length) { - const node = nodes[nodes.length - 1] - if (node) { - return this.referencesOf(node) - } - } + async referrencesAtPosition(position: any): Promise { + const nodes = await this.nodesAtPosition(position) + if (nodes && nodes.length) { + const node = nodes[nodes.length - 1] + if (node) { + return this.referencesOf(node) + } } + } - /** + /** * * @returns */ - async getNodes(): Promise { - return this.nodeIndex.flatReferences - } + async getNodes(): Promise { + return this.nodeIndex.flatReferences + } - /** + /** * * @param node * @returns */ - async getNodeLink(node: genericASTNode) { - const lineColumn = await this.getLineColumnOfNode(node) - const position = await this.positionOfDefinition(node) - if (this.compilerAbstract && this.compilerAbstract.source && position) { - const fileName = this.compilerAbstract.getSourceName(position.file) - return lineColumn ? `${fileName} ${lineColumn.start.line}:${lineColumn.start.column}` : null - } - return '' + async getNodeLink(node: genericASTNode) { + const lineColumn = await this.getLineColumnOfNode(node) + const position = await this.positionOfDefinition(node) + if (this.compilerAbstract && this.compilerAbstract.source && position) { + const fileName = this.compilerAbstract.getSourceName(position.file) + return lineColumn ? `${fileName} ${lineColumn.start.line}:${lineColumn.start.column}` : null } + return '' + } - /* + /* * @param node */ - async getLineColumnOfNode(node: any) { - const position = await this.positionOfDefinition(node) - return this.getLineColumnOfPosition(position) - } + async getLineColumnOfNode(node: any) { + const position = await this.positionOfDefinition(node) + return this.getLineColumnOfPosition(position) + } - /* + /* * @param position */ - async getLineColumnOfPosition(position: any) { - if (position) { - const fileName = this.compilerAbstract.getSourceName(position.file) - const lineBreaks = sourceMappingDecoder.getLinebreakPositions(this.compilerAbstract.source.sources[fileName].content) - const lineColumn = sourceMappingDecoder.convertOffsetToLineColumn(position, lineBreaks) - return lineColumn - } + async getLineColumnOfPosition(position: any) { + if (position) { + const fileName = this.compilerAbstract.getSourceName(position.file) + const lineBreaks = sourceMappingDecoder.getLinebreakPositions(this.compilerAbstract.source.sources[fileName].content) + const lineColumn = sourceMappingDecoder.convertOffsetToLineColumn(position, lineBreaks) + return lineColumn } + } - /** + /** * * @param node * @returns */ - async getNodeDocumentation(node: genericASTNode) { - if (("documentation" in node) && node.documentation && (node.documentation as any).text) { - let text = ''; - (node.documentation as any).text.split('\n').forEach(line => { - text += `${line.trim()}\n` - }) - return text - } + async getNodeDocumentation(node: genericASTNode) { + if (("documentation" in node) && node.documentation && (node.documentation as any).text) { + let text = ''; + (node.documentation as any).text.split('\n').forEach(line => { + text += `${line.trim()}\n` + }) + return text } + } - /** + /** * * @param node * @returns */ - async getVariableDeclaration(node: any) { - const nodeVisibility = node.visibility && node.visibility.length ? node.visibility + ' ' : '' - const nodeName = node.name && node.name.length ? node.name : '' - if (node.typeDescriptions && node.typeDescriptions.typeString) { - return `${node.typeDescriptions.typeString} ${nodeVisibility}${nodeName}` - } else { - if (node.typeName && node.typeName.name) { - return `${node.typeName.name} ${nodeVisibility}${nodeName}` - } - else if (node.typeName && node.typeName.namePath) { - return `${node.typeName.namePath} ${nodeVisibility}${nodeName}` - } - else { - return `${nodeName}${nodeName}` - } - } - } - - /** + async getVariableDeclaration(node: any) { + const nodeVisibility = node.visibility && node.visibility.length ? node.visibility + ' ' : '' + const nodeName = node.name && node.name.length ? node.name : '' + if (node.typeDescriptions && node.typeDescriptions.typeString) { + return `${node.typeDescriptions.typeString} ${nodeVisibility}${nodeName}` + } else { + if (node.typeName && node.typeName.name) { + return `${node.typeName.name} ${nodeVisibility}${nodeName}` + } + else if (node.typeName && node.typeName.namePath) { + return `${node.typeName.namePath} ${nodeVisibility}${nodeName}` + } + else { + return `${nodeName}${nodeName}` + } + } + } + + /** * * @param node * @returns */ - async getFunctionParamaters(node: any) { - const localParam = (node.parameters && node.parameters.parameters) || (node.parameters) - if (localParam) { - const params = [] - for (const param of localParam) { - params.push(await this.getVariableDeclaration(param)) - } - return `(${params.join(', ')})` - } - } - - /** + async getFunctionParamaters(node: any) { + const localParam = (node.parameters && node.parameters.parameters) || (node.parameters) + if (localParam) { + const params = [] + for (const param of localParam) { + params.push(await this.getVariableDeclaration(param)) + } + return `(${params.join(', ')})` + } + } + + /** * * @param node * @returns */ - async getFunctionReturnParameters(node: any) { - const localParam = (node.returnParameters && node.returnParameters.parameters) - if (localParam) { - const params = [] - for (const param of localParam) { - params.push(await this.getVariableDeclaration(param)) - } - return `(${params.join(', ')})` - } - } + async getFunctionReturnParameters(node: any) { + const localParam = (node.returnParameters && node.returnParameters.parameters) + if (localParam) { + const params = [] + for (const param of localParam) { + params.push(await this.getVariableDeclaration(param)) + } + return `(${params.join(', ')})` + } + } diff --git a/apps/remix-ide/src/app/plugins/parser/services/antlr-worker.ts b/apps/remix-ide/src/app/plugins/parser/services/antlr-worker.ts index c9e026d085..6a77ac5205 100644 --- a/apps/remix-ide/src/app/plugins/parser/services/antlr-worker.ts +++ b/apps/remix-ide/src/app/plugins/parser/services/antlr-worker.ts @@ -1,45 +1,45 @@ let parser: any self.onmessage = (e: MessageEvent) => { - const data: any = e.data - switch (data.cmd) { - case 'load': - { - (self as any).importScripts(e.data.url) - // @ts-ignore - parser = SolidityParser as any; + const data: any = e.data + switch (data.cmd) { + case 'load': + { + (self as any).importScripts(e.data.url) + // @ts-ignore + parser = SolidityParser as any; - self.postMessage({ - cmd: 'loaded', - }) - break - } + self.postMessage({ + cmd: 'loaded', + }) + break + } - case 'parse': - if (data.text && parser) { + case 'parse': + if (data.text && parser) { - try { - let startTime = performance.now() - const blocks = parser.parseBlock(data.text, { loc: true, range: true, tolerant: true }) - const blockDuration = performance.now() - startTime - startTime = performance.now() - const ast = parser.parse(data.text, { loc: true, range: true, tolerant: true }) - const endTime = performance.now() + try { + let startTime = performance.now() + const blocks = parser.parseBlock(data.text, { loc: true, range: true, tolerant: true }) + const blockDuration = performance.now() - startTime + startTime = performance.now() + const ast = parser.parse(data.text, { loc: true, range: true, tolerant: true }) + const endTime = performance.now() - self.postMessage({ - cmd: 'parsed', - timestamp: data.timestamp, - ast, - text: data.text, - file: data.file, - duration: endTime - startTime, - blockDuration, - blocks - }) - } catch (e) { - // do nothing - } + self.postMessage({ + cmd: 'parsed', + timestamp: data.timestamp, + ast, + text: data.text, + file: data.file, + duration: endTime - startTime, + blockDuration, + blocks + }) + } catch (e) { + // do nothing + } - } - break } + break + } } \ No newline at end of file diff --git a/apps/remix-ide/src/app/plugins/parser/services/code-parser-antlr-service.ts b/apps/remix-ide/src/app/plugins/parser/services/code-parser-antlr-service.ts index 3585dda157..ae49da7443 100644 --- a/apps/remix-ide/src/app/plugins/parser/services/code-parser-antlr-service.ts +++ b/apps/remix-ide/src/app/plugins/parser/services/code-parser-antlr-service.ts @@ -20,13 +20,13 @@ interface BlockDefinition { } export default class CodeParserAntlrService { - plugin: CodeParser - worker: Worker - parserStartTime: number = 0 - workerTimer: NodeJS.Timer - parserThreshold: number = 10 - parserThresholdSampleAmount = 3 - cache: { + plugin: CodeParser + worker: Worker + parserStartTime: number = 0 + workerTimer: NodeJS.Timer + parserThreshold: number = 10 + parserThresholdSampleAmount = 3 + cache: { [name: string]: { text: string, ast: antlr.ParseResult | null, @@ -36,253 +36,253 @@ export default class CodeParserAntlrService { blockDurations?: number[] } } = {}; - constructor(plugin: CodeParser) { - this.plugin = plugin - this.createWorker() - } + constructor(plugin: CodeParser) { + this.plugin = plugin + this.createWorker() + } - createWorker() { - this.worker = new Worker(new URL('./antlr-worker', import.meta.url)) - this.worker.postMessage({ - cmd: 'load', - url: document.location.protocol + '//' + document.location.host + '/assets/js/parser/antlr.js', - }); - const self = this + createWorker() { + this.worker = new Worker(new URL('./antlr-worker', import.meta.url)) + this.worker.postMessage({ + cmd: 'load', + url: document.location.protocol + '//' + document.location.host + '/assets/js/parser/antlr.js', + }); + const self = this - this.worker.addEventListener('message', function (ev) { - switch (ev.data.cmd) { - case 'parsed': - if (ev.data.ast && self.parserStartTime === ev.data.timestamp) { - self.cache[ev.data.file] = { - ...self.cache[ev.data.file], - text: ev.data.text, - ast: ev.data.ast, - duration: ev.data.duration, - blocks: ev.data.blocks, - blockDurations: self.cache[ev.data.file].blockDurations? [...self.cache[ev.data.file].blockDurations.slice(-self.parserThresholdSampleAmount), ev.data.blockDuration]: [ev.data.blockDuration] - } - self.setFileParsingState(ev.data.file) - } - break; - } + this.worker.addEventListener('message', function (ev) { + switch (ev.data.cmd) { + case 'parsed': + if (ev.data.ast && self.parserStartTime === ev.data.timestamp) { + self.cache[ev.data.file] = { + ...self.cache[ev.data.file], + text: ev.data.text, + ast: ev.data.ast, + duration: ev.data.duration, + blocks: ev.data.blocks, + blockDurations: self.cache[ev.data.file].blockDurations? [...self.cache[ev.data.file].blockDurations.slice(-self.parserThresholdSampleAmount), ev.data.blockDuration]: [ev.data.blockDuration] + } + self.setFileParsingState(ev.data.file) + } + break; + } - }); - } + }); + } - setFileParsingState(file: string) { - if (this.cache[file]) { - if (this.cache[file].blockDurations && this.cache[file].blockDurations.length > this.parserThresholdSampleAmount) { - // calculate average of durations to determine if the parsing should be disabled - const values = [...this.cache[file].blockDurations] - const average = values.reduce((a, b) => a + b, 0) / values.length - if (average > this.parserThreshold) { - this.cache[file].parsingEnabled = false - } else { - this.cache[file].parsingEnabled = true - } - } - } + setFileParsingState(file: string) { + if (this.cache[file]) { + if (this.cache[file].blockDurations && this.cache[file].blockDurations.length > this.parserThresholdSampleAmount) { + // calculate average of durations to determine if the parsing should be disabled + const values = [...this.cache[file].blockDurations] + const average = values.reduce((a, b) => a + b, 0) / values.length + if (average > this.parserThreshold) { + this.cache[file].parsingEnabled = false + } else { + this.cache[file].parsingEnabled = true + } + } } + } - enableWorker() { - if (!this.workerTimer) { - this.workerTimer = setInterval(() => { - this.setCurrentFileAST() - }, 5000) - } + enableWorker() { + if (!this.workerTimer) { + this.workerTimer = setInterval(() => { + this.setCurrentFileAST() + }, 5000) } + } - disableWorker() { - clearInterval(this.workerTimer) - } + disableWorker() { + clearInterval(this.workerTimer) + } - async parseWithWorker(text: string, file: string) { - this.parserStartTime = Date.now() - this.worker.postMessage({ - cmd: 'parse', - text, - timestamp: this.parserStartTime, - file, - parsingEnabled: (this.cache[file] && this.cache[file].parsingEnabled) || true - }); + async parseWithWorker(text: string, file: string) { + this.parserStartTime = Date.now() + this.worker.postMessage({ + cmd: 'parse', + text, + timestamp: this.parserStartTime, + file, + parsingEnabled: (this.cache[file] && this.cache[file].parsingEnabled) || true + }); - } + } - async parseSolidity(text: string) { - const ast: antlr.ParseResult = (SolidityParser as any).parse(text, { loc: true, range: true, tolerant: true }) - return ast - } + async parseSolidity(text: string) { + const ast: antlr.ParseResult = (SolidityParser as any).parse(text, { loc: true, range: true, tolerant: true }) + return ast + } - /** + /** * Tries to parse the current file or the given text and returns the AST * If the parsing fails it returns the last successful AST for this file * @param text * @returns */ - async setCurrentFileAST(text: string | null = null) { - try { - this.plugin.currentFile = await this.plugin.call('fileManager', 'file') - if (this.plugin.currentFile && this.plugin.currentFile.endsWith('.sol')) { - const fileContent = text || await this.plugin.call('fileManager', 'readFile', this.plugin.currentFile) - if (!this.cache[this.plugin.currentFile]) { - this.cache[this.plugin.currentFile] = { - text: '', - ast: null, - parsingEnabled: true, - blockDurations: [] - } - } - if (this.cache[this.plugin.currentFile] && this.cache[this.plugin.currentFile].text !== fileContent) { - try { - await this.parseWithWorker(fileContent, this.plugin.currentFile) - } catch (e) { - // do nothing - } - } - } - } catch (e) { + async setCurrentFileAST(text: string | null = null) { + try { + this.plugin.currentFile = await this.plugin.call('fileManager', 'file') + if (this.plugin.currentFile && this.plugin.currentFile.endsWith('.sol')) { + const fileContent = text || await this.plugin.call('fileManager', 'readFile', this.plugin.currentFile) + if (!this.cache[this.plugin.currentFile]) { + this.cache[this.plugin.currentFile] = { + text: '', + ast: null, + parsingEnabled: true, + blockDurations: [] + } + } + if (this.cache[this.plugin.currentFile] && this.cache[this.plugin.currentFile].text !== fileContent) { + try { + await this.parseWithWorker(fileContent, this.plugin.currentFile) + } catch (e) { // do nothing + } } + } + } catch (e) { + // do nothing } + } - /** + /** * Lists the AST nodes from the current file parser * These nodes need to be changed to match the node types returned by the compiler * @returns */ - async listAstNodes() { - this.plugin.currentFile = await this.plugin.call('fileManager', 'file') - if (!this.cache[this.plugin.currentFile]) return - const nodes: AstNode[] = []; - (SolidityParser as any).visit(this.cache[this.plugin.currentFile].ast, { - StateVariableDeclaration: (node: antlr.StateVariableDeclaration) => { - if (node.variables) { - for (const variable of node.variables) { - nodes.push({ ...variable, nodeType: 'VariableDeclaration', id: null, src: null }) - } - } - }, - VariableDeclaration: (node: antlr.VariableDeclaration) => { - nodes.push({ ...node, nodeType: node.type, id: null, src: null }) - }, - UserDefinedTypeName: (node: antlr.UserDefinedTypeName) => { - nodes.push({ ...node, nodeType: node.type, id: null, src: null }) - }, - FunctionDefinition: (node: antlr.FunctionDefinition) => { - nodes.push({ ...node, nodeType: node.type, id: null, src: null }) - }, - ContractDefinition: (node: antlr.ContractDefinition) => { - nodes.push({ ...node, nodeType: node.type, id: null, src: null }) - }, - MemberAccess: function (node: antlr.MemberAccess) { - nodes.push({ ...node, nodeType: node.type, id: null, src: null }) - }, - Identifier: function (node: antlr.Identifier) { - nodes.push({ ...node, nodeType: node.type, id: null, src: null }) - }, - EventDefinition: function (node: antlr.EventDefinition) { - nodes.push({ ...node, nodeType: node.type, id: null, src: null }) - }, - ModifierDefinition: function (node: antlr.ModifierDefinition) { - nodes.push({ ...node, nodeType: node.type, id: null, src: null }) - }, - InvalidNode: function (node: antlr.InvalidNode) { - nodes.push({ ...node, nodeType: node.type, id: null, src: null }) - }, - EnumDefinition: function (node: antlr.EnumDefinition) { - nodes.push({ ...node, nodeType: node.type, id: null, src: null }) - }, - StructDefinition: function (node: antlr.StructDefinition) { - nodes.push({ ...node, nodeType: node.type, id: null, src: null }) - } + async listAstNodes() { + this.plugin.currentFile = await this.plugin.call('fileManager', 'file') + if (!this.cache[this.plugin.currentFile]) return + const nodes: AstNode[] = []; + (SolidityParser as any).visit(this.cache[this.plugin.currentFile].ast, { + StateVariableDeclaration: (node: antlr.StateVariableDeclaration) => { + if (node.variables) { + for (const variable of node.variables) { + nodes.push({ ...variable, nodeType: 'VariableDeclaration', id: null, src: null }) + } + } + }, + VariableDeclaration: (node: antlr.VariableDeclaration) => { + nodes.push({ ...node, nodeType: node.type, id: null, src: null }) + }, + UserDefinedTypeName: (node: antlr.UserDefinedTypeName) => { + nodes.push({ ...node, nodeType: node.type, id: null, src: null }) + }, + FunctionDefinition: (node: antlr.FunctionDefinition) => { + nodes.push({ ...node, nodeType: node.type, id: null, src: null }) + }, + ContractDefinition: (node: antlr.ContractDefinition) => { + nodes.push({ ...node, nodeType: node.type, id: null, src: null }) + }, + MemberAccess: function (node: antlr.MemberAccess) { + nodes.push({ ...node, nodeType: node.type, id: null, src: null }) + }, + Identifier: function (node: antlr.Identifier) { + nodes.push({ ...node, nodeType: node.type, id: null, src: null }) + }, + EventDefinition: function (node: antlr.EventDefinition) { + nodes.push({ ...node, nodeType: node.type, id: null, src: null }) + }, + ModifierDefinition: function (node: antlr.ModifierDefinition) { + nodes.push({ ...node, nodeType: node.type, id: null, src: null }) + }, + InvalidNode: function (node: antlr.InvalidNode) { + nodes.push({ ...node, nodeType: node.type, id: null, src: null }) + }, + EnumDefinition: function (node: antlr.EnumDefinition) { + nodes.push({ ...node, nodeType: node.type, id: null, src: null }) + }, + StructDefinition: function (node: antlr.StructDefinition) { + nodes.push({ ...node, nodeType: node.type, id: null, src: null }) + } - }) - return nodes - } + }) + return nodes + } - /** + /** * * @param ast * @returns */ - async getLastNodeInLine(ast: string) { - let lastNode: any - const checkLastNode = (node: antlr.MemberAccess | antlr.Identifier) => { - if (lastNode && lastNode.range && lastNode.range[1]) { - if (node.range[1] > lastNode.range[1]) { - lastNode = node - } - } else { - lastNode = node - } + async getLastNodeInLine(ast: string) { + let lastNode: any + const checkLastNode = (node: antlr.MemberAccess | antlr.Identifier) => { + if (lastNode && lastNode.range && lastNode.range[1]) { + if (node.range[1] > lastNode.range[1]) { + lastNode = node } + } else { + lastNode = node + } + } - (SolidityParser as any).visit(ast, { - MemberAccess: function (node: antlr.MemberAccess) { - checkLastNode(node) - }, - Identifier: function (node: antlr.Identifier) { - checkLastNode(node) - } - }) - if (lastNode && lastNode.expression) { - return lastNode.expression - } - return lastNode + (SolidityParser as any).visit(ast, { + MemberAccess: function (node: antlr.MemberAccess) { + checkLastNode(node) + }, + Identifier: function (node: antlr.Identifier) { + checkLastNode(node) + } + }) + if (lastNode && lastNode.expression) { + return lastNode.expression } - /* + return lastNode + } + /* * get the code blocks of the current file */ - async getCurrentFileBlocks(text: string | null = null) { - this.plugin.currentFile = await this.plugin.call('fileManager', 'file') - if (this.cache[this.plugin.currentFile]) { - if (!this.cache[this.plugin.currentFile].parsingEnabled) { - return - } - } - if (this.plugin.currentFile && this.plugin.currentFile.endsWith('.sol')) { - const fileContent = text || await this.plugin.call('fileManager', 'readFile', this.plugin.currentFile) - try { - const startTime = Date.now() - const blocks = (SolidityParser as any).parseBlock(fileContent, { loc: true, range: true, tolerant: true }) - if(this.cache[this.plugin.currentFile] && this.cache[this.plugin.currentFile].blockDurations){ - this.cache[this.plugin.currentFile].blockDurations = [...this.cache[this.plugin.currentFile].blockDurations.slice(-this.parserThresholdSampleAmount), Date.now() - startTime] - this.setFileParsingState(this.plugin.currentFile) - } - if (blocks) this.cache[this.plugin.currentFile].blocks = blocks - return blocks - } catch (e) { - // do nothing - } + async getCurrentFileBlocks(text: string | null = null) { + this.plugin.currentFile = await this.plugin.call('fileManager', 'file') + if (this.cache[this.plugin.currentFile]) { + if (!this.cache[this.plugin.currentFile].parsingEnabled) { + return + } + } + if (this.plugin.currentFile && this.plugin.currentFile.endsWith('.sol')) { + const fileContent = text || await this.plugin.call('fileManager', 'readFile', this.plugin.currentFile) + try { + const startTime = Date.now() + const blocks = (SolidityParser as any).parseBlock(fileContent, { loc: true, range: true, tolerant: true }) + if(this.cache[this.plugin.currentFile] && this.cache[this.plugin.currentFile].blockDurations){ + this.cache[this.plugin.currentFile].blockDurations = [...this.cache[this.plugin.currentFile].blockDurations.slice(-this.parserThresholdSampleAmount), Date.now() - startTime] + this.setFileParsingState(this.plugin.currentFile) } + if (blocks) this.cache[this.plugin.currentFile].blocks = blocks + return blocks + } catch (e) { + // do nothing + } } + } - /** + /** * Returns the block surrounding the given position * For example if the position is in the middle of a function, it will return the function * @param {position} position * @param {string} text // optional * @return {any} * */ - async getANTLRBlockAtPosition(position: any, text: string = null) { - const blocks: any[] = await this.getCurrentFileBlocks(text) + async getANTLRBlockAtPosition(position: any, text: string = null) { + const blocks: any[] = await this.getCurrentFileBlocks(text) - const walkAst = (blocks) => { - let nodeFound = null - for (const object of blocks) { - if (object.start <= position) { - nodeFound = object - break - } - } - return nodeFound + const walkAst = (blocks) => { + let nodeFound = null + for (const object of blocks) { + if (object.start <= position) { + nodeFound = object + break } - if (!blocks) return - blocks.reverse() - const block = walkAst(blocks) - return block + } + return nodeFound } + if (!blocks) return + blocks.reverse() + const block = walkAst(blocks) + return block + } } diff --git a/apps/remix-ide/src/app/plugins/parser/services/code-parser-compiler.ts b/apps/remix-ide/src/app/plugins/parser/services/code-parser-compiler.ts index b3a1a9c322..ab65134e71 100644 --- a/apps/remix-ide/src/app/plugins/parser/services/code-parser-compiler.ts +++ b/apps/remix-ide/src/app/plugins/parser/services/code-parser-compiler.ts @@ -34,253 +34,253 @@ type errorMarker = { file: string } export default class CodeParserCompiler { + plugin: CodeParser + compiler: any // used to compile the current file seperately from the main compiler + onAstFinished: (success: any, data: CompilationResult, source: CompilationSourceCode, input: any, version: any) => Promise; + errorState: boolean; + gastEstimateTimeOut: any + constructor( plugin: CodeParser - compiler: any // used to compile the current file seperately from the main compiler - onAstFinished: (success: any, data: CompilationResult, source: CompilationSourceCode, input: any, version: any) => Promise; - errorState: boolean; - gastEstimateTimeOut: any - constructor( - plugin: CodeParser - ) { - this.plugin = plugin - } - - init() { + ) { + this.plugin = plugin + } - this.onAstFinished = async (success, data: CompilationResult, source: CompilationSourceCode, input: any, version) => { - this.plugin.call('editor', 'clearAnnotations') - this.errorState = true - const result = new CompilerAbstract('soljson', data, source, input) - let allErrors: errorMarker[] = [] - if (data.errors || data.error) { - const file = await this.plugin.call('fileManager', 'getCurrentFile') - const currentFileContent = await this.plugin.call('fileManager', 'readFile', file) - const sources = result.getSourceCode().sources || [] - if (data.error) { - if (data.error.formattedMessage) { - // mark this file as error - const errorMarker = await this.createErrorMarker(data.error, file, { start: { line: 0, column: 0 }, end: { line: 0, column: 100 } }) - allErrors = [...allErrors, errorMarker] - } - } else { - for (const error of data.errors) { - if (!error.sourceLocation) { - // mark this file as error - const errorMarker = await this.createErrorMarker(error, file, { start: { line: 0, column: 0 }, end: { line: 0, column: 100 } }) - allErrors = [...allErrors, errorMarker] - } else { - const lineBreaks = sourceMappingDecoder.getLinebreakPositions(sources[error.sourceLocation.file].content) - const lineColumn = sourceMappingDecoder.convertOffsetToLineColumn({ - start: error.sourceLocation.start, - length: error.sourceLocation.end - error.sourceLocation.start - }, lineBreaks) + init() { + this.onAstFinished = async (success, data: CompilationResult, source: CompilationSourceCode, input: any, version) => { + this.plugin.call('editor', 'clearAnnotations') + this.errorState = true + const result = new CompilerAbstract('soljson', data, source, input) + let allErrors: errorMarker[] = [] + if (data.errors || data.error) { + const file = await this.plugin.call('fileManager', 'getCurrentFile') + const currentFileContent = await this.plugin.call('fileManager', 'readFile', file) + const sources = result.getSourceCode().sources || [] + if (data.error) { + if (data.error.formattedMessage) { + // mark this file as error + const errorMarker = await this.createErrorMarker(data.error, file, { start: { line: 0, column: 0 }, end: { line: 0, column: 100 } }) + allErrors = [...allErrors, errorMarker] + } + } else { + for (const error of data.errors) { + if (!error.sourceLocation) { + // mark this file as error + const errorMarker = await this.createErrorMarker(error, file, { start: { line: 0, column: 0 }, end: { line: 0, column: 100 } }) + allErrors = [...allErrors, errorMarker] + } else { + const lineBreaks = sourceMappingDecoder.getLinebreakPositions(sources[error.sourceLocation.file].content) + const lineColumn = sourceMappingDecoder.convertOffsetToLineColumn({ + start: error.sourceLocation.start, + length: error.sourceLocation.end - error.sourceLocation.start + }, lineBreaks) - const filePath = error.sourceLocation.file - const fileTarget = await this.plugin.call('fileManager', 'getUrlFromPath', filePath) - const importFilePositions = await this.getPositionForImportErrors(fileTarget.file, currentFileContent) - for (const importFilePosition of importFilePositions) { - for (const line of importFilePosition.lines) { - allErrors = [...allErrors, await this.createErrorMarker(error, file, line.position)] - } - } + const filePath = error.sourceLocation.file + const fileTarget = await this.plugin.call('fileManager', 'getUrlFromPath', filePath) - allErrors = [...allErrors, await this.createErrorMarker(error, filePath, lineColumn)] - } - } + const importFilePositions = await this.getPositionForImportErrors(fileTarget.file, currentFileContent) + for (const importFilePosition of importFilePositions) { + for (const line of importFilePosition.lines) { + allErrors = [...allErrors, await this.createErrorMarker(error, file, line.position)] } + } - const displayErrors = await this.plugin.call('config', 'getAppParameter', 'display-errors') - if (displayErrors) await this.plugin.call('editor', 'addErrorMarker', allErrors) - this.addDecorators(allErrors, sources) - } else { - await this.plugin.call('editor', 'clearErrorMarkers', result.getSourceCode().sources) - await this.clearDecorators(result.getSourceCode().sources) + allErrors = [...allErrors, await this.createErrorMarker(error, filePath, lineColumn)] } + } + } + const displayErrors = await this.plugin.call('config', 'getAppParameter', 'display-errors') + if (displayErrors) await this.plugin.call('editor', 'addErrorMarker', allErrors) + this.addDecorators(allErrors, sources) + } else { + await this.plugin.call('editor', 'clearErrorMarkers', result.getSourceCode().sources) + await this.clearDecorators(result.getSourceCode().sources) + } - if (!data.sources) return - if (data.sources && Object.keys(data.sources).length === 0) return - this.plugin.compilerAbstract = new CompilerAbstract('soljson', data, source, input) - this.errorState = false - this.plugin.nodeIndex = { - declarations: {}, - flatReferences: {}, - nodesPerFile: {}, - } + if (!data.sources) return + if (data.sources && Object.keys(data.sources).length === 0) return + this.plugin.compilerAbstract = new CompilerAbstract('soljson', data, source, input) + this.errorState = false + this.plugin.nodeIndex = { + declarations: {}, + flatReferences: {}, + nodesPerFile: {}, + } - this.plugin._buildIndex(data, source) - // cast from the remix-plugin interface to the solidity one. Should be fixed when remix-plugin move to the remix-project repository - this.plugin.nodeIndex.nodesPerFile[this.plugin.currentFile] = this.plugin._extractFileNodes(this.plugin.currentFile, this.plugin.compilerAbstract as unknown as lastCompilationResult) - await this.plugin.gasService.showGasEstimates() - this.plugin.emit('astFinished') - } - - this.compiler = new Compiler((url, cb) => this.plugin.call('contentImport', 'resolveAndSave', url, undefined).then((result) => cb(null, result)).catch((error) => cb(error.message))) - this.compiler.event.register('compilationFinished', this.onAstFinished) + + this.plugin._buildIndex(data, source) + // cast from the remix-plugin interface to the solidity one. Should be fixed when remix-plugin move to the remix-project repository + this.plugin.nodeIndex.nodesPerFile[this.plugin.currentFile] = this.plugin._extractFileNodes(this.plugin.currentFile, this.plugin.compilerAbstract as unknown as lastCompilationResult) + await this.plugin.gasService.showGasEstimates() + this.plugin.emit('astFinished') } + + this.compiler = new Compiler((url, cb) => this.plugin.call('contentImport', 'resolveAndSave', url, undefined).then((result) => cb(null, result)).catch((error) => cb(error.message))) + this.compiler.event.register('compilationFinished', this.onAstFinished) + } - // COMPILER + // COMPILER - /** + /** * * @returns */ - async compile() { - try { - this.plugin.currentFile = await this.plugin.call('fileManager', 'file') - if (this.plugin.currentFile && this.plugin.currentFile.endsWith('.sol')) { - const state = await this.plugin.call('solidity', 'getCompilerState') - this.compiler.set('optimize', state.optimize) - this.compiler.set('evmVersion', state.evmVersion) - this.compiler.set('language', state.language) - this.compiler.set('runs', state.runs) - this.compiler.set('useFileConfiguration', true) - this.compiler.set('compilerRetriggerMode', CompilerRetriggerMode.retrigger) - const configFileContent = { - "language": "Solidity", - "settings": { - "optimizer": { - "enabled": state.optimize, - "runs": state.runs - }, - "outputSelection": { - "*": { - "": ["ast"], - "*": ["evm.gasEstimates"] - } - }, - "evmVersion": state.evmVersion && state.evmVersion.toString() || "berlin", - } - } - - this.compiler.set('configFileContent', JSON.stringify(configFileContent)) - this.plugin.currentFile = await this.plugin.call('fileManager', 'file') - if (!this.plugin.currentFile) return - const content = await this.plugin.call('fileManager', 'readFile', this.plugin.currentFile) - const sources = { [this.plugin.currentFile]: { content } } - this.compiler.compile(sources, this.plugin.currentFile) - } - } catch (e) { - // do nothing + async compile() { + try { + this.plugin.currentFile = await this.plugin.call('fileManager', 'file') + if (this.plugin.currentFile && this.plugin.currentFile.endsWith('.sol')) { + const state = await this.plugin.call('solidity', 'getCompilerState') + this.compiler.set('optimize', state.optimize) + this.compiler.set('evmVersion', state.evmVersion) + this.compiler.set('language', state.language) + this.compiler.set('runs', state.runs) + this.compiler.set('useFileConfiguration', true) + this.compiler.set('compilerRetriggerMode', CompilerRetriggerMode.retrigger) + const configFileContent = { + "language": "Solidity", + "settings": { + "optimizer": { + "enabled": state.optimize, + "runs": state.runs + }, + "outputSelection": { + "*": { + "": ["ast"], + "*": ["evm.gasEstimates"] + } + }, + "evmVersion": state.evmVersion && state.evmVersion.toString() || "berlin", + } } - } - async addDecorators(allErrors: errorMarker[], sources: any) { - const displayErrors = await this.plugin.call('config', 'getAppParameter', 'display-errors') - if (!displayErrors) return - const errorsPerFiles: { [fileName: string]: errorMarker[] } = {} - for (const error of allErrors) { - if (!errorsPerFiles[error.file]) { - errorsPerFiles[error.file] = [] - } - errorsPerFiles[error.file].push(error) - } + this.compiler.set('configFileContent', JSON.stringify(configFileContent)) + this.plugin.currentFile = await this.plugin.call('fileManager', 'file') + if (!this.plugin.currentFile) return + const content = await this.plugin.call('fileManager', 'readFile', this.plugin.currentFile) + const sources = { [this.plugin.currentFile]: { content } } + this.compiler.compile(sources, this.plugin.currentFile) + } + } catch (e) { + // do nothing + } + } - const errorPriority = { - 'error': 0, - 'warning': 1, - } + async addDecorators(allErrors: errorMarker[], sources: any) { + const displayErrors = await this.plugin.call('config', 'getAppParameter', 'display-errors') + if (!displayErrors) return + const errorsPerFiles: { [fileName: string]: errorMarker[] } = {} + for (const error of allErrors) { + if (!errorsPerFiles[error.file]) { + errorsPerFiles[error.file] = [] + } + errorsPerFiles[error.file].push(error) + } - // sort errorPerFiles by error priority - const sortedErrorsPerFiles: { [fileName: string]: errorMarker[] } = {} - for (const fileName in errorsPerFiles) { - const errors = errorsPerFiles[fileName] - errors.sort((a, b) => { - return errorPriority[a.severity] - errorPriority[b.severity] - } - ) - sortedErrorsPerFiles[fileName] = errors - } - const filesWithOutErrors = Object.keys(sources).filter((fileName) => !sortedErrorsPerFiles[fileName]) - // add decorators - const decorators: fileDecoration[] = [] - for (const fileName in sortedErrorsPerFiles) { - const errors = sortedErrorsPerFiles[fileName] - const fileTarget = await this.plugin.call('fileManager', 'getPathFromUrl', fileName) - const decorator: fileDecoration = { - path: fileTarget.file, - isDirectory: false, - fileStateType: errors[0].severity == MarkerSeverity.Error ? fileDecorationType.Error : fileDecorationType.Warning, - fileStateLabelClass: errors[0].severity == MarkerSeverity.Error ? 'text-danger' : 'text-warning', - fileStateIconClass: '', - fileStateIcon: '', - text: errors.length.toString(), - owner: 'code-parser', - bubble: true, - comment: errors.map((error) => error.message), - } - decorators.push(decorator) - } - for (const fileName of filesWithOutErrors) { - const fileTarget = await this.plugin.call('fileManager', 'getPathFromUrl', fileName) - const decorator: fileDecoration = { - path: fileTarget.file, - isDirectory: false, - fileStateType: fileDecorationType.None, - fileStateLabelClass: '', - fileStateIconClass: '', - fileStateIcon: '', - text: '', - owner: 'code-parser', - bubble: false - } - decorators.push(decorator) - } - await this.plugin.call('fileDecorator', 'setFileDecorators', decorators) - await this.plugin.call('editor', 'clearErrorMarkers', filesWithOutErrors) + const errorPriority = { + 'error': 0, + 'warning': 1, + } + // sort errorPerFiles by error priority + const sortedErrorsPerFiles: { [fileName: string]: errorMarker[] } = {} + for (const fileName in errorsPerFiles) { + const errors = errorsPerFiles[fileName] + errors.sort((a, b) => { + return errorPriority[a.severity] - errorPriority[b.severity] + } + ) + sortedErrorsPerFiles[fileName] = errors + } + const filesWithOutErrors = Object.keys(sources).filter((fileName) => !sortedErrorsPerFiles[fileName]) + // add decorators + const decorators: fileDecoration[] = [] + for (const fileName in sortedErrorsPerFiles) { + const errors = sortedErrorsPerFiles[fileName] + const fileTarget = await this.plugin.call('fileManager', 'getPathFromUrl', fileName) + const decorator: fileDecoration = { + path: fileTarget.file, + isDirectory: false, + fileStateType: errors[0].severity == MarkerSeverity.Error ? fileDecorationType.Error : fileDecorationType.Warning, + fileStateLabelClass: errors[0].severity == MarkerSeverity.Error ? 'text-danger' : 'text-warning', + fileStateIconClass: '', + fileStateIcon: '', + text: errors.length.toString(), + owner: 'code-parser', + bubble: true, + comment: errors.map((error) => error.message), + } + decorators.push(decorator) + } + for (const fileName of filesWithOutErrors) { + const fileTarget = await this.plugin.call('fileManager', 'getPathFromUrl', fileName) + const decorator: fileDecoration = { + path: fileTarget.file, + isDirectory: false, + fileStateType: fileDecorationType.None, + fileStateLabelClass: '', + fileStateIconClass: '', + fileStateIcon: '', + text: '', + owner: 'code-parser', + bubble: false + } + decorators.push(decorator) } + await this.plugin.call('fileDecorator', 'setFileDecorators', decorators) + await this.plugin.call('editor', 'clearErrorMarkers', filesWithOutErrors) - async createErrorMarker(error: any, filePath: string, lineColumn): Promise { - return { - message: error.formattedMessage, - severity: error.severity === 'error' ? MarkerSeverity.Error : MarkerSeverity.Warning, - position: { - start: { - line: ((lineColumn.start && lineColumn.start.line) || 0) + 1, - column: ((lineColumn.start && lineColumn.start.column) || 0) + 1 - }, - end: { - line: ((lineColumn.end && lineColumn.end.line) || 0) + 1, - column: ((lineColumn.end && lineColumn.end.column) || 0) + 1 - } - } - , file: filePath + } + + async createErrorMarker(error: any, filePath: string, lineColumn): Promise { + return { + message: error.formattedMessage, + severity: error.severity === 'error' ? MarkerSeverity.Error : MarkerSeverity.Warning, + position: { + start: { + line: ((lineColumn.start && lineColumn.start.line) || 0) + 1, + column: ((lineColumn.start && lineColumn.start.column) || 0) + 1 + }, + end: { + line: ((lineColumn.end && lineColumn.end.line) || 0) + 1, + column: ((lineColumn.end && lineColumn.end.column) || 0) + 1 } + } + , file: filePath } + } - async clearDecorators(sources: any) { - const decorators: fileDecoration[] = [] - if (!sources) return - for (const fileName of Object.keys(sources)) { - const decorator: fileDecoration = { - path: fileName, - isDirectory: false, - fileStateType: fileDecorationType.None, - fileStateLabelClass: '', - fileStateIconClass: '', - fileStateIcon: '', - text: '', - owner: 'code-parser', - bubble: false - } - decorators.push(decorator) - } + async clearDecorators(sources: any) { + const decorators: fileDecoration[] = [] + if (!sources) return + for (const fileName of Object.keys(sources)) { + const decorator: fileDecoration = { + path: fileName, + isDirectory: false, + fileStateType: fileDecorationType.None, + fileStateLabelClass: '', + fileStateIconClass: '', + fileStateIcon: '', + text: '', + owner: 'code-parser', + bubble: false + } + decorators.push(decorator) + } - await this.plugin.call('fileDecorator', 'setFileDecorators', decorators) - } + await this.plugin.call('fileDecorator', 'setFileDecorators', decorators) + } - async getPositionForImportErrors(importedFileName: string, text: string) { - const re = new RegExp(importedFileName, 'gi') - const result: SearchResultLine[] = findLinesInStringWithMatch( - text, - re - ) - return result - } + async getPositionForImportErrors(importedFileName: string, text: string) { + const re = new RegExp(importedFileName, 'gi') + const result: SearchResultLine[] = findLinesInStringWithMatch( + text, + re + ) + return result + } } \ No newline at end of file diff --git a/apps/remix-ide/src/app/plugins/parser/services/code-parser-gas-service.ts b/apps/remix-ide/src/app/plugins/parser/services/code-parser-gas-service.ts index 274466fe9c..5c1f7fb982 100644 --- a/apps/remix-ide/src/app/plugins/parser/services/code-parser-gas-service.ts +++ b/apps/remix-ide/src/app/plugins/parser/services/code-parser-gas-service.ts @@ -3,76 +3,76 @@ import { lineText } from '@remix-ui/editor' import { lastCompilationResult } from '@remixproject/plugin-api'; export default class CodeParserGasService { - plugin: CodeParser + plugin: CodeParser - constructor(plugin: CodeParser) { - this.plugin = plugin - } + constructor(plugin: CodeParser) { + this.plugin = plugin + } - async getGasEstimates(fileName: string) { - if (!fileName) { - fileName = await this.plugin.currentFile - } - if (this.plugin.nodeIndex.nodesPerFile && this.plugin.nodeIndex.nodesPerFile[fileName] && this.plugin.nodeIndex.nodesPerFile[fileName].contracts) { - const estimates: any = [] - for (const contract in this.plugin.nodeIndex.nodesPerFile[fileName].contracts) { - if (this.plugin.nodeIndex.nodesPerFile[fileName].contracts[contract].contractNodes) { - const nodes = this.plugin.nodeIndex.nodesPerFile[fileName].contracts[contract].contractNodes - for (const node of Object.values(nodes) as any[]) { - if (node.gasEstimate) { - estimates.push({ - node, - range: await this.plugin.getLineColumnOfNode(node) - }) - } - } - } + async getGasEstimates(fileName: string) { + if (!fileName) { + fileName = await this.plugin.currentFile + } + if (this.plugin.nodeIndex.nodesPerFile && this.plugin.nodeIndex.nodesPerFile[fileName] && this.plugin.nodeIndex.nodesPerFile[fileName].contracts) { + const estimates: any = [] + for (const contract in this.plugin.nodeIndex.nodesPerFile[fileName].contracts) { + if (this.plugin.nodeIndex.nodesPerFile[fileName].contracts[contract].contractNodes) { + const nodes = this.plugin.nodeIndex.nodesPerFile[fileName].contracts[contract].contractNodes + for (const node of Object.values(nodes) as any[]) { + if (node.gasEstimate) { + estimates.push({ + node, + range: await this.plugin.getLineColumnOfNode(node) + }) } - return estimates + } } - + } + return estimates } + } - async showGasEstimates() { - const showGasConfig = await this.plugin.call('config', 'getAppParameter', 'show-gas') - if(!showGasConfig) { - await this.plugin.call('editor', 'discardLineTexts') - return - } - this.plugin.currentFile = await this.plugin.call('fileManager', 'file') - // cast from the remix-plugin interface to the solidity one. Should be fixed when remix-plugin move to the remix-project repository - this.plugin.nodeIndex.nodesPerFile[this.plugin.currentFile] = await this.plugin._extractFileNodes(this.plugin.currentFile, this.plugin.compilerAbstract as unknown as lastCompilationResult) - const gasEstimates = await this.getGasEstimates(this.plugin.currentFile) + async showGasEstimates() { + const showGasConfig = await this.plugin.call('config', 'getAppParameter', 'show-gas') + if(!showGasConfig) { + await this.plugin.call('editor', 'discardLineTexts') + return + } + this.plugin.currentFile = await this.plugin.call('fileManager', 'file') + // cast from the remix-plugin interface to the solidity one. Should be fixed when remix-plugin move to the remix-project repository + this.plugin.nodeIndex.nodesPerFile[this.plugin.currentFile] = await this.plugin._extractFileNodes(this.plugin.currentFile, this.plugin.compilerAbstract as unknown as lastCompilationResult) + + const gasEstimates = await this.getGasEstimates(this.plugin.currentFile) - const friendlyNames = { - 'executionCost': 'Estimated execution cost', - 'codeDepositCost': 'Estimated code deposit cost', - 'creationCost': 'Estimated creation cost', + const friendlyNames = { + 'executionCost': 'Estimated execution cost', + 'codeDepositCost': 'Estimated code deposit cost', + 'creationCost': 'Estimated creation cost', + } + await this.plugin.call('editor', 'discardLineTexts') + if (gasEstimates) { + for (const estimate of gasEstimates) { + const linetext: lineText = { + content: Object.entries(estimate.node.gasEstimate).map(([, value]) => `${value} gas`).join(' '), + position: estimate.range, + hide: false, + className: 'text-muted small', + afterContentClassName: 'text-muted small fas fa-gas-pump pl-4', + from: 'codeParser', + hoverMessage: [{ + value: `${Object.entries(estimate.node.gasEstimate).map(([key, value]) => `${friendlyNames[key]}: ${value} gas`).join(' ')}`, + }, + ], } - await this.plugin.call('editor', 'discardLineTexts') - if (gasEstimates) { - for (const estimate of gasEstimates) { - const linetext: lineText = { - content: Object.entries(estimate.node.gasEstimate).map(([, value]) => `${value} gas`).join(' '), - position: estimate.range, - hide: false, - className: 'text-muted small', - afterContentClassName: 'text-muted small fas fa-gas-pump pl-4', - from: 'codeParser', - hoverMessage: [{ - value: `${Object.entries(estimate.node.gasEstimate).map(([key, value]) => `${friendlyNames[key]}: ${value} gas`).join(' ')}`, - }, - ], - } - this.plugin.call('editor', 'addLineText', linetext, estimate.range.fileName) + this.plugin.call('editor', 'addLineText', linetext, estimate.range.fileName) - } - } + } } + } } \ No newline at end of file diff --git a/apps/remix-ide/src/app/plugins/parser/services/code-parser-imports.ts b/apps/remix-ide/src/app/plugins/parser/services/code-parser-imports.ts index 73b162d95b..25818647d1 100644 --- a/apps/remix-ide/src/app/plugins/parser/services/code-parser-imports.ts +++ b/apps/remix-ide/src/app/plugins/parser/services/code-parser-imports.ts @@ -8,81 +8,81 @@ export type CodeParserImportsData= { } export default class CodeParserImports { - plugin: CodeParser + plugin: CodeParser - data: CodeParserImportsData = {} - constructor(plugin: CodeParser) { - this.plugin = plugin - this.init() - } + data: CodeParserImportsData = {} + constructor(plugin: CodeParser) { + this.plugin = plugin + this.init() + } - async getImports(){ - return this.data - } + async getImports(){ + return this.data + } - async init() { - // @ts-ignore - const txt = await import('raw-loader!libs/remix-ui/editor/src/lib/providers/completion/contracts/contracts.txt') - this.data.modules = txt.default.split('\n') - .filter(x => x !== '') - .map(x => x.replace('./node_modules/', '')) - .filter(x => { - if(x.includes('@openzeppelin')) { - return !x.includes('mock') - }else{ - return true - } - }) + async init() { + // @ts-ignore + const txt = await import('raw-loader!libs/remix-ui/editor/src/lib/providers/completion/contracts/contracts.txt') + this.data.modules = txt.default.split('\n') + .filter(x => x !== '') + .map(x => x.replace('./node_modules/', '')) + .filter(x => { + if(x.includes('@openzeppelin')) { + return !x.includes('mock') + }else{ + return true + } + }) - // get unique first words of the values in the array - this.data.packages = [...new Set(this.data.modules.map(x => x.split('/')[0]))] - } + // get unique first words of the values in the array + this.data.packages = [...new Set(this.data.modules.map(x => x.split('/')[0]))] + } - setFileTree = async () => { - this.data.files = await this.getDirectory('/') - this.data.files = this.data.files.filter(x => x.endsWith('.sol') && !x.startsWith('.deps') && !x.startsWith('.git')) - } + setFileTree = async () => { + this.data.files = await this.getDirectory('/') + this.data.files = this.data.files.filter(x => x.endsWith('.sol') && !x.startsWith('.deps') && !x.startsWith('.git')) + } - getDirectory = async (dir: string) => { - let result = [] - let files = {} - try { - if (await this.plugin.call('fileManager', 'exists', dir)) { - files = await this.plugin.call('fileManager', 'readdir', dir) - } - } catch (e) {} + getDirectory = async (dir: string) => { + let result = [] + let files = {} + try { + if (await this.plugin.call('fileManager', 'exists', dir)) { + files = await this.plugin.call('fileManager', 'readdir', dir) + } + } catch (e) {} - const fileArray = this.normalize(files) - for (const fi of fileArray) { - if (fi) { - const type = fi.data.isDirectory - if (type === true) { - result = [...result, ...(await this.getDirectory(`${fi.filename}`))] - } else { - result = [...result, fi.filename] - } - } + const fileArray = this.normalize(files) + for (const fi of fileArray) { + if (fi) { + const type = fi.data.isDirectory + if (type === true) { + result = [...result, ...(await this.getDirectory(`${fi.filename}`))] + } else { + result = [...result, fi.filename] } - return result + } } + return result + } - normalize = filesList => { - const folders = [] - const files = [] - Object.keys(filesList || {}).forEach(key => { - if (filesList[key].isDirectory) { - folders.push({ - filename: key, - data: filesList[key] - }) - } else { - files.push({ - filename: key, - data: filesList[key] - }) - } + normalize = filesList => { + const folders = [] + const files = [] + Object.keys(filesList || {}).forEach(key => { + if (filesList[key].isDirectory) { + folders.push({ + filename: key, + data: filesList[key] }) - return [...folders, ...files] - } + } else { + files.push({ + filename: key, + data: filesList[key] + }) + } + }) + return [...folders, ...files] + } } \ No newline at end of file diff --git a/apps/remix-ide/src/app/plugins/parser/types/antlr-types.ts b/apps/remix-ide/src/app/plugins/parser/types/antlr-types.ts index 3e2675c43a..e0e1854b64 100644 --- a/apps/remix-ide/src/app/plugins/parser/types/antlr-types.ts +++ b/apps/remix-ide/src/app/plugins/parser/types/antlr-types.ts @@ -596,132 +596,132 @@ type ASTTypeMap = ASTMap export const astNodeTypes = [ - 'SourceUnit', - 'PragmaDirective', - 'ImportDirective', - 'ContractDefinition', - 'InheritanceSpecifier', - 'StateVariableDeclaration', - 'UsingForDeclaration', - 'StructDefinition', - 'ModifierDefinition', - 'ModifierInvocation', - 'FunctionDefinition', - 'EventDefinition', - 'CustomErrorDefinition', - 'RevertStatement', - 'EnumValue', - 'EnumDefinition', - 'VariableDeclaration', - 'UserDefinedTypeName', - 'Mapping', - 'ArrayTypeName', - 'FunctionTypeName', - 'Block', - 'ExpressionStatement', - 'IfStatement', - 'WhileStatement', - 'ForStatement', - 'InlineAssemblyStatement', - 'DoWhileStatement', - 'ContinueStatement', - 'Break', - 'Continue', - 'BreakStatement', - 'ReturnStatement', - 'EmitStatement', - 'ThrowStatement', - 'VariableDeclarationStatement', - 'ElementaryTypeName', - 'FunctionCall', - 'AssemblyBlock', - 'AssemblyCall', - 'AssemblyLocalDefinition', - 'AssemblyAssignment', - 'AssemblyStackAssignment', - 'LabelDefinition', - 'AssemblySwitch', - 'AssemblyCase', - 'AssemblyFunctionDefinition', - 'AssemblyFunctionReturns', - 'AssemblyFor', - 'AssemblyIf', - 'SubAssembly', - 'TupleExpression', - 'NameValueExpression', - 'BooleanLiteral', - 'NumberLiteral', - 'Identifier', - 'BinaryOperation', - 'UnaryOperation', - 'NewExpression', - 'Conditional', - 'StringLiteral', - 'HexLiteral', - 'HexNumber', - 'DecimalNumber', - 'MemberAccess', - 'IndexAccess', - 'IndexRangeAccess', - 'NameValueList', - 'UncheckedStatement', - 'TryStatement', - 'CatchClause', - 'FileLevelConstant', - 'AssemblyMemberAccess', - 'TypeDefinition', - 'InvalidNode' + 'SourceUnit', + 'PragmaDirective', + 'ImportDirective', + 'ContractDefinition', + 'InheritanceSpecifier', + 'StateVariableDeclaration', + 'UsingForDeclaration', + 'StructDefinition', + 'ModifierDefinition', + 'ModifierInvocation', + 'FunctionDefinition', + 'EventDefinition', + 'CustomErrorDefinition', + 'RevertStatement', + 'EnumValue', + 'EnumDefinition', + 'VariableDeclaration', + 'UserDefinedTypeName', + 'Mapping', + 'ArrayTypeName', + 'FunctionTypeName', + 'Block', + 'ExpressionStatement', + 'IfStatement', + 'WhileStatement', + 'ForStatement', + 'InlineAssemblyStatement', + 'DoWhileStatement', + 'ContinueStatement', + 'Break', + 'Continue', + 'BreakStatement', + 'ReturnStatement', + 'EmitStatement', + 'ThrowStatement', + 'VariableDeclarationStatement', + 'ElementaryTypeName', + 'FunctionCall', + 'AssemblyBlock', + 'AssemblyCall', + 'AssemblyLocalDefinition', + 'AssemblyAssignment', + 'AssemblyStackAssignment', + 'LabelDefinition', + 'AssemblySwitch', + 'AssemblyCase', + 'AssemblyFunctionDefinition', + 'AssemblyFunctionReturns', + 'AssemblyFor', + 'AssemblyIf', + 'SubAssembly', + 'TupleExpression', + 'NameValueExpression', + 'BooleanLiteral', + 'NumberLiteral', + 'Identifier', + 'BinaryOperation', + 'UnaryOperation', + 'NewExpression', + 'Conditional', + 'StringLiteral', + 'HexLiteral', + 'HexNumber', + 'DecimalNumber', + 'MemberAccess', + 'IndexAccess', + 'IndexRangeAccess', + 'NameValueList', + 'UncheckedStatement', + 'TryStatement', + 'CatchClause', + 'FileLevelConstant', + 'AssemblyMemberAccess', + 'TypeDefinition', + 'InvalidNode' ] as const export const binaryOpValues = [ - '+', - '-', - '*', - '/', - '**', - '%', - '<<', - '>>', - '&&', - '||', - ',,', - '&', - ',', - '^', - '<', - '>', - '<=', - '>=', - '==', - '!=', - '=', - ',=', - '^=', - '&=', - '<<=', - '>>=', - '+=', - '-=', - '*=', - '/=', - '%=', - '|', - '|=', + '+', + '-', + '*', + '/', + '**', + '%', + '<<', + '>>', + '&&', + '||', + ',,', + '&', + ',', + '^', + '<', + '>', + '<=', + '>=', + '==', + '!=', + '=', + ',=', + '^=', + '&=', + '<<=', + '>>=', + '+=', + '-=', + '*=', + '/=', + '%=', + '|', + '|=', ] as const export type BinOp = typeof binaryOpValues[number] export const unaryOpValues = [ - '-', - '+', - '++', - '--', - '~', - 'after', - 'delete', - '!', + '-', + '+', + '++', + '--', + '~', + 'after', + 'delete', + '!', ] as const export type UnaryOp = typeof unaryOpValues[number] diff --git a/apps/remix-ide/src/app/plugins/permission-handler-plugin.tsx b/apps/remix-ide/src/app/plugins/permission-handler-plugin.tsx index c89b087eed..0395c26452 100644 --- a/apps/remix-ide/src/app/plugins/permission-handler-plugin.tsx +++ b/apps/remix-ide/src/app/plugins/permission-handler-plugin.tsx @@ -6,73 +6,73 @@ import { PermissionHandlerDialog, PermissionHandlerValue } from '@remix-ui/permi import { Profile } from '@remixproject/plugin-utils' const profile = { - name: 'permissionhandler', - displayName: 'permissionhandler', - description: 'Plugin to handle permissions', - methods: ['askPermission'] + name: 'permissionhandler', + displayName: 'permissionhandler', + description: 'Plugin to handle permissions', + methods: ['askPermission'] } export class PermissionHandlerPlugin extends Plugin { - permissions: any - sessionPermissions: any - currentVersion: number - fallbackMemory: boolean - constructor() { - super(profile) - this.fallbackMemory = false - this.permissions = this._getFromLocal() - this.sessionPermissions = {} - this.currentVersion = 1 - // here we remove the old permissions saved before adding 'permissionVersion' - // since with v1 the structure has been changed because of new engine ^0.2.0-alpha.6 changes - if (!localStorage.getItem('permissionVersion')) { - localStorage.setItem('plugins/permissions', '') - localStorage.setItem('permissionVersion', this.currentVersion.toString()) - } + permissions: any + sessionPermissions: any + currentVersion: number + fallbackMemory: boolean + constructor() { + super(profile) + this.fallbackMemory = false + this.permissions = this._getFromLocal() + this.sessionPermissions = {} + this.currentVersion = 1 + // here we remove the old permissions saved before adding 'permissionVersion' + // since with v1 the structure has been changed because of new engine ^0.2.0-alpha.6 changes + if (!localStorage.getItem('permissionVersion')) { + localStorage.setItem('plugins/permissions', '') + localStorage.setItem('permissionVersion', this.currentVersion.toString()) } + } - _getFromLocal() { - if (this.fallbackMemory) return this.permissions - const permission = localStorage.getItem('plugins/permissions') - return permission ? JSON.parse(permission) : {} - } + _getFromLocal() { + if (this.fallbackMemory) return this.permissions + const permission = localStorage.getItem('plugins/permissions') + return permission ? JSON.parse(permission) : {} + } - persistPermissions() { - const permissions = JSON.stringify(this.permissions) - try { - localStorage.setItem('plugins/permissions', permissions) - } catch (e) { - this.fallbackMemory = true - console.log(e) - } + persistPermissions() { + const permissions = JSON.stringify(this.permissions) + try { + localStorage.setItem('plugins/permissions', permissions) + } catch (e) { + this.fallbackMemory = true + console.log(e) } + } - switchMode (from: Profile, to: Profile, method: string, set: boolean, sensitiveCall: boolean) { - if (sensitiveCall) { - set - ? this.sessionPermissions[to.name][method][from.name] = {} - : delete this.sessionPermissions[to.name][method][from.name] - } else { - set - ? this.permissions[to.name][method][from.name] = {} - : delete this.permissions[to.name][method][from.name] - } - } + switchMode (from: Profile, to: Profile, method: string, set: boolean, sensitiveCall: boolean) { + if (sensitiveCall) { + set + ? this.sessionPermissions[to.name][method][from.name] = {} + : delete this.sessionPermissions[to.name][method][from.name] + } else { + set + ? this.permissions[to.name][method][from.name] = {} + : delete this.permissions[to.name][method][from.name] + } + } - clear() { - localStorage.removeItem('plugins/permissions') - this.permissions = this._getFromLocal() - this.sessionPermissions = {} - } + clear() { + localStorage.removeItem('plugins/permissions') + this.permissions = this._getFromLocal() + this.sessionPermissions = {} + } - notAllowWarning(from: Profile, to: Profile, method: string) { - return `${from.displayName || from.name} is not allowed to call ${method} method of ${to.displayName || to.name}.` - } + notAllowWarning(from: Profile, to: Profile, method: string) { + return `${from.displayName || from.name} is not allowed to call ${method} method of ${to.displayName || to.name}.` + } - async getTheme() { - return (await this.call('theme', 'currentTheme')).quality - } + async getTheme() { + return (await this.call('theme', 'currentTheme')).quality + } - /** + /** * Check if a plugin has the permission to call another plugin and askPermission if needed * @param {PluginProfile} from the profile of the plugin that make the call * @param {ModuleProfile} to The profile of the module that receive the call @@ -80,95 +80,95 @@ export class PermissionHandlerPlugin extends Plugin { * @param {string} message from the caller plugin to add more details if needed * @returns {Promise} */ - async askPermission(from: Profile, to: Profile, method: string, message: string, sensitiveCall: boolean) { - try { - if (sensitiveCall) { - if (!this.sessionPermissions[to.name]) this.sessionPermissions[to.name] = {} - if (!this.sessionPermissions[to.name][method]) this.sessionPermissions[to.name][method] = {} - if (!this.sessionPermissions[to.name][method][from.name]) return this.openPermission(from, to, method, message, sensitiveCall) - } else { - this.permissions = this._getFromLocal() - if (!this.permissions[to.name]) this.permissions[to.name] = {} - if (!this.permissions[to.name][method]) this.permissions[to.name][method] = {} - if (!this.permissions[to.name][method][from.name]) return this.openPermission(from, to, method, message, sensitiveCall) - } + async askPermission(from: Profile, to: Profile, method: string, message: string, sensitiveCall: boolean) { + try { + if (sensitiveCall) { + if (!this.sessionPermissions[to.name]) this.sessionPermissions[to.name] = {} + if (!this.sessionPermissions[to.name][method]) this.sessionPermissions[to.name][method] = {} + if (!this.sessionPermissions[to.name][method][from.name]) return this.openPermission(from, to, method, message, sensitiveCall) + } else { + this.permissions = this._getFromLocal() + if (!this.permissions[to.name]) this.permissions[to.name] = {} + if (!this.permissions[to.name][method]) this.permissions[to.name][method] = {} + if (!this.permissions[to.name][method][from.name]) return this.openPermission(from, to, method, message, sensitiveCall) + } - const { allow, hash } = sensitiveCall ? this.sessionPermissions[to.name][method][from.name] : this.permissions[to.name][method][from.name] - if (!allow) { - const warning = this.notAllowWarning(from, to, method) - this.call('notification', 'toast', warning) - return false - } - return hash === from.hash - ? true // Allow - : await this.openPermission(from, to, method, message, sensitiveCall) - } catch (err) { - throw new Error(err) - } + const { allow, hash } = sensitiveCall ? this.sessionPermissions[to.name][method][from.name] : this.permissions[to.name][method][from.name] + if (!allow) { + const warning = this.notAllowWarning(from, to, method) + this.call('notification', 'toast', warning) + return false + } + return hash === from.hash + ? true // Allow + : await this.openPermission(from, to, method, message, sensitiveCall) + } catch (err) { + throw new Error(err) } + } - async openPermission(from: Profile, to: Profile, method: string, message: string, sensitiveCall: boolean) { - let remember + async openPermission(from: Profile, to: Profile, method: string, message: string, sensitiveCall: boolean) { + let remember + if (sensitiveCall) { + remember = this.sessionPermissions[to.name][method][from.name] + } else { + remember = this.permissions[to.name][method][from.name] + } + const value: PermissionHandlerValue = { + from, + to, + method, + message, + remember, + sensitiveCall + } + const modal: AppModal = { + id: 'PermissionHandler', + title: , + message: , + okLabel: , + cancelLabel: + } + + const result = await this.call('notification', 'modal', modal) + return new Promise((resolve, reject) => { + if (result) { if (sensitiveCall) { - remember = this.sessionPermissions[to.name][method][from.name] + if (this.sessionPermissions[to.name][method][from.name]) { + this.sessionPermissions[to.name][method][from.name] = { + allow: true, + hash: from.hash + } + } } else { - remember = this.permissions[to.name][method][from.name] - } - const value: PermissionHandlerValue = { - from, - to, - method, - message, - remember, - sensitiveCall - } - const modal: AppModal = { - id: 'PermissionHandler', - title: , - message: , - okLabel: , - cancelLabel: + if (this.permissions[to.name][method][from.name]) { + this.permissions[to.name][method][from.name] = { + allow: true, + hash: from.hash + } + this.persistPermissions() + } } - - const result = await this.call('notification', 'modal', modal) - return new Promise((resolve, reject) => { - if (result) { - if (sensitiveCall) { - if (this.sessionPermissions[to.name][method][from.name]) { - this.sessionPermissions[to.name][method][from.name] = { - allow: true, - hash: from.hash - } - } - } else { - if (this.permissions[to.name][method][from.name]) { - this.permissions[to.name][method][from.name] = { - allow: true, - hash: from.hash - } - this.persistPermissions() - } - } - resolve(true) - } else { - if (sensitiveCall) { - if (this.sessionPermissions[to.name][method][from.name]) { - this.sessionPermissions[to.name][method][from.name] = { - allow: false, - hash: from.hash - } - } - } else { - if (this.permissions[to.name][method][from.name]) { - this.permissions[to.name][method][from.name] = { - allow: false, - hash: from.hash - } - this.persistPermissions() - } - } - reject(this.notAllowWarning(from, to, method)) + resolve(true) + } else { + if (sensitiveCall) { + if (this.sessionPermissions[to.name][method][from.name]) { + this.sessionPermissions[to.name][method][from.name] = { + allow: false, + hash: from.hash } - }) - } + } + } else { + if (this.permissions[to.name][method][from.name]) { + this.permissions[to.name][method][from.name] = { + allow: false, + hash: from.hash + } + this.persistPermissions() + } + } + reject(this.notAllowWarning(from, to, method)) + } + }) + } } diff --git a/apps/remix-ide/src/app/plugins/remixd-handle.tsx b/apps/remix-ide/src/app/plugins/remixd-handle.tsx index a39c52c3d3..2163dbd6d1 100644 --- a/apps/remix-ide/src/app/plugins/remixd-handle.tsx +++ b/apps/remix-ide/src/app/plugins/remixd-handle.tsx @@ -11,179 +11,179 @@ import { AppModal, AlertModal } from '@remix-ui/app' const LOCALHOST = ' - connect to localhost - ' const profile = { - name: 'remixd', - displayName: 'RemixD', - url: 'ws://127.0.0.1:65520', - methods: ['folderIsReadOnly', 'resolveDirectory', 'get', 'exists', 'isFile', 'set', 'rename', 'remove', 'isDirectory', 'list', 'createDir'], - events: [], - description: 'Using Remixd daemon, allow to access file system', - kind: 'other', - version: packageJson.version, - repo: "https://github.com/ethereum/remix-project/tree/master/libs/remixd", - maintainedBy: "Remix", - documentation: "https://remix-ide.readthedocs.io/en/latest/remixd.html", - authorContact: "" + name: 'remixd', + displayName: 'RemixD', + url: 'ws://127.0.0.1:65520', + methods: ['folderIsReadOnly', 'resolveDirectory', 'get', 'exists', 'isFile', 'set', 'rename', 'remove', 'isDirectory', 'list', 'createDir'], + events: [], + description: 'Using Remixd daemon, allow to access file system', + kind: 'other', + version: packageJson.version, + repo: "https://github.com/ethereum/remix-project/tree/master/libs/remixd", + maintainedBy: "Remix", + documentation: "https://remix-ide.readthedocs.io/en/latest/remixd.html", + authorContact: "" } export class RemixdHandle extends WebsocketPlugin { - localhostProvider: any - appManager: PluginManager - dependentPlugins: Array - constructor(localhostProvider, appManager) { - super(profile) - this.localhostProvider = localhostProvider - this.appManager = appManager - this.dependentPlugins = ['hardhat', 'truffle', 'slither', 'foundry'] - } + localhostProvider: any + appManager: PluginManager + dependentPlugins: Array + constructor(localhostProvider, appManager) { + super(profile) + this.localhostProvider = localhostProvider + this.appManager = appManager + this.dependentPlugins = ['hardhat', 'truffle', 'slither', 'foundry'] + } - async deactivate() { - for (const plugin of this.dependentPlugins) { - if (await this.appManager.isActive(plugin)) { - await this.appManager.deactivatePlugin(plugin) - } - } - if (super.socket) super.deactivate() - // this.appManager.deactivatePlugin('git') // plugin call doesn't work.. see issue https://github.com/ethereum/remix-plugin/issues/342 - this.localhostProvider.close((error) => { - if (error) console.log(error) - }) + async deactivate() { + for (const plugin of this.dependentPlugins) { + if (await this.appManager.isActive(plugin)) { + await this.appManager.deactivatePlugin(plugin) + } } + if (super.socket) super.deactivate() + // this.appManager.deactivatePlugin('git') // plugin call doesn't work.. see issue https://github.com/ethereum/remix-plugin/issues/342 + this.localhostProvider.close((error) => { + if (error) console.log(error) + }) + } + + async activate() { + this.connectToLocalhost() + return true + } - async activate() { - this.connectToLocalhost() - return true + async canceled() { + for (const plugin of this.dependentPlugins) { + if (await this.appManager.isActive(plugin)) { + await this.appManager.deactivatePlugin(plugin) + } } - async canceled() { - for (const plugin of this.dependentPlugins) { - if (await this.appManager.isActive(plugin)) { - await this.appManager.deactivatePlugin(plugin) - } - } + await this.appManager.deactivatePlugin('remixd') - await this.appManager.deactivatePlugin('remixd') + } + async callPluginMethod(key: string, payload?: any[]) { + if (this.socket.readyState !== this.socket.OPEN) { + console.log(`${this.profile.name} connection is not opened.`) + return false } + return super.callPluginMethod(key, payload) + } - async callPluginMethod(key: string, payload?: any[]) { - if (this.socket.readyState !== this.socket.OPEN) { - console.log(`${this.profile.name} connection is not opened.`) - return false - } - return super.callPluginMethod(key, payload) - } - - /** + /** * connect to localhost if no connection and render the explorer * disconnect from localhost if connected and remove the explorer * * @param {String} txHash - hash of the transaction */ - async connectToLocalhost() { - const connection = async (error?: any) => { - if (error) { - console.log(error) - const alert: AlertModal = { - id: 'connectionAlert', - message: 'Cannot connect to the remixd daemon. Please make sure you have the remixd running in the background.' - } - this.call('notification', 'alert', alert) - this.canceled() - } else { - const intervalId = setInterval(() => { - if (!this.socket || (this.socket && this.socket.readyState === 3)) { // 3 means connection closed - clearInterval(intervalId) - const alert: AlertModal = { - id: 'connectionAlert', - message: 'Connection to remixd terminated. Please make sure remixd is still running in the background.' - } - this.call('notification', 'alert', alert) - this.canceled() - } - }, 3000) - this.localhostProvider.init(() => { - this.call('filePanel', 'setWorkspace', { name: LOCALHOST, isLocalhost: true }, true) - }); - for (const plugin of this.dependentPlugins) { - await this.appManager.activatePlugin(plugin) - } - } + async connectToLocalhost() { + const connection = async (error?: any) => { + if (error) { + console.log(error) + const alert: AlertModal = { + id: 'connectionAlert', + message: 'Cannot connect to the remixd daemon. Please make sure you have the remixd running in the background.' } - if (this.localhostProvider.isConnected()) { - this.deactivate() - } else if (!isElectron()) { - // warn the user only if he/she is in the browser context - const mod: AppModal = { - id: 'remixdConnect', - title: 'Access file system using remixd', - message: remixdDialog(), - okLabel: 'Connect', - cancelLabel: 'Cancel', - } - const result = await this.call('notification', 'modal', mod) - if (result) { - try { - this.localhostProvider.preInit() - super.activate() - setTimeout(() => { - if (!this.socket || (this.socket && this.socket.readyState === 3)) { // 3 means connection closed - connection(new Error('Connection with daemon failed.')) - } else { - connection() - } - }, 3000) - } catch (error) { - connection(error) - } - } - else { - await this.canceled() + this.call('notification', 'alert', alert) + this.canceled() + } else { + const intervalId = setInterval(() => { + if (!this.socket || (this.socket && this.socket.readyState === 3)) { // 3 means connection closed + clearInterval(intervalId) + const alert: AlertModal = { + id: 'connectionAlert', + message: 'Connection to remixd terminated. Please make sure remixd is still running in the background.' } - } else { - try { - super.activate() - setTimeout(() => { connection() }, 2000) - } catch (error) { - connection(error) + this.call('notification', 'alert', alert) + this.canceled() + } + }, 3000) + this.localhostProvider.init(() => { + this.call('filePanel', 'setWorkspace', { name: LOCALHOST, isLocalhost: true }, true) + }); + for (const plugin of this.dependentPlugins) { + await this.appManager.activatePlugin(plugin) + } + } + } + if (this.localhostProvider.isConnected()) { + this.deactivate() + } else if (!isElectron()) { + // warn the user only if he/she is in the browser context + const mod: AppModal = { + id: 'remixdConnect', + title: 'Access file system using remixd', + message: remixdDialog(), + okLabel: 'Connect', + cancelLabel: 'Cancel', + } + const result = await this.call('notification', 'modal', mod) + if (result) { + try { + this.localhostProvider.preInit() + super.activate() + setTimeout(() => { + if (!this.socket || (this.socket && this.socket.readyState === 3)) { // 3 means connection closed + connection(new Error('Connection with daemon failed.')) + } else { + connection() } + }, 3000) + } catch (error) { + connection(error) } + } + else { + await this.canceled() + } + } else { + try { + super.activate() + setTimeout(() => { connection() }, 2000) + } catch (error) { + connection(error) + } } + } } function remixdDialog() { - const commandText = 'remixd' - const fullCommandText = 'remixd -s -u ' - return (<> -
-
+ const commandText = 'remixd' + const fullCommandText = 'remixd -s -u ' + return (<> +
+
Access your local file system from Remix IDE using Remixd NPM package. -
-
+
+
Remixd documentation. -
-
+
+
The remixd command is: -
{commandText} -
-
+
{commandText} +
+
The remixd command without options uses the terminal's current directory as the shared directory and the shared Remix domain can only be https://remix.ethereum.org, https://remix-alpha.ethereum.org, or https://remix-beta.ethereum.org -
-
+
+
Example command with flags:
- {fullCommandText} -
-
+ {fullCommandText} +
+
For info about ports, see Remixd ports usage -
-
+
+
This feature is still in Alpha. We recommend to keep a backup of the shared folder. -
-
-
+
+
+
Before using, make sure remixd version is latest i.e. v{remixdVersion} -

Read here how to update it -
-
-
- ) +

Read here how to update it + +
+
+ ) } diff --git a/apps/remix-ide/src/app/plugins/solidity-script.tsx b/apps/remix-ide/src/app/plugins/solidity-script.tsx index 88c3b1e962..00f4542544 100644 --- a/apps/remix-ide/src/app/plugins/solidity-script.tsx +++ b/apps/remix-ide/src/app/plugins/solidity-script.tsx @@ -6,25 +6,25 @@ import { TransactionConfig } from 'web3-core' const _paq = window._paq = window._paq || [] //eslint-disable-line const profile = { - name: 'solidity-script', - displayName: 'solidity-script', - description: 'solidity-script', - methods: ['execute'] + name: 'solidity-script', + displayName: 'solidity-script', + description: 'solidity-script', + methods: ['execute'] } export class SolidityScript extends Plugin { - constructor () { - super(profile) - } + constructor () { + super(profile) + } - async execute (path: string, functionName: string = 'run') { - _paq.push(['trackEvent', 'SolidityScript', 'execute', 'script']) - this.call('terminal', 'log', `Running free function '${functionName}' from ${path}...`) - let content = await this.call('fileManager', 'readFile', path) - const params = await this.call('solidity', 'getCompilerParameters') + async execute (path: string, functionName: string = 'run') { + _paq.push(['trackEvent', 'SolidityScript', 'execute', 'script']) + this.call('terminal', 'log', `Running free function '${functionName}' from ${path}...`) + let content = await this.call('fileManager', 'readFile', path) + const params = await this.call('solidity', 'getCompilerParameters') - content = ` + content = ` // SPDX-License-Identifier: GPL-3.0 pragma solidity >=0.7.1; @@ -38,69 +38,69 @@ export class SolidityScript extends Plugin { ${functionName}(); } }` - const targets = { 'script.sol': { content } } + const targets = { 'script.sol': { content } } - // compile - const compilation = await compile(targets, params, async (url, cb) => { - await this.call('contentImport', 'resolveAndSave', url).then((result) => cb(null, result)).catch((error) => cb(error.message)) - }) + // compile + const compilation = await compile(targets, params, async (url, cb) => { + await this.call('contentImport', 'resolveAndSave', url).then((result) => cb(null, result)).catch((error) => cb(error.message)) + }) - if (compilation.data.error) { - this.call('terminal', 'log', compilation.data.error.formattedMessage) - } - if (compilation.data.errors && compilation.data.errors.length > 0) { - compilation.data.errors.map((error) => { - this.call('terminal', 'log', error.formattedMessage) - }) - } + if (compilation.data.error) { + this.call('terminal', 'log', compilation.data.error.formattedMessage) + } + if (compilation.data.errors && compilation.data.errors.length > 0) { + compilation.data.errors.map((error) => { + this.call('terminal', 'log', error.formattedMessage) + }) + } - // get the contract - const contract = compilation.getContract('SolidityScript') - if (!contract) { - console.log('compilation failed') - return - } - const bytecode = '0x' + contract.object.evm.bytecode.object - const web3 = await this.call('blockchain', 'web3VM') - const accounts = await this.call('blockchain', 'getAccounts') - if (!accounts || accounts.length === 0) { - throw new Error('no account available') - } + // get the contract + const contract = compilation.getContract('SolidityScript') + if (!contract) { + console.log('compilation failed') + return + } + const bytecode = '0x' + contract.object.evm.bytecode.object + const web3 = await this.call('blockchain', 'web3VM') + const accounts = await this.call('blockchain', 'getAccounts') + if (!accounts || accounts.length === 0) { + throw new Error('no account available') + } - // deploy the contract - let tx: TransactionConfig = { - from: accounts[0], - data: bytecode - } - const receipt = await web3.eth.sendTransaction(tx) - tx = { - from: accounts[0], - to: receipt.contractAddress, - data: '0x69d4394b' // function remixRun() public - } - const receiptCall = await web3.eth.sendTransaction(tx) + // deploy the contract + let tx: TransactionConfig = { + from: accounts[0], + data: bytecode + } + const receipt = await web3.eth.sendTransaction(tx) + tx = { + from: accounts[0], + to: receipt.contractAddress, + data: '0x69d4394b' // function remixRun() public + } + const receiptCall = await web3.eth.sendTransaction(tx) - const hhlogs = await web3.eth.getHHLogsForTx(receiptCall.transactionHash) + const hhlogs = await web3.eth.getHHLogsForTx(receiptCall.transactionHash) - if (hhlogs && hhlogs.length) { - const finalLogs =
console.log:
- { - hhlogs.map((log) => { - let formattedLog - // Hardhat implements the same formatting options that can be found in Node.js' console.log, - // which in turn uses util.format: https://nodejs.org/dist/latest-v12.x/docs/api/util.html#util_util_format_format_args - // For example: console.log("Name: %s, Age: %d", remix, 6) will log 'Name: remix, Age: 6' - // We check first arg to determine if 'util.format' is needed - if (typeof log[0] === 'string' && (log[0].includes('%s') || log[0].includes('%d'))) { - formattedLog = format(log[0], ...log.slice(1)) - } else { - formattedLog = log.join(' ') - } - return
{formattedLog}
- })} -
- _paq.push(['trackEvent', 'udapp', 'hardhat', 'console.log']) - this.call('terminal', 'logHtml', finalLogs) - } - } + if (hhlogs && hhlogs.length) { + const finalLogs =
console.log:
+ { + hhlogs.map((log) => { + let formattedLog + // Hardhat implements the same formatting options that can be found in Node.js' console.log, + // which in turn uses util.format: https://nodejs.org/dist/latest-v12.x/docs/api/util.html#util_util_format_format_args + // For example: console.log("Name: %s, Age: %d", remix, 6) will log 'Name: remix, Age: 6' + // We check first arg to determine if 'util.format' is needed + if (typeof log[0] === 'string' && (log[0].includes('%s') || log[0].includes('%d'))) { + formattedLog = format(log[0], ...log.slice(1)) + } else { + formattedLog = log.join(' ') + } + return
{formattedLog}
+ })} +
+ _paq.push(['trackEvent', 'udapp', 'hardhat', 'console.log']) + this.call('terminal', 'logHtml', finalLogs) + } + } } diff --git a/apps/remix-ide/src/app/plugins/solidity-umlgen.tsx b/apps/remix-ide/src/app/plugins/solidity-umlgen.tsx index 66e4ecbc87..b87a2cfe1c 100644 --- a/apps/remix-ide/src/app/plugins/solidity-umlgen.tsx +++ b/apps/remix-ide/src/app/plugins/solidity-umlgen.tsx @@ -16,39 +16,39 @@ const parser = (window as any).SolidityParser const _paq = window._paq = window._paq || [] const profile = { - name: 'solidityumlgen', - displayName: 'Solidity UML Generator', - description: 'Generates UML diagram in svg format from last compiled contract', - location: 'mainPanel', - methods: ['showUmlDiagram', 'generateUml', 'generateCustomAction'], - events: [], + name: 'solidityumlgen', + displayName: 'Solidity UML Generator', + description: 'Generates UML diagram in svg format from last compiled contract', + location: 'mainPanel', + methods: ['showUmlDiagram', 'generateUml', 'generateCustomAction'], + events: [], } const themeCollection = [ - { themeName: 'HackerOwl', backgroundColor: '#011628', textColor: '#babbcc', - shapeColor: '#8694a1',fillColor: '#011C32'}, - { themeName: 'Cerulean', backgroundColor: '#ffffff', textColor: '#343a40', - shapeColor: '#343a40',fillColor: '#f8f9fa'}, - { themeName: 'Cyborg', backgroundColor: '#060606', textColor: '#adafae', - shapeColor: '#adafae', fillColor: '#222222'}, - { themeName: 'Dark', backgroundColor: '#222336', textColor: '#babbcc', - shapeColor: '#babbcc',fillColor: '#2a2c3f'}, - { themeName: 'Flatly', backgroundColor: '#ffffff', textColor: '#343a40', - shapeColor: '#7b8a8b',fillColor: '#ffffff'}, - { themeName: 'Black', backgroundColor: '#1a1a1a', textColor: '#babbcc', - shapeColor: '#b5b4bc',fillColor: '#1f2020'}, - { themeName: 'Light', backgroundColor: '#eef1f6', textColor: '#3b445e', - shapeColor: '#343a40',fillColor: '#ffffff'}, - { themeName: 'Midcentury', backgroundColor: '#DBE2E0', textColor: '#11556c', - shapeColor: '#343a40',fillColor: '#eeede9'}, - { themeName: 'Spacelab', backgroundColor: '#ffffff', textColor: '#343a40', - shapeColor: '#333333', fillColor: '#eeeeee'}, - { themeName: 'Candy', backgroundColor: '#d5efff', textColor: '#11556c', - shapeColor: '#343a40',fillColor: '#fbe7f8' }, - { themeName: 'Violet', backgroundColor: '#f1eef6', textColor: '#3b445e', - shapeColor: '#343a40',fillColor: '#f8fafe' }, - { themeName: 'Unicorn', backgroundColor: '#f1eef6', textColor: '#343a40', - shapeColor: '#343a40',fillColor: '#f8fafe' }, + { themeName: 'HackerOwl', backgroundColor: '#011628', textColor: '#babbcc', + shapeColor: '#8694a1',fillColor: '#011C32'}, + { themeName: 'Cerulean', backgroundColor: '#ffffff', textColor: '#343a40', + shapeColor: '#343a40',fillColor: '#f8f9fa'}, + { themeName: 'Cyborg', backgroundColor: '#060606', textColor: '#adafae', + shapeColor: '#adafae', fillColor: '#222222'}, + { themeName: 'Dark', backgroundColor: '#222336', textColor: '#babbcc', + shapeColor: '#babbcc',fillColor: '#2a2c3f'}, + { themeName: 'Flatly', backgroundColor: '#ffffff', textColor: '#343a40', + shapeColor: '#7b8a8b',fillColor: '#ffffff'}, + { themeName: 'Black', backgroundColor: '#1a1a1a', textColor: '#babbcc', + shapeColor: '#b5b4bc',fillColor: '#1f2020'}, + { themeName: 'Light', backgroundColor: '#eef1f6', textColor: '#3b445e', + shapeColor: '#343a40',fillColor: '#ffffff'}, + { themeName: 'Midcentury', backgroundColor: '#DBE2E0', textColor: '#11556c', + shapeColor: '#343a40',fillColor: '#eeede9'}, + { themeName: 'Spacelab', backgroundColor: '#ffffff', textColor: '#343a40', + shapeColor: '#333333', fillColor: '#eeeeee'}, + { themeName: 'Candy', backgroundColor: '#d5efff', textColor: '#11556c', + shapeColor: '#343a40',fillColor: '#fbe7f8' }, + { themeName: 'Violet', backgroundColor: '#f1eef6', textColor: '#3b445e', + shapeColor: '#343a40',fillColor: '#f8fafe' }, + { themeName: 'Unicorn', backgroundColor: '#f1eef6', textColor: '#343a40', + shapeColor: '#343a40',fillColor: '#f8fafe' }, ] /** @@ -57,182 +57,182 @@ const themeCollection = [ */ export class SolidityUmlGen extends ViewPlugin implements ISolidityUmlGen { - element: HTMLDivElement - currentFile: string - svgPayload: string - updatedSvg: string - currentlySelectedTheme: string - themeName: string - themeDark: string - loading: boolean - themeCollection: ThemeSummary[] - activeTheme: ThemeSummary - triggerGenerateUml: boolean - umlClasses: UmlClass[] = [] - - appManager: RemixAppManager - dispatch: React.Dispatch = () => {} - constructor(appManager: RemixAppManager) { - super(profile) - this.currentFile = '' - this.svgPayload = '' - this.updatedSvg = '' - this.loading = false - this.currentlySelectedTheme = '' - this.themeName = '' - - this.themeCollection = themeCollection - this.activeTheme = themeCollection.find(t => t.themeName === 'Dark') - this.appManager = appManager - this.element = document.createElement('div') - this.element.setAttribute('id', 'sol-uml-gen') - } - - onActivation(): void { - this.handleThemeChange() - this.on('solidity', 'compilationFinished', async (file: string, source, languageVersion, data, input, version) => { - if(!this.triggerGenerateUml) return - this.triggerGenerateUml = false - const currentTheme: ThemeQualityType = await this.call('theme', 'currentTheme') - this.currentlySelectedTheme = currentTheme.quality - this.themeName = currentTheme.name - let result = '' - const normalized = normalizeContractPath(file) - this.currentFile = normalized[normalized.length - 1] - try { - if (data.sources && Object.keys(data.sources).length > 1) { // we should flatten first as there are multiple asts - result = await this.flattenContract(source, file, data) - } - const ast = result.length > 1 ? parser.parse(result) : parser.parse(source.sources[file].content) - this.umlClasses = convertAST2UmlClasses(ast, this.currentFile) - let umlDot = '' - this.activeTheme = themeCollection.find(theme => theme.themeName === currentTheme.name) - umlDot = convertUmlClasses2Dot(this.umlClasses, false, { backColor: this.activeTheme.backgroundColor, textColor: this.activeTheme.textColor, shapeColor: this.activeTheme.shapeColor, fillColor: this.activeTheme.fillColor }) - const payload = vizRenderStringSync(umlDot) - this.updatedSvg = payload - _paq.push(['trackEvent', 'solidityumlgen', 'umlgenerated']) - this.renderComponent() - await this.call('tabs', 'focus', 'solidityumlgen') - } catch (error) { - console.log('error', error) - } - }) - } - - getThemeCssVariables(cssVars: string) { - return window.getComputedStyle(document.documentElement) - .getPropertyValue(cssVars) - } - - private handleThemeChange() { - this.on('theme', 'themeChanged', async (theme) => { - this.currentlySelectedTheme = theme.quality - const themeQuality: ThemeQualityType = await this.call('theme', 'currentTheme') - themeCollection.forEach((theme) => { - if (theme.themeName === themeQuality.name) { - this.themeDark = theme.backgroundColor - this.activeTheme = theme - const umlDot = convertUmlClasses2Dot(this.umlClasses, false, { backColor: this.activeTheme.backgroundColor, textColor: this.activeTheme.textColor, shapeColor: this.activeTheme.shapeColor, fillColor: this.activeTheme.fillColor }) - this.updatedSvg = vizRenderStringSync(umlDot) - this.renderComponent() - } - }) - await this.call('tabs', 'focus', 'solidityumlgen') - }) - } - - async mangleSvgPayload(svgPayload: string) : Promise { - const parser = new DOMParser() - const themeQuality: ThemeQualityType = await this.call('theme', 'currentTheme') - const parsedDocument = parser.parseFromString(svgPayload, 'image/svg+xml') - const element = parsedDocument.getElementsByTagName('svg') - themeCollection.forEach((theme) => { - if (theme.themeName === themeQuality.name) { - parsedDocument.documentElement.setAttribute('style', `background-color: var(${this.getThemeCssVariables('--body-bg')})`) - element[0].setAttribute('fill', theme.backgroundColor) - } - }) - const stringifiedSvg = new XMLSerializer().serializeToString(parsedDocument) - return stringifiedSvg - } - - onDeactivation(): void { - this.off('solidity', 'compilationFinished') - } - - generateCustomAction = async (action: customAction) => { - this.triggerGenerateUml = true - this.updatedSvg = this.updatedSvg.startsWith(' = () => {} + constructor(appManager: RemixAppManager) { + super(profile) + this.currentFile = '' + this.svgPayload = '' + this.updatedSvg = '' + this.loading = false + this.currentlySelectedTheme = '' + this.themeName = '' + + this.themeCollection = themeCollection + this.activeTheme = themeCollection.find(t => t.themeName === 'Dark') + this.appManager = appManager + this.element = document.createElement('div') + this.element.setAttribute('id', 'sol-uml-gen') + } + + onActivation(): void { + this.handleThemeChange() + this.on('solidity', 'compilationFinished', async (file: string, source, languageVersion, data, input, version) => { + if(!this.triggerGenerateUml) return + this.triggerGenerateUml = false + const currentTheme: ThemeQualityType = await this.call('theme', 'currentTheme') + this.currentlySelectedTheme = currentTheme.quality + this.themeName = currentTheme.name + let result = '' + const normalized = normalizeContractPath(file) + this.currentFile = normalized[normalized.length - 1] + try { + if (data.sources && Object.keys(data.sources).length > 1) { // we should flatten first as there are multiple asts + result = await this.flattenContract(source, file, data) + } + const ast = result.length > 1 ? parser.parse(result) : parser.parse(source.sources[file].content) + this.umlClasses = convertAST2UmlClasses(ast, this.currentFile) + let umlDot = '' + this.activeTheme = themeCollection.find(theme => theme.themeName === currentTheme.name) + umlDot = convertUmlClasses2Dot(this.umlClasses, false, { backColor: this.activeTheme.backgroundColor, textColor: this.activeTheme.textColor, shapeColor: this.activeTheme.shapeColor, fillColor: this.activeTheme.fillColor }) + const payload = vizRenderStringSync(umlDot) + this.updatedSvg = payload + _paq.push(['trackEvent', 'solidityumlgen', 'umlgenerated']) this.renderComponent() - } - - /** + await this.call('tabs', 'focus', 'solidityumlgen') + } catch (error) { + console.log('error', error) + } + }) + } + + getThemeCssVariables(cssVars: string) { + return window.getComputedStyle(document.documentElement) + .getPropertyValue(cssVars) + } + + private handleThemeChange() { + this.on('theme', 'themeChanged', async (theme) => { + this.currentlySelectedTheme = theme.quality + const themeQuality: ThemeQualityType = await this.call('theme', 'currentTheme') + themeCollection.forEach((theme) => { + if (theme.themeName === themeQuality.name) { + this.themeDark = theme.backgroundColor + this.activeTheme = theme + const umlDot = convertUmlClasses2Dot(this.umlClasses, false, { backColor: this.activeTheme.backgroundColor, textColor: this.activeTheme.textColor, shapeColor: this.activeTheme.shapeColor, fillColor: this.activeTheme.fillColor }) + this.updatedSvg = vizRenderStringSync(umlDot) + this.renderComponent() + } + }) + await this.call('tabs', 'focus', 'solidityumlgen') + }) + } + + async mangleSvgPayload(svgPayload: string) : Promise { + const parser = new DOMParser() + const themeQuality: ThemeQualityType = await this.call('theme', 'currentTheme') + const parsedDocument = parser.parseFromString(svgPayload, 'image/svg+xml') + const element = parsedDocument.getElementsByTagName('svg') + themeCollection.forEach((theme) => { + if (theme.themeName === themeQuality.name) { + parsedDocument.documentElement.setAttribute('style', `background-color: var(${this.getThemeCssVariables('--body-bg')})`) + element[0].setAttribute('fill', theme.backgroundColor) + } + }) + const stringifiedSvg = new XMLSerializer().serializeToString(parsedDocument) + return stringifiedSvg + } + + onDeactivation(): void { + this.off('solidity', 'compilationFinished') + } + + generateCustomAction = async (action: customAction) => { + this.triggerGenerateUml = true + this.updatedSvg = this.updatedSvg.startsWith('} */ - async flattenContract (source: any, filePath: string, data: any) { - const result = await this.call('contractflattener', 'flattenContract', source, filePath, data) - return result - } - - - - async showUmlDiagram(svgPayload: string) { - this.updatedSvg = svgPayload - this.renderComponent() - } - - hideSpinner() { - this.loading = false - this.renderComponent() - } - - setDispatch (dispatch: React.Dispatch) { - this.dispatch = dispatch - this.renderComponent() - } - - render() { - return
- -
- } - - renderComponent () { - this.dispatch({ - ...this, - updatedSvg: this.updatedSvg, - loading: this.loading, - themeSelected: this.currentlySelectedTheme, - themeName: this.themeName, - themeDark: this.themeDark, - fileName: this.currentFile, - themeCollection: this.themeCollection, - activeTheme: this.activeTheme, - }) - } - - updateComponent(state: any) { - return - } + async flattenContract (source: any, filePath: string, data: any) { + const result = await this.call('contractflattener', 'flattenContract', source, filePath, data) + return result + } + + + + async showUmlDiagram(svgPayload: string) { + this.updatedSvg = svgPayload + this.renderComponent() + } + + hideSpinner() { + this.loading = false + this.renderComponent() + } + + setDispatch (dispatch: React.Dispatch) { + this.dispatch = dispatch + this.renderComponent() + } + + render() { + return
+ +
+ } + + renderComponent () { + this.dispatch({ + ...this, + updatedSvg: this.updatedSvg, + loading: this.loading, + themeSelected: this.currentlySelectedTheme, + themeName: this.themeName, + themeDark: this.themeDark, + fileName: this.currentFile, + themeCollection: this.themeCollection, + activeTheme: this.activeTheme, + }) + } + + updateComponent(state: any) { + return + } } @@ -246,10 +246,10 @@ interface Sol2umlClassOptions extends ClassOptions { import { dirname } from 'path' import { convertClass2Dot } from 'sol2uml/lib/converterClass2Dot' import { - Association, - ClassStereotype, - ReferenceType, - UmlClass, + Association, + ClassStereotype, + ReferenceType, + UmlClass, } from 'sol2uml/lib/umlClass' import { findAssociatedClass } from 'sol2uml/lib/associations' @@ -264,11 +264,11 @@ import { findAssociatedClass } from 'sol2uml/lib/associations' * @return dotString Graphviz's DOT format for defining nodes, edges and clusters. */ export function convertUmlClasses2Dot( - umlClasses: UmlClass[], - clusterFolders: boolean = false, - classOptions: Sol2umlClassOptions = {} + umlClasses: UmlClass[], + clusterFolders: boolean = false, + classOptions: Sol2umlClassOptions = {} ): string { - let dotString: string = ` + let dotString: string = ` digraph UmlClassDiagram { rankdir=BT arrowhead=open @@ -276,124 +276,124 @@ bgcolor="${classOptions.backColor}" edge [color="${classOptions.shapeColor}"] node [shape=record, style=filled, color="${classOptions.shapeColor}", fillcolor="${classOptions.fillColor}", fontcolor="${classOptions.textColor}"]` - // Sort UML Classes by folder of source file - const umlClassesSortedByCodePath = sortUmlClassesByCodePath(umlClasses) + // Sort UML Classes by folder of source file + const umlClassesSortedByCodePath = sortUmlClassesByCodePath(umlClasses) - let currentCodeFolder = '' - for (const umlClass of umlClassesSortedByCodePath) { - const codeFolder = dirname(umlClass.relativePath) - if (currentCodeFolder !== codeFolder) { - // Need to close off the last subgraph if not the first - if (currentCodeFolder != '') { - dotString += '\n}' - } + let currentCodeFolder = '' + for (const umlClass of umlClassesSortedByCodePath) { + const codeFolder = dirname(umlClass.relativePath) + if (currentCodeFolder !== codeFolder) { + // Need to close off the last subgraph if not the first + if (currentCodeFolder != '') { + dotString += '\n}' + } - dotString += ` + dotString += ` subgraph ${getSubGraphName(clusterFolders)} { label="${codeFolder}"` - currentCodeFolder = codeFolder - } - dotString += convertClass2Dot(umlClass, classOptions) + currentCodeFolder = codeFolder } + dotString += convertClass2Dot(umlClass, classOptions) + } - // Need to close off the last subgraph if not the first - if (currentCodeFolder != '') { - dotString += '\n}' - } + // Need to close off the last subgraph if not the first + if (currentCodeFolder != '') { + dotString += '\n}' + } - dotString += addAssociationsToDot(umlClasses, classOptions) + dotString += addAssociationsToDot(umlClasses, classOptions) - // Need to close off the last the digraph - dotString += '\n}' + // Need to close off the last the digraph + dotString += '\n}' - // debug(dotString) + // debug(dotString) - return dotString + return dotString } let subGraphCount = 0 function getSubGraphName(clusterFolders: boolean = false) { - if (clusterFolders) { - return ` cluster_${subGraphCount++}` - } - return ` graph_${subGraphCount++}` + if (clusterFolders) { + return ` cluster_${subGraphCount++}` + } + return ` graph_${subGraphCount++}` } function sortUmlClassesByCodePath(umlClasses: UmlClass[]): UmlClass[] { - return umlClasses.sort((a, b) => { - if (a.relativePath < b.relativePath) { - return -1 - } - if (a.relativePath > b.relativePath) { - return 1 - } - return 0 - }) + return umlClasses.sort((a, b) => { + if (a.relativePath < b.relativePath) { + return -1 + } + if (a.relativePath > b.relativePath) { + return 1 + } + return 0 + }) } export function addAssociationsToDot( - umlClasses: UmlClass[], - classOptions: ClassOptions = {} + umlClasses: UmlClass[], + classOptions: ClassOptions = {} ): string { - let dotString: string = '' - - // for each class - for (const sourceUmlClass of umlClasses) { - if (!classOptions.hideEnums) { - // for each enum in the class - sourceUmlClass.enums.forEach((enumId) => { - // Has the enum been filtered out? eg depth limited - const targetUmlClass = umlClasses.find((c) => c.id === enumId) - if (targetUmlClass) { - // Draw aggregated link from contract to contract level Enum - dotString += `\n${enumId} -> ${sourceUmlClass.id} [arrowhead=diamond, weight=2]` - } - }) + let dotString: string = '' + + // for each class + for (const sourceUmlClass of umlClasses) { + if (!classOptions.hideEnums) { + // for each enum in the class + sourceUmlClass.enums.forEach((enumId) => { + // Has the enum been filtered out? eg depth limited + const targetUmlClass = umlClasses.find((c) => c.id === enumId) + if (targetUmlClass) { + // Draw aggregated link from contract to contract level Enum + dotString += `\n${enumId} -> ${sourceUmlClass.id} [arrowhead=diamond, weight=2]` } - if (!classOptions.hideStructs) { - // for each struct in the class - sourceUmlClass.structs.forEach((structId) => { - // Has the struct been filtered out? eg depth limited - const targetUmlClass = umlClasses.find((c) => c.id === structId) - if (targetUmlClass) { - // Draw aggregated link from contract to contract level Struct - dotString += `\n${structId} -> ${sourceUmlClass.id} [arrowhead=diamond, weight=2]` - } - }) + }) + } + if (!classOptions.hideStructs) { + // for each struct in the class + sourceUmlClass.structs.forEach((structId) => { + // Has the struct been filtered out? eg depth limited + const targetUmlClass = umlClasses.find((c) => c.id === structId) + if (targetUmlClass) { + // Draw aggregated link from contract to contract level Struct + dotString += `\n${structId} -> ${sourceUmlClass.id} [arrowhead=diamond, weight=2]` } + }) + } - // for each association in that class - for (const association of Object.values(sourceUmlClass.associations)) { - const targetUmlClass = findAssociatedClass( - association, - sourceUmlClass, - umlClasses - ) - if (targetUmlClass) { - dotString += addAssociationToDot( - sourceUmlClass, - targetUmlClass, - association, - classOptions - ) - } - } + // for each association in that class + for (const association of Object.values(sourceUmlClass.associations)) { + const targetUmlClass = findAssociatedClass( + association, + sourceUmlClass, + umlClasses + ) + if (targetUmlClass) { + dotString += addAssociationToDot( + sourceUmlClass, + targetUmlClass, + association, + classOptions + ) + } } + } - return dotString + return dotString } function addAssociationToDot( - sourceUmlClass: UmlClass, - targetUmlClass: UmlClass, - association: Association, - classOptions: ClassOptions = {} + sourceUmlClass: UmlClass, + targetUmlClass: UmlClass, + association: Association, + classOptions: ClassOptions = {} ): string { - // do not include library or interface associations if hidden - // Or associations to Structs, Enums or Constants if they are hidden - if ( - (classOptions.hideLibraries && + // do not include library or interface associations if hidden + // Or associations to Structs, Enums or Constants if they are hidden + if ( + (classOptions.hideLibraries && (sourceUmlClass.stereotype === ClassStereotype.Library || targetUmlClass.stereotype === ClassStereotype.Library)) || (classOptions.hideInterfaces && @@ -408,28 +408,28 @@ function addAssociationToDot( targetUmlClass.stereotype === ClassStereotype.Enum) || (classOptions.hideConstants && targetUmlClass.stereotype === ClassStereotype.Constant) - ) { - return '' - } + ) { + return '' + } - let dotString = `\n${sourceUmlClass.id} -> ${targetUmlClass.id} [` + let dotString = `\n${sourceUmlClass.id} -> ${targetUmlClass.id} [` - if ( - association.referenceType == ReferenceType.Memory || + if ( + association.referenceType == ReferenceType.Memory || (association.realization && targetUmlClass.stereotype === ClassStereotype.Interface) - ) { - dotString += 'style=dashed, ' - } - - if (association.realization) { - dotString += 'arrowhead=empty, arrowsize=3, ' - if (!targetUmlClass.stereotype) { - dotString += 'weight=4, ' - } else { - dotString += 'weight=3, ' - } + ) { + dotString += 'style=dashed, ' + } + + if (association.realization) { + dotString += 'arrowhead=empty, arrowsize=3, ' + if (!targetUmlClass.stereotype) { + dotString += 'weight=4, ' + } else { + dotString += 'weight=3, ' } + } - return dotString + ']' + return dotString + ']' } diff --git a/apps/remix-ide/src/app/plugins/storage.ts b/apps/remix-ide/src/app/plugins/storage.ts index cd7ec1e162..90b49ef647 100644 --- a/apps/remix-ide/src/app/plugins/storage.ts +++ b/apps/remix-ide/src/app/plugins/storage.ts @@ -1,59 +1,59 @@ import { Plugin } from '@remixproject/engine'; const profile = { - name: 'storage', - displayName: 'Storage', - description: 'Storage', - methods: ['getStorage', 'formatString'] + name: 'storage', + displayName: 'Storage', + description: 'Storage', + methods: ['getStorage', 'formatString'] }; export class StoragePlugin extends Plugin { - constructor() { - super(profile); + constructor() { + super(profile); + } + + async getStorage() { + let storage = null + if ('storage' in navigator && 'estimate' in navigator.storage && (window as any).remixFileSystem.name !== 'localstorage') { + storage = await navigator.storage.estimate() + } else { + storage ={ + usage: parseFloat(this.calculateLocalStorage()) * 1000, + quota: 5000000, + } } - - async getStorage() { - let storage = null - if ('storage' in navigator && 'estimate' in navigator.storage && (window as any).remixFileSystem.name !== 'localstorage') { - storage = await navigator.storage.estimate() - } else { - storage ={ - usage: parseFloat(this.calculateLocalStorage()) * 1000, - quota: 5000000, - } - } - const _paq = (window as any)._paq = (window as any)._paq || [] - _paq.push(['trackEvent', 'Storage', 'used', this.formatString(storage)]); - return storage - } - - formatString(storage) { - return `${this.formatBytes(storage.usage)} / ${this.formatBytes(storage.quota)}`; + const _paq = (window as any)._paq = (window as any)._paq || [] + _paq.push(['trackEvent', 'Storage', 'used', this.formatString(storage)]); + return storage + } + + formatString(storage) { + return `${this.formatBytes(storage.usage)} / ${this.formatBytes(storage.quota)}`; + } + + calculateLocalStorage() { + let _lsTotal = 0 + let _xLen; let _x + for (_x in localStorage) { + // eslint-disable-next-line no-prototype-builtins + if (!localStorage.hasOwnProperty(_x)) { + continue + } + _xLen = ((localStorage[_x].length + _x.length)) + _lsTotal += _xLen } + return (_lsTotal / 1024).toFixed(2) + } - calculateLocalStorage() { - let _lsTotal = 0 - let _xLen; let _x - for (_x in localStorage) { - // eslint-disable-next-line no-prototype-builtins - if (!localStorage.hasOwnProperty(_x)) { - continue - } - _xLen = ((localStorage[_x].length + _x.length)) - _lsTotal += _xLen - } - return (_lsTotal / 1024).toFixed(2) - } - - formatBytes(bytes: number, decimals = 2) { - if (bytes === 0) return '0 Bytes'; + formatBytes(bytes: number, decimals = 2) { + if (bytes === 0) return '0 Bytes'; - const k = 1024; - const dm = decimals < 0 ? 0 : decimals; - const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB']; + const k = 1024; + const dm = decimals < 0 ? 0 : decimals; + const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB']; - const i = Math.floor(Math.log(bytes) / Math.log(k)); + const i = Math.floor(Math.log(bytes) / Math.log(k)); - return parseFloat((bytes / Math.pow(k, i)).toFixed(dm)) + ' ' + sizes[i]; - } + return parseFloat((bytes / Math.pow(k, i)).toFixed(dm)) + ' ' + sizes[i]; + } } diff --git a/apps/remix-ide/src/app/providers/abstract-provider.tsx b/apps/remix-ide/src/app/providers/abstract-provider.tsx index 5d9df384c3..a7d1b4e599 100644 --- a/apps/remix-ide/src/app/providers/abstract-provider.tsx +++ b/apps/remix-ide/src/app/providers/abstract-provider.tsx @@ -28,110 +28,110 @@ export interface IProvider { } export abstract class AbstractProvider extends Plugin implements IProvider { - provider: ethers.providers.JsonRpcProvider - blockchain: Blockchain - defaultUrl: string - connected: boolean - nodeUrl: string - options: { [id: string] : any } = {} + provider: ethers.providers.JsonRpcProvider + blockchain: Blockchain + defaultUrl: string + connected: boolean + nodeUrl: string + options: { [id: string] : any } = {} - constructor (profile, blockchain, defaultUrl) { - super(profile) - this.defaultUrl = defaultUrl - this.provider = null - this.connected = false - this.blockchain = blockchain - this.nodeUrl = 'http://localhost:8545' - } + constructor (profile, blockchain, defaultUrl) { + super(profile) + this.defaultUrl = defaultUrl + this.provider = null + this.connected = false + this.blockchain = blockchain + this.nodeUrl = 'http://localhost:8545' + } abstract body(): JSX.Element onDeactivation () { - this.provider = null + this.provider = null } async init () { - this.nodeUrl = await ((): Promise => { - return new Promise((resolve, reject) => { - const modalContent: AppModal = { - id: this.profile.name, - title: this.profile.displayName, - message: this.body(), - modalType: ModalTypes.prompt, - okLabel: 'OK', - cancelLabel: 'Cancel', - validationFn: (value) => { - if (!value) return { valid: false, message: "value is empty" } - if (value.startsWith('https://') || value.startsWith('http://')) { - return { - valid: true, - message: '' - } - } else { - return { - valid: false, - message: 'the provided value should contain the protocol ( e.g starts with http:// or https:// )' - } - } - }, - okFn: (value: string) => { - setTimeout(() => resolve(value), 0) - }, - cancelFn: () => { - setTimeout(() => reject(new Error('Canceled')), 0) - }, - hideFn: () => { - setTimeout(() => reject(new Error('Hide')), 0) - }, - defaultValue: this.defaultUrl + this.nodeUrl = await ((): Promise => { + return new Promise((resolve, reject) => { + const modalContent: AppModal = { + id: this.profile.name, + title: this.profile.displayName, + message: this.body(), + modalType: ModalTypes.prompt, + okLabel: 'OK', + cancelLabel: 'Cancel', + validationFn: (value) => { + if (!value) return { valid: false, message: "value is empty" } + if (value.startsWith('https://') || value.startsWith('http://')) { + return { + valid: true, + message: '' } - this.call('notification', 'modal', modalContent) - }) - })() - this.provider = new ethers.providers.JsonRpcProvider(this.nodeUrl) - return { - nodeUrl: this.nodeUrl - } + } else { + return { + valid: false, + message: 'the provided value should contain the protocol ( e.g starts with http:// or https:// )' + } + } + }, + okFn: (value: string) => { + setTimeout(() => resolve(value), 0) + }, + cancelFn: () => { + setTimeout(() => reject(new Error('Canceled')), 0) + }, + hideFn: () => { + setTimeout(() => reject(new Error('Hide')), 0) + }, + defaultValue: this.defaultUrl + } + this.call('notification', 'modal', modalContent) + }) + })() + this.provider = new ethers.providers.JsonRpcProvider(this.nodeUrl) + return { + nodeUrl: this.nodeUrl + } } sendAsync (data: JsonDataRequest): Promise { - // eslint-disable-next-line no-async-promise-executor - return new Promise(async (resolve, reject) => { - if (!this.provider) return reject(new Error('provider node set')) - this.sendAsyncInternal(data, resolve, reject) - }) + // eslint-disable-next-line no-async-promise-executor + return new Promise(async (resolve, reject) => { + if (!this.provider) return reject(new Error('provider node set')) + this.sendAsyncInternal(data, resolve, reject) + }) } private async switchAway (showError) { - if (!this.provider) return - this.provider = null - this.connected = false - if (showError) { - const modalContent: AlertModal = { - id: this.profile.name, - title: this.profile.displayName, - message: `Error while connecting to the provider, provider not connected`, - } - this.call('notification', 'alert', modalContent) + if (!this.provider) return + this.provider = null + this.connected = false + if (showError) { + const modalContent: AlertModal = { + id: this.profile.name, + title: this.profile.displayName, + message: `Error while connecting to the provider, provider not connected`, } - await this.call('udapp', 'setEnvironmentMode', { context: 'vm-merge'}) - return + this.call('notification', 'alert', modalContent) + } + await this.call('udapp', 'setEnvironmentMode', { context: 'vm-merge'}) + return } private async sendAsyncInternal (data: JsonDataRequest, resolve: SuccessRequest, reject: RejectRequest): Promise { - if (this.provider) { - try { - const result = await this.provider.send(data.method, data.params) - resolve({ jsonrpc: '2.0', result, id: data.id }) - } catch (error) { - if (error && error.message && error.message.includes('net_version') && error.message.includes('SERVER_ERROR')) { - this.switchAway(true) - } - reject(error) - } - } else { - const result = data.method === 'net_listening' ? 'canceled' : [] - resolve({ jsonrpc: '2.0', result: result, id: data.id }) + if (this.provider) { + try { + const result = await this.provider.send(data.method, data.params) + resolve({ jsonrpc: '2.0', result, id: data.id }) + } catch (error) { + if (error && error.message && error.message.includes('net_version') && error.message.includes('SERVER_ERROR')) { + this.switchAway(true) + } + reject(error) } + } else { + const result = data.method === 'net_listening' ? 'canceled' : [] + resolve({ jsonrpc: '2.0', result: result, id: data.id }) + } } } diff --git a/apps/remix-ide/src/app/providers/custom-vm-fork-provider.tsx b/apps/remix-ide/src/app/providers/custom-vm-fork-provider.tsx index d4f9c81297..4271bdc977 100644 --- a/apps/remix-ide/src/app/providers/custom-vm-fork-provider.tsx +++ b/apps/remix-ide/src/app/providers/custom-vm-fork-provider.tsx @@ -5,102 +5,102 @@ import { BasicVMProvider } from './vm-provider' import { Hardfork } from '@ethereumjs/common' export class CustomForkVMProvider extends BasicVMProvider { - nodeUrl: string - blockNumber: number | 'latest' - inputs: any + nodeUrl: string + blockNumber: number | 'latest' + inputs: any - constructor (blockchain) { - super({ - name: 'vm-custom-fork', - displayName: 'Custom fork - Remix VM', - kind: 'provider', - description: 'Custom fork - Remix VM', - methods: ['sendAsync', 'init'], - version: packageJson.version - }, blockchain) - this.blockchain = blockchain - this.fork = '' - this.nodeUrl = '' - this.blockNumber = 'latest' - this.inputs = {} - } + constructor (blockchain) { + super({ + name: 'vm-custom-fork', + displayName: 'Custom fork - Remix VM', + kind: 'provider', + description: 'Custom fork - Remix VM', + methods: ['sendAsync', 'init'], + version: packageJson.version + }, blockchain) + this.blockchain = blockchain + this.fork = '' + this.nodeUrl = '' + this.blockNumber = 'latest' + this.inputs = {} + } - async init () { - const body = () => { - return
- Please provide information about the custom fork. If the node URL is not provided, the VM will start with an empty state. -
- - -
-
- - -
-
- - -
-
- } - const result = await ((): Promise => { - return new Promise((resolve, reject) => { - const modalContent: AppModal = { - id: this.profile.name, - title: this.profile.displayName, - message: body(), - validationFn: (data: any) => { - if(data.nodeUrl !== '' && !data.nodeUrl.startsWith("http")) { - return { - valid: false, - message: 'node URL should be a valid URL' - } - } - if (data.blockNumber !== 'latest' && isNaN(data.blockNumber)) { - return { - valid: false, - message: 'blockNumber should be a number or "latest"' - } - } - return { - valid: true, - message: '' - } - }, - modalType: ModalTypes.form, - okLabel: 'Connect', - cancelLabel: 'Cancel', - okFn: (value: string) => { - setTimeout(() => resolve(value), 0) - }, - cancelFn: () => { - setTimeout(() => reject(new Error('Canceled')), 0) - }, - hideFn: () => { - setTimeout(() => reject(new Error('Hide')), 0) - } - } - return this.call('notification', 'modal', modalContent) - }) - })() - this.fork = result.evmType - this.nodeUrl = result.nodeUrl - if (this.nodeUrl) { - const block = result.blockNumber - this.blockNumber = block === 'latest' ? 'latest' : parseInt(block) - } else { - this.nodeUrl = undefined - this.blockNumber = undefined + async init () { + const body = () => { + return
+ Please provide information about the custom fork. If the node URL is not provided, the VM will start with an empty state. +
+ + +
+
+ + +
+
+ + +
+
+ } + const result = await ((): Promise => { + return new Promise((resolve, reject) => { + const modalContent: AppModal = { + id: this.profile.name, + title: this.profile.displayName, + message: body(), + validationFn: (data: any) => { + if(data.nodeUrl !== '' && !data.nodeUrl.startsWith("http")) { + return { + valid: false, + message: 'node URL should be a valid URL' + } + } + if (data.blockNumber !== 'latest' && isNaN(data.blockNumber)) { + return { + valid: false, + message: 'blockNumber should be a number or "latest"' + } + } + return { + valid: true, + message: '' + } + }, + modalType: ModalTypes.form, + okLabel: 'Connect', + cancelLabel: 'Cancel', + okFn: (value: string) => { + setTimeout(() => resolve(value), 0) + }, + cancelFn: () => { + setTimeout(() => reject(new Error('Canceled')), 0) + }, + hideFn: () => { + setTimeout(() => reject(new Error('Hide')), 0) + } } + return this.call('notification', 'modal', modalContent) + }) + })() + this.fork = result.evmType + this.nodeUrl = result.nodeUrl + if (this.nodeUrl) { + const block = result.blockNumber + this.blockNumber = block === 'latest' ? 'latest' : parseInt(block) + } else { + this.nodeUrl = undefined + this.blockNumber = undefined + } - return { - 'fork': this.fork, - 'nodeUrl': this.nodeUrl, - 'blockNumber': this.blockNumber - } + return { + 'fork': this.fork, + 'nodeUrl': this.nodeUrl, + 'blockNumber': this.blockNumber } + } } diff --git a/apps/remix-ide/src/app/providers/external-http-provider.tsx b/apps/remix-ide/src/app/providers/external-http-provider.tsx index 36d864c2a1..52090d7ac2 100644 --- a/apps/remix-ide/src/app/providers/external-http-provider.tsx +++ b/apps/remix-ide/src/app/providers/external-http-provider.tsx @@ -3,39 +3,39 @@ import React from 'react' // eslint-disable-line import { AbstractProvider } from './abstract-provider' const profile = { - name: 'basic-http-provider', - displayName: 'External Http Provider', - kind: 'provider', - description: 'External Http Provider', - methods: ['sendAsync', 'init'], - version: packageJson.version + name: 'basic-http-provider', + displayName: 'External Http Provider', + kind: 'provider', + description: 'External Http Provider', + methods: ['sendAsync', 'init'], + version: packageJson.version } export class ExternalHttpProvider extends AbstractProvider { - constructor (blockchain) { - super(profile, blockchain, 'http://127.0.0.1:8545') - } + constructor (blockchain) { + super(profile, blockchain, 'http://127.0.0.1:8545') + } - body (): JSX.Element { - const thePath = '' - return ( - <> -
+ body (): JSX.Element { + const thePath = '' + return ( + <> +
Note: To use Geth & https://remix.ethereum.org, configure it to allow requests from Remix:(see Geth Docs on rpc server) -
geth --http --http.corsdomain https://remix.ethereum.org
-
+
geth --http --http.corsdomain https://remix.ethereum.org
+
To run Remix & a local Geth test node, use this command: (see Geth Docs on Dev mode) -
geth --http --http.corsdomain="{window.origin}" --http.api web3,eth,debug,personal,net --vmdebug --datadir {thePath} --dev console
-
-
- WARNING: It is not safe to use the --http.corsdomain flag with a wildcard: --http.corsdomain * -
-
For more info: Remix Docs on External HTTP Provider -
-
+
geth --http --http.corsdomain="{window.origin}" --http.api web3,eth,debug,personal,net --vmdebug --datadir {thePath} --dev console
+
+
+ WARNING: It is not safe to use the --http.corsdomain flag with a wildcard: --http.corsdomain * +
+
For more info: Remix Docs on External HTTP Provider +
+
External HTTP Provider Endpoint -
- - ) - } +
+ + ) + } } \ No newline at end of file diff --git a/apps/remix-ide/src/app/providers/foundry-provider.tsx b/apps/remix-ide/src/app/providers/foundry-provider.tsx index 904c330b3c..15bb372a00 100644 --- a/apps/remix-ide/src/app/providers/foundry-provider.tsx +++ b/apps/remix-ide/src/app/providers/foundry-provider.tsx @@ -3,29 +3,29 @@ import React from 'react' // eslint-disable-line import { AbstractProvider } from './abstract-provider' const profile = { - name: 'foundry-provider', - displayName: 'Foundry Provider', - kind: 'provider', - description: 'Foundry Anvil provider', - methods: ['sendAsync', 'init'], - version: packageJson.version + name: 'foundry-provider', + displayName: 'Foundry Provider', + kind: 'provider', + description: 'Foundry Anvil provider', + methods: ['sendAsync', 'init'], + version: packageJson.version } export class FoundryProvider extends AbstractProvider { - constructor (blockchain) { - super(profile, blockchain, 'http://127.0.0.1:8545') - } + constructor (blockchain) { + super(profile, blockchain, 'http://127.0.0.1:8545') + } - body (): JSX.Element { - return ( -
Note: To run Anvil on your system, run: -
curl -L https://foundry.paradigm.xyz | bash
-
anvil
-
+ body (): JSX.Element { + return ( +
Note: To run Anvil on your system, run: +
curl -L https://foundry.paradigm.xyz | bash
+
anvil
+
For more info, visit: Foundry Documentation -
-
Anvil JSON-RPC Endpoint:
-
- ) - } +
+
Anvil JSON-RPC Endpoint:
+
+ ) + } } \ No newline at end of file diff --git a/apps/remix-ide/src/app/providers/ganache-provider.tsx b/apps/remix-ide/src/app/providers/ganache-provider.tsx index 132aa20957..b27ada3d3d 100644 --- a/apps/remix-ide/src/app/providers/ganache-provider.tsx +++ b/apps/remix-ide/src/app/providers/ganache-provider.tsx @@ -3,29 +3,29 @@ import React from 'react' // eslint-disable-line import { AbstractProvider } from './abstract-provider' const profile = { - name: 'ganache-provider', - displayName: 'Ganache Provider', - kind: 'provider', - description: 'Truffle Ganache provider', - methods: ['sendAsync', 'init'], - version: packageJson.version + name: 'ganache-provider', + displayName: 'Ganache Provider', + kind: 'provider', + description: 'Truffle Ganache provider', + methods: ['sendAsync', 'init'], + version: packageJson.version } export class GanacheProvider extends AbstractProvider { - constructor (blockchain) { - super(profile, blockchain, 'http://127.0.0.1:8545') - } + constructor (blockchain) { + super(profile, blockchain, 'http://127.0.0.1:8545') + } - body (): JSX.Element { - return ( -
Note: To run Ganache on your system, run: -
yarn global add ganache
-
ganache
-
+ body (): JSX.Element { + return ( +
Note: To run Ganache on your system, run: +
yarn global add ganache
+
ganache
+
For more info, visit: Ganache Documentation -
-
Ganache JSON-RPC Endpoint:
-
- ) - } +
+
Ganache JSON-RPC Endpoint:
+
+ ) + } } \ No newline at end of file diff --git a/apps/remix-ide/src/app/providers/goerli-vm-fork-provider.tsx b/apps/remix-ide/src/app/providers/goerli-vm-fork-provider.tsx index 9327f38226..abc29be584 100644 --- a/apps/remix-ide/src/app/providers/goerli-vm-fork-provider.tsx +++ b/apps/remix-ide/src/app/providers/goerli-vm-fork-provider.tsx @@ -2,28 +2,28 @@ import * as packageJson from '../../../../../package.json' import { BasicVMProvider } from './vm-provider' export class GoerliForkVMProvider extends BasicVMProvider { - nodeUrl: string - blockNumber: number | 'latest' - constructor (blockchain) { - super({ - name: 'vm-goerli-fork', - displayName: 'Goerli fork - Remix VM (London)', - kind: 'provider', - description: 'Remix VM (London)', - methods: ['sendAsync', 'init'], - version: packageJson.version - }, blockchain) - this.blockchain = blockchain - this.fork = 'shanghai' - this.nodeUrl = 'https://remix-sepolia.ethdevops.io' - this.blockNumber = 'latest' - } + nodeUrl: string + blockNumber: number | 'latest' + constructor (blockchain) { + super({ + name: 'vm-goerli-fork', + displayName: 'Goerli fork - Remix VM (London)', + kind: 'provider', + description: 'Remix VM (London)', + methods: ['sendAsync', 'init'], + version: packageJson.version + }, blockchain) + this.blockchain = blockchain + this.fork = 'shanghai' + this.nodeUrl = 'https://remix-sepolia.ethdevops.io' + this.blockNumber = 'latest' + } - async init () { - return { - 'fork': this.fork, - 'nodeUrl': this.nodeUrl, - 'blockNumber': this.blockNumber - } + async init () { + return { + 'fork': this.fork, + 'nodeUrl': this.nodeUrl, + 'blockNumber': this.blockNumber } + } } diff --git a/apps/remix-ide/src/app/providers/hardhat-provider.tsx b/apps/remix-ide/src/app/providers/hardhat-provider.tsx index 1d579ab6c1..67d6168c6d 100644 --- a/apps/remix-ide/src/app/providers/hardhat-provider.tsx +++ b/apps/remix-ide/src/app/providers/hardhat-provider.tsx @@ -3,28 +3,28 @@ import React from 'react' // eslint-disable-line import { AbstractProvider } from './abstract-provider' const profile = { - name: 'hardhat-provider', - displayName: 'Hardhat Provider', - kind: 'provider', - description: 'Hardhat provider', - methods: ['sendAsync', 'init'], - version: packageJson.version + name: 'hardhat-provider', + displayName: 'Hardhat Provider', + kind: 'provider', + description: 'Hardhat provider', + methods: ['sendAsync', 'init'], + version: packageJson.version } export class HardhatProvider extends AbstractProvider { - constructor (blockchain) { - super(profile, blockchain, 'http://127.0.0.1:8545') - } + constructor (blockchain) { + super(profile, blockchain, 'http://127.0.0.1:8545') + } - body (): JSX.Element { - return ( -
Note: To run Hardhat network node on your system, go to hardhat project folder and run command: -
npx hardhat node
-
+ body (): JSX.Element { + return ( +
Note: To run Hardhat network node on your system, go to hardhat project folder and run command: +
npx hardhat node
+
For more info, visit: Hardhat Documentation -
-
Hardhat JSON-RPC Endpoint:
-
- ) - } +
+
Hardhat JSON-RPC Endpoint:
+
+ ) + } } \ No newline at end of file diff --git a/apps/remix-ide/src/app/providers/injected-L2-provider.tsx b/apps/remix-ide/src/app/providers/injected-L2-provider.tsx index afae690a39..483d924544 100644 --- a/apps/remix-ide/src/app/providers/injected-L2-provider.tsx +++ b/apps/remix-ide/src/app/providers/injected-L2-provider.tsx @@ -1,55 +1,55 @@ import { InjectedProviderDefaultBase } from './injected-provider-default' export class InjectedL2Provider extends InjectedProviderDefaultBase { - chainName: string - chainId: string - rpcUrls: Array + chainName: string + chainId: string + rpcUrls: Array - constructor (profile: any, chainName: string, chainId: string, rpcUrls: Array) { - super(profile) - this.chainName = chainName - this.chainId = chainId - this.rpcUrls = rpcUrls - } + constructor (profile: any, chainName: string, chainId: string, rpcUrls: Array) { + super(profile) + this.chainName = chainName + this.chainId = chainId + this.rpcUrls = rpcUrls + } - async init () { - await super.init() - if (this.chainName && this.rpcUrls && this.rpcUrls.length > 0) await addL2Network(this.chainName, this.chainId, this.rpcUrls) - else - throw new Error('Cannot add the L2 network to main injected provider') - return {} - } + async init () { + await super.init() + if (this.chainName && this.rpcUrls && this.rpcUrls.length > 0) await addL2Network(this.chainName, this.chainId, this.rpcUrls) + else + throw new Error('Cannot add the L2 network to main injected provider') + return {} + } } export const addL2Network = async (chainName: string, chainId: string, rpcUrls: Array) => { - try { + try { + await (window as any).ethereum.request({ + method: 'wallet_switchEthereumChain', + params: [{ chainId: chainId }], + }); + } catch (switchError) { + // This error code indicates that the chain has not been added to MetaMask. + if (switchError.code === 4902) { + try { await (window as any).ethereum.request({ - method: 'wallet_switchEthereumChain', - params: [{ chainId: chainId }], + method: 'wallet_addEthereumChain', + params: [ + { + chainId: chainId, + chainName: chainName, + rpcUrls: rpcUrls, + }, + ], }); - } catch (switchError) { - // This error code indicates that the chain has not been added to MetaMask. - if (switchError.code === 4902) { - try { - await (window as any).ethereum.request({ - method: 'wallet_addEthereumChain', - params: [ - { - chainId: chainId, - chainName: chainName, - rpcUrls: rpcUrls, - }, - ], - }); - await (window as any).ethereum.request({ - method: 'wallet_switchEthereumChain', - params: [{ chainId: chainId }], - }); - } catch (addError) { - // handle "add" error - } - } - // handle other "switch" errors + await (window as any).ethereum.request({ + method: 'wallet_switchEthereumChain', + params: [{ chainId: chainId }], + }); + } catch (addError) { + // handle "add" error + } } + // handle other "switch" errors + } } \ No newline at end of file diff --git a/apps/remix-ide/src/app/providers/injected-arbitrum-one-provider.tsx b/apps/remix-ide/src/app/providers/injected-arbitrum-one-provider.tsx index aaf20e9070..841d98276e 100644 --- a/apps/remix-ide/src/app/providers/injected-arbitrum-one-provider.tsx +++ b/apps/remix-ide/src/app/providers/injected-arbitrum-one-provider.tsx @@ -2,17 +2,17 @@ import * as packageJson from '../../../../../package.json' import { InjectedL2Provider } from './injected-L2-provider' const profile = { - name: 'injected-arbitrum-one-provider', - displayName: 'Injected Arbitrum One Provider', - kind: 'provider', - description: 'injected Arbitrum One Provider', - methods: ['sendAsync', 'init'], - version: packageJson.version + name: 'injected-arbitrum-one-provider', + displayName: 'Injected Arbitrum One Provider', + kind: 'provider', + description: 'injected Arbitrum One Provider', + methods: ['sendAsync', 'init'], + version: packageJson.version } export class InjectedArbitrumOneProvider extends InjectedL2Provider { - constructor () { - super(profile, 'Arbitrum One', '0xa4b1', ['https://arb1.arbitrum.io/rpc']) - } + constructor () { + super(profile, 'Arbitrum One', '0xa4b1', ['https://arb1.arbitrum.io/rpc']) + } } \ No newline at end of file diff --git a/apps/remix-ide/src/app/providers/injected-optimism-provider.tsx b/apps/remix-ide/src/app/providers/injected-optimism-provider.tsx index 60037f415b..207b80a1c2 100644 --- a/apps/remix-ide/src/app/providers/injected-optimism-provider.tsx +++ b/apps/remix-ide/src/app/providers/injected-optimism-provider.tsx @@ -2,17 +2,17 @@ import * as packageJson from '../../../../../package.json' import { InjectedL2Provider } from './injected-L2-provider' const profile = { - name: 'injected-optimism-provider', - displayName: 'Injected Optimism Provider', - kind: 'provider', - description: 'injected Optimism Provider', - methods: ['sendAsync', 'init'], - version: packageJson.version + name: 'injected-optimism-provider', + displayName: 'Injected Optimism Provider', + kind: 'provider', + description: 'injected Optimism Provider', + methods: ['sendAsync', 'init'], + version: packageJson.version } export class Injected0ptimismProvider extends InjectedL2Provider { - constructor () { - super(profile, 'Optimism', '0xa', ['https://mainnet.optimism.io']) - } + constructor () { + super(profile, 'Optimism', '0xa', ['https://mainnet.optimism.io']) + } } \ No newline at end of file diff --git a/apps/remix-ide/src/app/providers/injected-provider-default.tsx b/apps/remix-ide/src/app/providers/injected-provider-default.tsx index 9af45e5660..d58706f429 100644 --- a/apps/remix-ide/src/app/providers/injected-provider-default.tsx +++ b/apps/remix-ide/src/app/providers/injected-provider-default.tsx @@ -3,38 +3,38 @@ import * as packageJson from '../../../../../package.json' import { InjectedProvider } from './injected-provider' export class InjectedProviderDefaultBase extends InjectedProvider { - constructor (profile) { - super(profile) - } + constructor (profile) { + super(profile) + } - async init () { - const injectedProvider = this.getInjectedProvider() - if (injectedProvider && injectedProvider._metamask && injectedProvider._metamask.isUnlocked) { - if (!await injectedProvider._metamask.isUnlocked()) this.call('notification', 'toast', 'Please make sure the injected provider is unlocked (e.g Metamask).') - } - return super.init() + async init () { + const injectedProvider = this.getInjectedProvider() + if (injectedProvider && injectedProvider._metamask && injectedProvider._metamask.isUnlocked) { + if (!await injectedProvider._metamask.isUnlocked()) this.call('notification', 'toast', 'Please make sure the injected provider is unlocked (e.g Metamask).') } + return super.init() + } - getInjectedProvider () { - return (window as any).ethereum - } + getInjectedProvider () { + return (window as any).ethereum + } - notFound () { - return 'No injected provider found. Make sure your provider (e.g. MetaMask, ...) is active and running (when recently activated you may have to reload the page).' - } + notFound () { + return 'No injected provider found. Make sure your provider (e.g. MetaMask, ...) is active and running (when recently activated you may have to reload the page).' + } } const profile = { - name: 'injected', - displayName: 'Injected Provider', - kind: 'provider', - description: 'injected Provider', - methods: ['sendAsync', 'init'], - version: packageJson.version + name: 'injected', + displayName: 'Injected Provider', + kind: 'provider', + description: 'injected Provider', + methods: ['sendAsync', 'init'], + version: packageJson.version } export class InjectedProviderDefault extends InjectedProviderDefaultBase { - constructor () { - super(profile) - } + constructor () { + super(profile) + } } diff --git a/apps/remix-ide/src/app/providers/injected-provider-trustwallet.tsx b/apps/remix-ide/src/app/providers/injected-provider-trustwallet.tsx index c58f6d12f8..37e15add70 100644 --- a/apps/remix-ide/src/app/providers/injected-provider-trustwallet.tsx +++ b/apps/remix-ide/src/app/providers/injected-provider-trustwallet.tsx @@ -3,24 +3,24 @@ import * as packageJson from '../../../../../package.json' import { InjectedProvider } from './injected-provider' const profile = { - name: 'injected-trustwallet', - displayName: 'Trust wallet', - kind: 'provider', - description: 'Trust wallet', - methods: ['sendAsync', 'init'], - version: packageJson.version + name: 'injected-trustwallet', + displayName: 'Trust wallet', + kind: 'provider', + description: 'Trust wallet', + methods: ['sendAsync', 'init'], + version: packageJson.version } export class InjectedProviderTrustWallet extends InjectedProvider { - constructor () { - super(profile) - } + constructor () { + super(profile) + } - getInjectedProvider () { - return (window as any).trustwallet - } + getInjectedProvider () { + return (window as any).trustwallet + } - notFound () { - return 'Could not find Trust Wallet provider. Please make sure the Trust Wallet extension is active. Download the latest version from https://trustwallet.com/browser-extension' - } + notFound () { + return 'Could not find Trust Wallet provider. Please make sure the Trust Wallet extension is active. Download the latest version from https://trustwallet.com/browser-extension' + } } diff --git a/apps/remix-ide/src/app/providers/injected-provider.tsx b/apps/remix-ide/src/app/providers/injected-provider.tsx index 9a1f915128..757b083b3d 100644 --- a/apps/remix-ide/src/app/providers/injected-provider.tsx +++ b/apps/remix-ide/src/app/providers/injected-provider.tsx @@ -5,101 +5,101 @@ import { JsonDataRequest, RejectRequest, SuccessRequest } from '../providers/abs import { IProvider } from './abstract-provider' export abstract class InjectedProvider extends Plugin implements IProvider { - options: { [id: string] : any } = {} - listenerAccountsChanged: (accounts: Array) => void - listenerChainChanged: (chainId: number) => void + options: { [id: string] : any } = {} + listenerAccountsChanged: (accounts: Array) => void + listenerChainChanged: (chainId: number) => void - constructor (profile) { - super(profile) - this.listenerAccountsChanged = (accounts: Array) => { - this.emit('accountsChanged', accounts) - } - this.listenerChainChanged = (chainId: number) => { - this.emit('chainChanged', chainId) - } + constructor (profile) { + super(profile) + this.listenerAccountsChanged = (accounts: Array) => { + this.emit('accountsChanged', accounts) + } + this.listenerChainChanged = (chainId: number) => { + this.emit('chainChanged', chainId) } + } abstract getInjectedProvider(): any abstract notFound(): string onActivation(): void { - try { - const web3Provider = this.getInjectedProvider() - web3Provider.on('accountsChanged', this.listenerAccountsChanged); - web3Provider.on('chainChanged', this.listenerChainChanged); - } catch (error) { - console.log('unable to listen on context changed') - } + try { + const web3Provider = this.getInjectedProvider() + web3Provider.on('accountsChanged', this.listenerAccountsChanged); + web3Provider.on('chainChanged', this.listenerChainChanged); + } catch (error) { + console.log('unable to listen on context changed') + } } onDeactivation(): void { - try { - const web3Provider = this.getInjectedProvider() - web3Provider.removeListener('accountsChanged', this.listenerAccountsChanged) - web3Provider.removeListener('chainChanged', this.listenerChainChanged) - } catch (error) { - console.log('unable to remove listener on context changed') - } + try { + const web3Provider = this.getInjectedProvider() + web3Provider.removeListener('accountsChanged', this.listenerAccountsChanged) + web3Provider.removeListener('chainChanged', this.listenerChainChanged) + } catch (error) { + console.log('unable to remove listener on context changed') + } } askPermission (throwIfNoInjectedProvider) { - const web3Provider = this.getInjectedProvider() - if (typeof web3Provider !== "undefined" && typeof web3Provider.request === "function") { - web3Provider.request({ method: "eth_requestAccounts" }) - } else if (throwIfNoInjectedProvider) { - throw new Error(this.notFound()) - } + const web3Provider = this.getInjectedProvider() + if (typeof web3Provider !== "undefined" && typeof web3Provider.request === "function") { + web3Provider.request({ method: "eth_requestAccounts" }) + } else if (throwIfNoInjectedProvider) { + throw new Error(this.notFound()) + } } body (): JSX.Element { - return ( -
- ) + return ( +
+ ) } async init () { - const injectedProvider = this.getInjectedProvider() - if (injectedProvider === undefined) { - this.call('notification', 'toast', this.notFound()) - throw new Error(this.notFound()) - } else { - this.askPermission(true) - } - return {} + const injectedProvider = this.getInjectedProvider() + if (injectedProvider === undefined) { + this.call('notification', 'toast', this.notFound()) + throw new Error(this.notFound()) + } else { + this.askPermission(true) + } + return {} } sendAsync (data: JsonDataRequest): Promise { - return new Promise((resolve, reject) => { - this.sendAsyncInternal(data, resolve, reject) - }) + return new Promise((resolve, reject) => { + this.sendAsyncInternal(data, resolve, reject) + }) } private async sendAsyncInternal (data: JsonDataRequest, resolve: SuccessRequest, reject: RejectRequest): Promise { - // Check the case where current environment is VM on UI and it still sends RPC requests - // This will be displayed on UI tooltip as 'cannot get account list: Environment Updated !!' - const web3Provider = this.getInjectedProvider() - if (!web3Provider) { - this.call('notification', 'toast', 'No injected provider (e.g Metamask) has been found.') - return resolve({ jsonrpc: '2.0', error: 'no injected provider found', id: data.id }) + // Check the case where current environment is VM on UI and it still sends RPC requests + // This will be displayed on UI tooltip as 'cannot get account list: Environment Updated !!' + const web3Provider = this.getInjectedProvider() + if (!web3Provider) { + this.call('notification', 'toast', 'No injected provider (e.g Metamask) has been found.') + return resolve({ jsonrpc: '2.0', error: 'no injected provider found', id: data.id }) + } + try { + let resultData + if (web3Provider.send) resultData = await web3Provider.send(data.method, data.params) + else if (web3Provider.request) resultData = await web3Provider.request({ method: data.method, params: data.params}) + else { + resolve({ jsonrpc: '2.0', error: 'provider not valid', id: data.id }) + return } - try { - let resultData - if (web3Provider.send) resultData = await web3Provider.send(data.method, data.params) - else if (web3Provider.request) resultData = await web3Provider.request({ method: data.method, params: data.params}) - else { - resolve({ jsonrpc: '2.0', error: 'provider not valid', id: data.id }) - return - } - if (resultData) { - if (resultData.jsonrpc && resultData.jsonrpc === '2.0') { - resultData = resultData.result - } - resolve({ jsonrpc: '2.0', result: resultData, id: data.id }) - } else { - resolve({ jsonrpc: '2.0', error: 'no return data provided', id: data.id }) - } - } catch (error) { - resolve({ jsonrpc: '2.0', error: error.data && error.data.message ? error.data.message : error.message, id: data.id }) + if (resultData) { + if (resultData.jsonrpc && resultData.jsonrpc === '2.0') { + resultData = resultData.result + } + resolve({ jsonrpc: '2.0', result: resultData, id: data.id }) + } else { + resolve({ jsonrpc: '2.0', error: 'no return data provided', id: data.id }) } + } catch (error) { + resolve({ jsonrpc: '2.0', error: error.data && error.data.message ? error.data.message : error.message, id: data.id }) + } } } diff --git a/apps/remix-ide/src/app/providers/mainnet-vm-fork-provider.tsx b/apps/remix-ide/src/app/providers/mainnet-vm-fork-provider.tsx index 68fc03e5ba..09845ab502 100644 --- a/apps/remix-ide/src/app/providers/mainnet-vm-fork-provider.tsx +++ b/apps/remix-ide/src/app/providers/mainnet-vm-fork-provider.tsx @@ -2,28 +2,28 @@ import * as packageJson from '../../../../../package.json' import { BasicVMProvider } from './vm-provider' export class MainnetForkVMProvider extends BasicVMProvider { - nodeUrl: string - blockNumber: number | 'latest' - constructor (blockchain) { - super({ - name: 'vm-mainnet-fork', - displayName: 'Mainet fork -Remix VM (London)', - kind: 'provider', - description: 'Remix VM (London)', - methods: ['sendAsync', 'init'], - version: packageJson.version - }, blockchain) - this.blockchain = blockchain - this.fork = 'shanghai' - this.nodeUrl = 'https://mainnet.infura.io/v3/08b2a484451e4635a28b3d8234f24332' - this.blockNumber = 'latest' - } + nodeUrl: string + blockNumber: number | 'latest' + constructor (blockchain) { + super({ + name: 'vm-mainnet-fork', + displayName: 'Mainet fork -Remix VM (London)', + kind: 'provider', + description: 'Remix VM (London)', + methods: ['sendAsync', 'init'], + version: packageJson.version + }, blockchain) + this.blockchain = blockchain + this.fork = 'shanghai' + this.nodeUrl = 'https://mainnet.infura.io/v3/08b2a484451e4635a28b3d8234f24332' + this.blockNumber = 'latest' + } - async init () { - return { - 'fork': this.fork, - 'nodeUrl': this.nodeUrl, - 'blockNumber': this.blockNumber - } + async init () { + return { + 'fork': this.fork, + 'nodeUrl': this.nodeUrl, + 'blockNumber': this.blockNumber } + } } diff --git a/apps/remix-ide/src/app/providers/sepolia-vm-fork-provider.tsx b/apps/remix-ide/src/app/providers/sepolia-vm-fork-provider.tsx index 042bd0ed7c..74167e4f01 100644 --- a/apps/remix-ide/src/app/providers/sepolia-vm-fork-provider.tsx +++ b/apps/remix-ide/src/app/providers/sepolia-vm-fork-provider.tsx @@ -2,28 +2,28 @@ import * as packageJson from '../../../../../package.json' import { BasicVMProvider } from './vm-provider' export class SepoliaForkVMProvider extends BasicVMProvider { - nodeUrl: string - blockNumber: number | 'latest' - constructor (blockchain) { - super({ - name: 'vm-sepolia-fork', - displayName: 'Sepolia fork - Remix VM (London)', - kind: 'provider', - description: 'Remix VM (London)', - methods: ['sendAsync', 'init'], - version: packageJson.version - }, blockchain) - this.blockchain = blockchain - this.fork = 'shanghai' - this.nodeUrl = 'https://remix-sepolia.ethdevops.io' - this.blockNumber = 'latest' - } + nodeUrl: string + blockNumber: number | 'latest' + constructor (blockchain) { + super({ + name: 'vm-sepolia-fork', + displayName: 'Sepolia fork - Remix VM (London)', + kind: 'provider', + description: 'Remix VM (London)', + methods: ['sendAsync', 'init'], + version: packageJson.version + }, blockchain) + this.blockchain = blockchain + this.fork = 'shanghai' + this.nodeUrl = 'https://remix-sepolia.ethdevops.io' + this.blockNumber = 'latest' + } - async init () { - return { - 'fork': this.fork, - 'nodeUrl': this.nodeUrl, - 'blockNumber': this.blockNumber - } + async init () { + return { + 'fork': this.fork, + 'nodeUrl': this.nodeUrl, + 'blockNumber': this.blockNumber } + } } diff --git a/apps/remix-ide/src/app/providers/vm-provider.tsx b/apps/remix-ide/src/app/providers/vm-provider.tsx index 5e66152463..84c792e769 100644 --- a/apps/remix-ide/src/app/providers/vm-provider.tsx +++ b/apps/remix-ide/src/app/providers/vm-provider.tsx @@ -5,99 +5,99 @@ import { Plugin } from '@remixproject/engine' import { IProvider } from './abstract-provider' export class BasicVMProvider extends Plugin implements IProvider { - blockchain - fork: string - options: { [id: string] : any } = {} - constructor (profile, blockchain) { - super(profile) - this.blockchain = blockchain - this.fork = '' - } + blockchain + fork: string + options: { [id: string] : any } = {} + constructor (profile, blockchain) { + super(profile) + this.blockchain = blockchain + this.fork = '' + } - async init (): Promise<{ [id: string] : any }> { return {} } + async init (): Promise<{ [id: string] : any }> { return {} } - body (): JSX.Element { - return ( -
- ) - } + body (): JSX.Element { + return ( +
+ ) + } - sendAsync (data: JsonDataRequest): Promise { - return new Promise((resolve, reject) => { - this.sendAsyncInternal(data, resolve, reject) - }) - } + sendAsync (data: JsonDataRequest): Promise { + return new Promise((resolve, reject) => { + this.sendAsyncInternal(data, resolve, reject) + }) + } - private async sendAsyncInternal (data: JsonDataRequest, resolve: SuccessRequest, reject: RejectRequest): Promise { - try { - await this.blockchain.providers.vm.provider.sendAsync(data, (error, result) => { - if (error) return reject(error) - else { - resolve({ jsonrpc: '2.0', result, id: data.id }) - } - }) - } catch (error) { - reject(error) + private async sendAsyncInternal (data: JsonDataRequest, resolve: SuccessRequest, reject: RejectRequest): Promise { + try { + await this.blockchain.providers.vm.provider.sendAsync(data, (error, result) => { + if (error) return reject(error) + else { + resolve({ jsonrpc: '2.0', result, id: data.id }) } + }) + } catch (error) { + reject(error) } + } } export class MergeVMProvider extends BasicVMProvider { - constructor (blockchain) { - super({ - name: 'vm-merge', - displayName: 'Remix VM (Merge)', - kind: 'provider', - description: 'Remix VM (Merge)', - methods: ['sendAsync', 'init'], - version: packageJson.version - }, blockchain) - this.blockchain = blockchain - this.fork = 'merge' - } + constructor (blockchain) { + super({ + name: 'vm-merge', + displayName: 'Remix VM (Merge)', + kind: 'provider', + description: 'Remix VM (Merge)', + methods: ['sendAsync', 'init'], + version: packageJson.version + }, blockchain) + this.blockchain = blockchain + this.fork = 'merge' + } } export class LondonVMProvider extends BasicVMProvider { - constructor (blockchain) { - super({ - name: 'vm-london', - displayName: 'Remix VM (London)', - kind: 'provider', - description: 'Remix VM (London)', - methods: ['sendAsync', 'init'], - version: packageJson.version - }, blockchain) - this.blockchain = blockchain - this.fork = 'london' - } + constructor (blockchain) { + super({ + name: 'vm-london', + displayName: 'Remix VM (London)', + kind: 'provider', + description: 'Remix VM (London)', + methods: ['sendAsync', 'init'], + version: packageJson.version + }, blockchain) + this.blockchain = blockchain + this.fork = 'london' + } } export class BerlinVMProvider extends BasicVMProvider { - constructor (blockchain) { - super({ - name: 'vm-berlin', - displayName: 'Remix VM (Berlin)', - kind: 'provider', - description: 'Remix VM (Berlin)', - methods: ['sendAsync', 'init'], - version: packageJson.version - }, blockchain) - this.blockchain = blockchain - this.fork = 'berlin' - } + constructor (blockchain) { + super({ + name: 'vm-berlin', + displayName: 'Remix VM (Berlin)', + kind: 'provider', + description: 'Remix VM (Berlin)', + methods: ['sendAsync', 'init'], + version: packageJson.version + }, blockchain) + this.blockchain = blockchain + this.fork = 'berlin' + } } export class ShanghaiVMProvider extends BasicVMProvider { - constructor (blockchain) { - super({ - name: 'vm-shanghai', - displayName: 'Remix VM (Shanghai)', - kind: 'provider', - description: 'Remix VM (Shanghai)', - methods: ['sendAsync', 'init'], - version: packageJson.version - }, blockchain) - this.blockchain = blockchain - this.fork = 'shanghai' - } + constructor (blockchain) { + super({ + name: 'vm-shanghai', + displayName: 'Remix VM (Shanghai)', + kind: 'provider', + description: 'Remix VM (Shanghai)', + methods: ['sendAsync', 'init'], + version: packageJson.version + }, blockchain) + this.blockchain = blockchain + this.fork = 'shanghai' + } } \ No newline at end of file diff --git a/apps/remix-ide/src/app/state/registry.ts b/apps/remix-ide/src/app/state/registry.ts index dc3655293f..08f744cc32 100644 --- a/apps/remix-ide/src/app/state/registry.ts +++ b/apps/remix-ide/src/app/state/registry.ts @@ -4,35 +4,35 @@ type registryEntry = { } export default class Registry { - private static instance: Registry; - private state: any + private static instance: Registry; + private state: any - private constructor () { - this.state = {} - } - - public static getInstance (): Registry { - if (!Registry.instance) { - Registry.instance = new Registry() - } + private constructor () { + this.state = {} + } - return Registry.instance + public static getInstance (): Registry { + if (!Registry.instance) { + Registry.instance = new Registry() } - public put (entry: registryEntry) { - if (this.state[entry.name]) return this.state[entry.name] - const server = { - // uid: serveruid, - api: entry.api - } - this.state[entry.name] = { server } - return server - } + return Registry.instance + } - public get (name: string) { - const state = this.state[name] - if (!state) return - const server = state.server - return server + public put (entry: registryEntry) { + if (this.state[entry.name]) return this.state[entry.name] + const server = { + // uid: serveruid, + api: entry.api } + this.state[entry.name] = { server } + return server + } + + public get (name: string) { + const state = this.state[name] + if (!state) return + const server = state.server + return server + } } diff --git a/apps/remix-ide/src/app/tabs/analysis-tab.js b/apps/remix-ide/src/app/tabs/analysis-tab.js index 9a57c16516..929384d665 100644 --- a/apps/remix-ide/src/app/tabs/analysis-tab.js +++ b/apps/remix-ide/src/app/tabs/analysis-tab.js @@ -9,91 +9,91 @@ import { PluginViewWrapper } from '@remix-ui/helper' var EventManager = require('../../lib/events') const profile = { - name: 'solidityStaticAnalysis', - displayName: 'Solidity Analyzers', - methods: [], - events: [], - icon: 'assets/img/staticAnalysis.webp', - description: 'Analyze your code using Remix, Solhint and Slither.', - kind: 'analysis', - location: 'sidePanel', - documentation: 'https://remix-ide.readthedocs.io/en/latest/static_analysis.html', - version: packageJson.version, - maintainedBy: 'Remix' + name: 'solidityStaticAnalysis', + displayName: 'Solidity Analyzers', + methods: [], + events: [], + icon: 'assets/img/staticAnalysis.webp', + description: 'Analyze your code using Remix, Solhint and Slither.', + kind: 'analysis', + location: 'sidePanel', + documentation: 'https://remix-ide.readthedocs.io/en/latest/static_analysis.html', + version: packageJson.version, + maintainedBy: 'Remix' } class AnalysisTab extends ViewPlugin { - constructor () { - super(profile) - this.event = new EventManager() - this.events = new EventEmitter() - this.registry = Registry.getInstance() - this.element = document.createElement('div') - this.element.setAttribute('id', 'staticAnalyserView') - this._components = {} - this._components.registry = this.registry - this._deps = { - offsetToLineColumnConverter: this.registry.get( - 'offsettolinecolumnconverter').api - } - this.dispatch = null - this.hints = [] - this.basicEnabled = false - this.solhintEnabled = false - this.slitherEnabled = false + constructor () { + super(profile) + this.event = new EventManager() + this.events = new EventEmitter() + this.registry = Registry.getInstance() + this.element = document.createElement('div') + this.element.setAttribute('id', 'staticAnalyserView') + this._components = {} + this._components.registry = this.registry + this._deps = { + offsetToLineColumnConverter: this.registry.get( + 'offsettolinecolumnconverter').api } + this.dispatch = null + this.hints = [] + this.basicEnabled = false + this.solhintEnabled = false + this.slitherEnabled = false + } - async onActivation () { - this.renderComponent() - const isSolidityActive = await this.call('manager', 'isActive', 'solidity') - if (!isSolidityActive) { - await this.call('manager', 'activatePlugin', 'solidity') - } + async onActivation () { + this.renderComponent() + const isSolidityActive = await this.call('manager', 'isActive', 'solidity') + if (!isSolidityActive) { + await this.call('manager', 'activatePlugin', 'solidity') + } - this.event.register('staticAnaysisWarning', (count) => { - let payloadType = '' - const error = this.hints?.find(hint => hint.type === 'error') - if (error && this.solhintEnabled) { - payloadType = 'error' - } else { - payloadType = 'warning' - } + this.event.register('staticAnaysisWarning', (count) => { + let payloadType = '' + const error = this.hints?.find(hint => hint.type === 'error') + if (error && this.solhintEnabled) { + payloadType = 'error' + } else { + payloadType = 'warning' + } - if (count > 0) { - this.emit('statusChanged', { key: count, title: payloadType === 'error' ? `You have ${count} problem${count === 1 ? '' : 's'}` : `You have ${count} warnings`, type: payloadType }) - } else if (count === 0) { - this.emit('statusChanged', { key: 'succeed', title: 'no warnings or errors', type: 'success' }) - } else { - // count ==-1 no compilation result - this.emit('statusChanged', { key: 'none' }) - } - }) - } + if (count > 0) { + this.emit('statusChanged', { key: count, title: payloadType === 'error' ? `You have ${count} problem${count === 1 ? '' : 's'}` : `You have ${count} warnings`, type: payloadType }) + } else if (count === 0) { + this.emit('statusChanged', { key: 'succeed', title: 'no warnings or errors', type: 'success' }) + } else { + // count ==-1 no compilation result + this.emit('statusChanged', { key: 'none' }) + } + }) + } - setDispatch (dispatch) { - this.dispatch = dispatch - this.renderComponent() - } + setDispatch (dispatch) { + this.dispatch = dispatch + this.renderComponent() + } - render () { - return
- } + render () { + return
+ } - updateComponent(state) { - return - } + updateComponent(state) { + return + } - renderComponent () { - this.dispatch && this.dispatch({ - registry: this.registry, - analysisModule: this, - event: this.event - }) - } + renderComponent () { + this.dispatch && this.dispatch({ + registry: this.registry, + analysisModule: this, + event: this.event + }) + } } module.exports = AnalysisTab diff --git a/apps/remix-ide/src/app/tabs/compile-and-run.ts b/apps/remix-ide/src/app/tabs/compile-and-run.ts index 2b3c169896..84bafbf093 100644 --- a/apps/remix-ide/src/app/tabs/compile-and-run.ts +++ b/apps/remix-ide/src/app/tabs/compile-and-run.ts @@ -8,89 +8,89 @@ declare global { const _paq = window._paq = window._paq || [] export const profile = { - name: 'compileAndRun', - displayName: 'Compile and Run', - description: 'After each compilation, run the script defined in Natspec.', - methods: ['runScriptAfterCompilation'], - version: packageJson.version, - kind: 'none' + name: 'compileAndRun', + displayName: 'Compile and Run', + description: 'After each compilation, run the script defined in Natspec.', + methods: ['runScriptAfterCompilation'], + version: packageJson.version, + kind: 'none' } type listener = (event: KeyboardEvent) => void export class CompileAndRun extends Plugin { - executionListener: listener - targetFileName: string + executionListener: listener + targetFileName: string - constructor () { - super(profile) - this.executionListener = async (e) => { - // ctrl+e or command+e + constructor () { + super(profile) + this.executionListener = async (e) => { + // ctrl+e or command+e - if ((e.metaKey || e.ctrlKey) && e.shiftKey && e.keyCode === 83) { - const file = await this.call('fileManager', 'file') - if (file) { - if (file.endsWith('.sol')) { - e.preventDefault() - this.targetFileName = file - await this.call('solidity', 'compile', file) - _paq.push(['trackEvent', 'ScriptExecutor', 'CompileAndRun', 'compile_solidity']) - } else if (file.endsWith('.js') || file.endsWith('.ts')) { - e.preventDefault() - this.runScript(file, false) - _paq.push(['trackEvent', 'ScriptExecutor', 'CompileAndRun', 'run_script']) - } - } - } + if ((e.metaKey || e.ctrlKey) && e.shiftKey && e.keyCode === 83) { + const file = await this.call('fileManager', 'file') + if (file) { + if (file.endsWith('.sol')) { + e.preventDefault() + this.targetFileName = file + await this.call('solidity', 'compile', file) + _paq.push(['trackEvent', 'ScriptExecutor', 'CompileAndRun', 'compile_solidity']) + } else if (file.endsWith('.js') || file.endsWith('.ts')) { + e.preventDefault() + this.runScript(file, false) + _paq.push(['trackEvent', 'ScriptExecutor', 'CompileAndRun', 'run_script']) + } } + } } + } - runScriptAfterCompilation (fileName: string) { - this.targetFileName = fileName - _paq.push(['trackEvent', 'ScriptExecutor', 'CompileAndRun', 'request_run_script']) - } + runScriptAfterCompilation (fileName: string) { + this.targetFileName = fileName + _paq.push(['trackEvent', 'ScriptExecutor', 'CompileAndRun', 'request_run_script']) + } - async runScript (fileName, clearAllInstances) { - await this.call('terminal', 'log', { value: `running ${fileName} ...`, type: 'info' }) - try { - const exists = await this.call('fileManager', 'exists', fileName) - if (!exists) { - await this.call('terminal', 'log', { value: `${fileName} does not exist.`, type: 'info' } ) - return - } - const content = await this.call('fileManager', 'readFile', fileName) - if (clearAllInstances) { - await this.call('udapp', 'clearAllInstances') - } - await this.call('scriptRunner', 'execute', content, fileName) - } catch (e) { - this.call('notification', 'toast', e.message || e) - } - } + async runScript (fileName, clearAllInstances) { + await this.call('terminal', 'log', { value: `running ${fileName} ...`, type: 'info' }) + try { + const exists = await this.call('fileManager', 'exists', fileName) + if (!exists) { + await this.call('terminal', 'log', { value: `${fileName} does not exist.`, type: 'info' } ) + return + } + const content = await this.call('fileManager', 'readFile', fileName) + if (clearAllInstances) { + await this.call('udapp', 'clearAllInstances') + } + await this.call('scriptRunner', 'execute', content, fileName) + } catch (e) { + this.call('notification', 'toast', e.message || e) + } + } - onActivation () { - window.document.addEventListener('keydown', this.executionListener) + onActivation () { + window.document.addEventListener('keydown', this.executionListener) - this.on('compilerMetadata', 'artefactsUpdated', async (fileName, contract) => { - if (this.targetFileName === contract.file) { - this.targetFileName = null - if (contract.object && contract.object.devdoc['custom:dev-run-script']) { - const file = contract.object.devdoc['custom:dev-run-script'] - if (file) { - this.runScript(file, true) - _paq.push(['trackEvent', 'ScriptExecutor', 'CompileAndRun', 'run_script_after_compile']) - } else { - this.call('notification', 'toast', 'You have not set a script to run. Set it with @custom:dev-run-script NatSpec tag.') - } - } else { - this.call('notification', 'toast', 'You have not set a script to run. Set it with @custom:dev-run-script NatSpec tag.') - } - } - }) - } + this.on('compilerMetadata', 'artefactsUpdated', async (fileName, contract) => { + if (this.targetFileName === contract.file) { + this.targetFileName = null + if (contract.object && contract.object.devdoc['custom:dev-run-script']) { + const file = contract.object.devdoc['custom:dev-run-script'] + if (file) { + this.runScript(file, true) + _paq.push(['trackEvent', 'ScriptExecutor', 'CompileAndRun', 'run_script_after_compile']) + } else { + this.call('notification', 'toast', 'You have not set a script to run. Set it with @custom:dev-run-script NatSpec tag.') + } + } else { + this.call('notification', 'toast', 'You have not set a script to run. Set it with @custom:dev-run-script NatSpec tag.') + } + } + }) + } - onDeactivation () { - window.document.removeEventListener('keydown', this.executionListener) - this.off('compilerMetadata', 'artefactsUpdated') - } + onDeactivation () { + window.document.removeEventListener('keydown', this.executionListener) + this.off('compilerMetadata', 'artefactsUpdated') + } } diff --git a/apps/remix-ide/src/app/tabs/compile-tab.js b/apps/remix-ide/src/app/tabs/compile-tab.js index 8531f2ead2..0cc42ad65e 100644 --- a/apps/remix-ide/src/app/tabs/compile-tab.js +++ b/apps/remix-ide/src/app/tabs/compile-tab.js @@ -11,17 +11,17 @@ import { compilerConfigChangedToastMsg, compileToastMsg } from '@remix-ui/helper import { isNative } from '../../remixAppManager' const profile = { - name: 'solidity', - displayName: 'Solidity compiler', - icon: 'assets/img/solidity.webp', - description: 'Compile solidity contracts', - kind: 'compiler', - permission: true, - location: 'sidePanel', - documentation: 'https://remix-ide.readthedocs.io/en/latest/compile.html', - version: packageJson.version, - maintainedBy: 'Remix', - methods: ['getCompilationResult', 'compile', 'compileWithParameters', 'setCompilerConfig', 'compileFile', 'getCompilerState', 'getCompilerParameters', 'getCompiler'] + name: 'solidity', + displayName: 'Solidity compiler', + icon: 'assets/img/solidity.webp', + description: 'Compile solidity contracts', + kind: 'compiler', + permission: true, + location: 'sidePanel', + documentation: 'https://remix-ide.readthedocs.io/en/latest/compile.html', + version: packageJson.version, + maintainedBy: 'Remix', + methods: ['getCompilationResult', 'compile', 'compileWithParameters', 'setCompilerConfig', 'compileFile', 'getCompilerState', 'getCompilerParameters', 'getCompiler'] } // EditorApi: @@ -29,134 +29,134 @@ const profile = { // - methods: ['getCompilationResult'] class CompileTab extends CompilerApiMixin(ViewPlugin) { // implements ICompilerApi - constructor (config, fileManager) { - super(profile) - this.fileManager = fileManager - this.config = config - this.queryParams = new QueryParams() - this.compileTabLogic = new CompileTabLogic(this, this.contentImport) - this.compiler = this.compileTabLogic.compiler - this.compileTabLogic.init() - this.initCompilerApi() - this.el = document.createElement('div') - this.el.setAttribute('id', 'compileTabView') - } - - renderComponent () { - // empty method, is a state update needed? - } - - onCurrentFileChanged () { - this.renderComponent() - } - - // onResetResults () { - // this.renderComponent() - // } - - onSetWorkspace () { - this.renderComponent() - } - - onFileRemoved () { - this.renderComponent() - } - - onNoFileSelected () { - this.renderComponent() - } - - onFileClosed () { - this.renderComponent() - } - - onCompilationFinished () { - this.renderComponent() - } - - render () { - return
- } - - async compileWithParameters (compilationTargets, settings) { - return await super.compileWithParameters(compilationTargets, settings) - } - - getCompilationResult () { - return super.getCompilationResult() - } - - getFileManagerMode () { - return this.fileManager.mode - } - - /** + constructor (config, fileManager) { + super(profile) + this.fileManager = fileManager + this.config = config + this.queryParams = new QueryParams() + this.compileTabLogic = new CompileTabLogic(this, this.contentImport) + this.compiler = this.compileTabLogic.compiler + this.compileTabLogic.init() + this.initCompilerApi() + this.el = document.createElement('div') + this.el.setAttribute('id', 'compileTabView') + } + + renderComponent () { + // empty method, is a state update needed? + } + + onCurrentFileChanged () { + this.renderComponent() + } + + // onResetResults () { + // this.renderComponent() + // } + + onSetWorkspace () { + this.renderComponent() + } + + onFileRemoved () { + this.renderComponent() + } + + onNoFileSelected () { + this.renderComponent() + } + + onFileClosed () { + this.renderComponent() + } + + onCompilationFinished () { + this.renderComponent() + } + + render () { + return
+ } + + async compileWithParameters (compilationTargets, settings) { + return await super.compileWithParameters(compilationTargets, settings) + } + + getCompilationResult () { + return super.getCompilationResult() + } + + getFileManagerMode () { + return this.fileManager.mode + } + + /** * set the compiler configuration * This function is used by remix-plugin compiler API. * @param {object} settings {evmVersion, optimize, runs, version, language} */ - setCompilerConfig (settings) { - super.setCompilerConfig(settings) - this.renderComponent() - // @todo(#2875) should use loading compiler return value to check whether the compiler is loaded instead of "setInterval" - const value = JSON.stringify(settings, null, '\t') - - this.call('notification', 'toast', compilerConfigChangedToastMsg(this.currentRequest.from, value)) - } - - compile (fileName) { - if (!isNative(this.currentRequest.from)) this.call('notification', 'toast', compileToastMsg(this.currentRequest.from, fileName)) - super.compile(fileName) - } - - compileFile (event) { - return super.compileFile(event) - } - - async onActivation () { - super.onActivation() - this.on('filePanel', 'workspaceInitializationCompleted', () => { - this.call('filePanel', 'registerContextMenuItem', { - id: 'solidity', - name: 'compileFile', - label: 'Compile', - type: [], - extension: ['.sol'], - path: [], - pattern: [], - group: 6 - }) - }) - try { - this.currentFile = await this.call('fileManager', 'file') - } catch (error) { - if (error.message !== 'Error: No such file or directory No file selected') throw error - } - } - - getCompiler () { - return this.compileTabLogic.compiler - } - - getCompilerParameters () { - const params = this.queryParams.get() - params.evmVersion = params.evmVersion === 'null' || params.evmVersion === 'undefined' ? null : params.evmVersion - params.optimize = (params.optimize === 'false' || params.optimize === null || params.optimize === undefined) ? false : params.optimize - params.optimize = params.optimize === 'true' ? true : params.optimize - return params - } - - setCompilerParameters (params) { - this.queryParams.update(params) - } - - async getAppParameter (name) { - return await this.call('config', 'getAppParameter', name) - } - - async setAppParameter (name, value) { - await this.call('config', 'setAppParameter', name, value) - } + setCompilerConfig (settings) { + super.setCompilerConfig(settings) + this.renderComponent() + // @todo(#2875) should use loading compiler return value to check whether the compiler is loaded instead of "setInterval" + const value = JSON.stringify(settings, null, '\t') + + this.call('notification', 'toast', compilerConfigChangedToastMsg(this.currentRequest.from, value)) + } + + compile (fileName) { + if (!isNative(this.currentRequest.from)) this.call('notification', 'toast', compileToastMsg(this.currentRequest.from, fileName)) + super.compile(fileName) + } + + compileFile (event) { + return super.compileFile(event) + } + + async onActivation () { + super.onActivation() + this.on('filePanel', 'workspaceInitializationCompleted', () => { + this.call('filePanel', 'registerContextMenuItem', { + id: 'solidity', + name: 'compileFile', + label: 'Compile', + type: [], + extension: ['.sol'], + path: [], + pattern: [], + group: 6 + }) + }) + try { + this.currentFile = await this.call('fileManager', 'file') + } catch (error) { + if (error.message !== 'Error: No such file or directory No file selected') throw error + } + } + + getCompiler () { + return this.compileTabLogic.compiler + } + + getCompilerParameters () { + const params = this.queryParams.get() + params.evmVersion = params.evmVersion === 'null' || params.evmVersion === 'undefined' ? null : params.evmVersion + params.optimize = (params.optimize === 'false' || params.optimize === null || params.optimize === undefined) ? false : params.optimize + params.optimize = params.optimize === 'true' ? true : params.optimize + return params + } + + setCompilerParameters (params) { + this.queryParams.update(params) + } + + async getAppParameter (name) { + return await this.call('config', 'getAppParameter', name) + } + + async setAppParameter (name, value) { + await this.call('config', 'setAppParameter', name, value) + } } module.exports = CompileTab diff --git a/apps/remix-ide/src/app/tabs/debugger-tab.js b/apps/remix-ide/src/app/tabs/debugger-tab.js index cd06ea4ffa..b7d07b3e08 100644 --- a/apps/remix-ide/src/app/tabs/debugger-tab.js +++ b/apps/remix-ide/src/app/tabs/debugger-tab.js @@ -9,106 +9,106 @@ import { compilationFinishedToastMsg, compilingToastMsg, localCompilationToastMs const css = require('./styles/debugger-tab-styles') const profile = { - name: 'debugger', - displayName: 'Debugger', - methods: ['debug', 'getTrace', 'decodeLocalVariable', 'decodeStateVariable', 'globalContext'], - events: [], - icon: 'assets/img/debuggerLogo.webp', - description: 'Debug transactions', - kind: 'debugging', - location: 'sidePanel', - documentation: 'https://remix-ide.readthedocs.io/en/latest/debugger.html', - version: packageJson.version, - maintainedBy: 'Remix' + name: 'debugger', + displayName: 'Debugger', + methods: ['debug', 'getTrace', 'decodeLocalVariable', 'decodeStateVariable', 'globalContext'], + events: [], + icon: 'assets/img/debuggerLogo.webp', + description: 'Debug transactions', + kind: 'debugging', + location: 'sidePanel', + documentation: 'https://remix-ide.readthedocs.io/en/latest/debugger.html', + version: packageJson.version, + maintainedBy: 'Remix' } export class DebuggerTab extends DebuggerApiMixin(ViewPlugin) { - constructor () { - super(profile) - this.el = document.createElement('div') - this.el.setAttribute('id', 'debugView') - this.el.classList.add(css.debuggerTabView) - this.initDebuggerApi() - } + constructor () { + super(profile) + this.el = document.createElement('div') + this.el.setAttribute('id', 'debugView') + this.el.classList.add(css.debuggerTabView) + this.initDebuggerApi() + } - render () { - this.on('fetchAndCompile', 'compiling', (settings) => { - settings = JSON.stringify(settings, null, '\t') - this.call('notification', 'toast', compilingToastMsg(settings)) - }) + render () { + this.on('fetchAndCompile', 'compiling', (settings) => { + settings = JSON.stringify(settings, null, '\t') + this.call('notification', 'toast', compilingToastMsg(settings)) + }) - this.on('fetchAndCompile', 'compilationFailed', (data) => { - this.call('notification', 'toast', compilationFinishedToastMsg()) - }) + this.on('fetchAndCompile', 'compilationFailed', (data) => { + this.call('notification', 'toast', compilationFinishedToastMsg()) + }) - this.on('fetchAndCompile', 'notFound', (contractAddress) => { - this.call('notification', 'toast', notFoundToastMsg(contractAddress)) - }) + this.on('fetchAndCompile', 'notFound', (contractAddress) => { + this.call('notification', 'toast', notFoundToastMsg(contractAddress)) + }) - this.on('fetchAndCompile', 'sourceVerificationNotAvailable', () => { - this.call('notification', 'toast', sourceVerificationNotAvailableToastMsg()) - }) - const onReady = (api) => { this.api = api } - return
- } + this.on('fetchAndCompile', 'sourceVerificationNotAvailable', () => { + this.call('notification', 'toast', sourceVerificationNotAvailableToastMsg()) + }) + const onReady = (api) => { this.api = api } + return
+ } - showMessage (title, message) { - try { - this.call('notification', 'alert', { - id: 'debuggerTabShowMessage', - title, - message: bleach.sanitize(message) - }) - } catch (e) { - console.log(e) - } + showMessage (title, message) { + try { + this.call('notification', 'alert', { + id: 'debuggerTabShowMessage', + title, + message: bleach.sanitize(message) + }) + } catch (e) { + console.log(e) } + } - async decodeLocalVariable (variableId) { - if (!this.debuggerBackend) return null - return await this.debuggerBackend.debugger.decodeLocalVariableByIdAtCurrentStep(this.debuggerBackend.step_manager.currentStepIndex, variableId) - } + async decodeLocalVariable (variableId) { + if (!this.debuggerBackend) return null + return await this.debuggerBackend.debugger.decodeLocalVariableByIdAtCurrentStep(this.debuggerBackend.step_manager.currentStepIndex, variableId) + } - async decodeStateVariable (variableId) { - if (!this.debuggerBackend) return null - return await this.debuggerBackend.debugger.decodeStateVariableByIdAtCurrentStep(this.debuggerBackend.step_manager.currentStepIndex, variableId) - } + async decodeStateVariable (variableId) { + if (!this.debuggerBackend) return null + return await this.debuggerBackend.debugger.decodeStateVariableByIdAtCurrentStep(this.debuggerBackend.step_manager.currentStepIndex, variableId) + } - async globalContext () { - if (this.api?.globalContext) { - const { tx, block } = await this.api.globalContext() - const blockContext = { - 'chainid': tx.chainId, - 'coinbase': block.miner, - 'difficulty': block.difficulty, - 'gaslimit': block.gasLimit, - 'number': block.number, - 'timestamp': block.timestamp, - } - if (block.baseFeePerGas) { - blockContext['basefee'] = Web3.utils.toBN(block.baseFeePerGas).toString(10) + ` Wei (${block.baseFeePerGas})` - } - const msg = { - 'sender': tx.from, - 'sig': tx.input.substring(0, 10), - 'value': tx.value + ' Wei' - } + async globalContext () { + if (this.api?.globalContext) { + const { tx, block } = await this.api.globalContext() + const blockContext = { + 'chainid': tx.chainId, + 'coinbase': block.miner, + 'difficulty': block.difficulty, + 'gaslimit': block.gasLimit, + 'number': block.number, + 'timestamp': block.timestamp, + } + if (block.baseFeePerGas) { + blockContext['basefee'] = Web3.utils.toBN(block.baseFeePerGas).toString(10) + ` Wei (${block.baseFeePerGas})` + } + const msg = { + 'sender': tx.from, + 'sig': tx.input.substring(0, 10), + 'value': tx.value + ' Wei' + } - const txOrigin = { - 'origin': tx.from - } + const txOrigin = { + 'origin': tx.from + } - return { - block: blockContext, - msg, - tx: txOrigin - } - } else { - return { - block: null, - msg: null, - tx: null - } - } + return { + block: blockContext, + msg, + tx: txOrigin + } + } else { + return { + block: null, + msg: null, + tx: null + } } + } } diff --git a/apps/remix-ide/src/app/tabs/locale-module.js b/apps/remix-ide/src/app/tabs/locale-module.js index 0487f94e96..c368e1aec1 100644 --- a/apps/remix-ide/src/app/tabs/locale-module.js +++ b/apps/remix-ide/src/app/tabs/locale-module.js @@ -8,71 +8,71 @@ import zhJson from './locales/zh' const _paq = window._paq = window._paq || [] const locales = [ - { code: 'en', name: 'English', localeName: 'English', messages: enJson }, - { code: 'zh', name: 'Chinese Simplified', localeName: '简体中文', messages: zhJson }, + { code: 'en', name: 'English', localeName: 'English', messages: enJson }, + { code: 'zh', name: 'Chinese Simplified', localeName: '简体中文', messages: zhJson }, ] const profile = { - name: 'locale', - events: ['localeChanged'], - methods: ['switchLocale', 'getLocales', 'currentLocale'], - version: packageJson.version, - kind: 'locale' + name: 'locale', + events: ['localeChanged'], + methods: ['switchLocale', 'getLocales', 'currentLocale'], + version: packageJson.version, + kind: 'locale' } export class LocaleModule extends Plugin { - constructor () { - super(profile) - this.events = new EventEmitter() - this._deps = { - config: Registry.getInstance().get('config') && Registry.getInstance().get('config').api - } - this.locales = {} - locales.map((locale) => { - this.locales[locale.code.toLocaleLowerCase()] = locale - }) - this._paq = _paq - this.queryParams = new QueryParams() - let queryLocale = this.queryParams.get().lang - queryLocale = queryLocale && queryLocale.toLocaleLowerCase() - queryLocale = this.locales[queryLocale] ? queryLocale : null - let currentLocale = (this._deps.config && this._deps.config.get('settings/locale')) || null - currentLocale = currentLocale && currentLocale.toLocaleLowerCase() - currentLocale = this.locales[currentLocale] ? currentLocale : null - this.currentLocaleState = { queryLocale, currentLocale } - this.active = queryLocale || currentLocale || 'en' - this.forced = !!queryLocale - this.queryParams.update({ lang: this.active }) + constructor () { + super(profile) + this.events = new EventEmitter() + this._deps = { + config: Registry.getInstance().get('config') && Registry.getInstance().get('config').api } + this.locales = {} + locales.map((locale) => { + this.locales[locale.code.toLocaleLowerCase()] = locale + }) + this._paq = _paq + this.queryParams = new QueryParams() + let queryLocale = this.queryParams.get().lang + queryLocale = queryLocale && queryLocale.toLocaleLowerCase() + queryLocale = this.locales[queryLocale] ? queryLocale : null + let currentLocale = (this._deps.config && this._deps.config.get('settings/locale')) || null + currentLocale = currentLocale && currentLocale.toLocaleLowerCase() + currentLocale = this.locales[currentLocale] ? currentLocale : null + this.currentLocaleState = { queryLocale, currentLocale } + this.active = queryLocale || currentLocale || 'en' + this.forced = !!queryLocale + this.queryParams.update({ lang: this.active }) + } - /** Return the active locale */ - currentLocale () { - return this.locales[this.active] - } + /** Return the active locale */ + currentLocale () { + return this.locales[this.active] + } - /** Returns all locales as an array */ - getLocales () { - return Object.keys(this.locales).map(key => this.locales[key]) - } + /** Returns all locales as an array */ + getLocales () { + return Object.keys(this.locales).map(key => this.locales[key]) + } - /** + /** * Change the current locale * @param {string} [localeCode] - The code of the locale */ - switchLocale (localeCode) { - localeCode = localeCode && localeCode.toLocaleLowerCase() - if (localeCode && !Object.keys(this.locales).includes(localeCode)) { - throw new Error(`Locale ${localeCode} doesn't exist`) - } - const next = localeCode || this.active // Name - if (next === this.active) return // --> exit out of this method - _paq.push(['trackEvent', 'localeModule', 'switchTo', next]) - const nextLocale = this.locales[next] // Locale - if (!this.forced) this._deps.config.set('settings/locale', next) - - if (localeCode) this.active = localeCode - this.queryParams.update({ lang: localeCode }) - this.emit('localeChanged', nextLocale) - this.events.emit('localeChanged', nextLocale) + switchLocale (localeCode) { + localeCode = localeCode && localeCode.toLocaleLowerCase() + if (localeCode && !Object.keys(this.locales).includes(localeCode)) { + throw new Error(`Locale ${localeCode} doesn't exist`) } + const next = localeCode || this.active // Name + if (next === this.active) return // --> exit out of this method + _paq.push(['trackEvent', 'localeModule', 'switchTo', next]) + const nextLocale = this.locales[next] // Locale + if (!this.forced) this._deps.config.set('settings/locale', next) + + if (localeCode) this.active = localeCode + this.queryParams.update({ lang: localeCode }) + this.emit('localeChanged', nextLocale) + this.events.emit('localeChanged', nextLocale) + } } diff --git a/apps/remix-ide/src/app/tabs/locales/en/index.js b/apps/remix-ide/src/app/tabs/locales/en/index.js index a5b807a902..a9ef03c64c 100644 --- a/apps/remix-ide/src/app/tabs/locales/en/index.js +++ b/apps/remix-ide/src/app/tabs/locales/en/index.js @@ -12,16 +12,16 @@ import solidityUnitTestingJson from './solidityUnitTesting.json'; import permissionHandlerJson from './permissionHandler.json'; export default { - ...debuggerJson, - ...filePanelJson, - ...homeJson, - ...panelJson, - ...pluginManagerJson, - ...searchJson, - ...settingsJson, - ...solidityJson, - ...terminalJson, - ...udappJson, - ...solidityUnitTestingJson, - ...permissionHandlerJson, + ...debuggerJson, + ...filePanelJson, + ...homeJson, + ...panelJson, + ...pluginManagerJson, + ...searchJson, + ...settingsJson, + ...solidityJson, + ...terminalJson, + ...udappJson, + ...solidityUnitTestingJson, + ...permissionHandlerJson, } diff --git a/apps/remix-ide/src/app/tabs/locales/zh/index.js b/apps/remix-ide/src/app/tabs/locales/zh/index.js index 6b77c72871..1a175f14f3 100644 --- a/apps/remix-ide/src/app/tabs/locales/zh/index.js +++ b/apps/remix-ide/src/app/tabs/locales/zh/index.js @@ -15,16 +15,16 @@ import enJson from '../en'; // There may have some un-translated content. Always fill in the gaps with EN JSON. // No need for a defaultMessage prop when render a FormattedMessage component. export default Object.assign({}, enJson, { - ...debuggerJson, - ...filePanelJson, - ...homeJson, - ...panelJson, - ...pluginManagerJson, - ...searchJson, - ...settingsJson, - ...solidityJson, - ...terminalJson, - ...udappJson, - ...solidityUnitTestingJson, - ...permissionHandlerJson, + ...debuggerJson, + ...filePanelJson, + ...homeJson, + ...panelJson, + ...pluginManagerJson, + ...searchJson, + ...settingsJson, + ...solidityJson, + ...terminalJson, + ...udappJson, + ...solidityUnitTestingJson, + ...permissionHandlerJson, }) diff --git a/apps/remix-ide/src/app/tabs/network-module.js b/apps/remix-ide/src/app/tabs/network-module.js index c1cc1e12b5..635a431488 100644 --- a/apps/remix-ide/src/app/tabs/network-module.js +++ b/apps/remix-ide/src/app/tabs/network-module.js @@ -3,11 +3,11 @@ import * as packageJson from '../../../../../package.json' import { Web3 } from 'web3' export const profile = { - name: 'network', - description: 'Manage the network (mainnet, ropsten, goerli...) and the provider (web3, vm, injected)', - methods: ['getNetworkProvider', 'getEndpoint', 'detectNetwork', 'addNetwork', 'removeNetwork'], - version: packageJson.version, - kind: 'network' + name: 'network', + description: 'Manage the network (mainnet, ropsten, goerli...) and the provider (web3, vm, injected)', + methods: ['getNetworkProvider', 'getEndpoint', 'detectNetwork', 'addNetwork', 'removeNetwork'], + version: packageJson.version, + kind: 'network' } // Network API has : @@ -15,46 +15,46 @@ export const profile = { // - methods: ['getNetworkProvider', 'getEndpoint', 'detectNetwork', 'addNetwork', 'removeNetwork'] export class NetworkModule extends Plugin { - constructor (blockchain) { - super(profile) - this.blockchain = blockchain - // TODO: See with remix-lib to make sementic coherent - this.blockchain.event.register('contextChanged', (provider) => { - this.emit('providerChanged', provider) - }) - } - - /** Return the current network provider (web3, vm, injected) */ - getNetworkProvider () { - return this.blockchain.getProvider() - } - - /** Return the current network */ - detectNetwork () { - return new Promise((resolve, reject) => { - this.blockchain.detectNetwork((error, network) => { - error ? reject(error) : resolve(network) - }) - }) - } - - /** Return the url only if network provider is 'web3' */ - getEndpoint () { - const provider = this.blockchain.getProvider() - if (provider !== 'web3') { - throw new Error('no endpoint: current provider is either injected or vm') - } - return this.blockchain.web3().currentProvider.host - } - - /** Add a custom network to the list of available networks */ - addNetwork (network) { // { name, url } - const provider = network.url === 'ipc' ? new Web3.providers.IpcProvider() : new Web3.providers.HttpProvider(network.url) - this.blockchain.addProvider({ name: network.name, provider }) - } - - /** Remove a network to the list of availble networks */ - removeNetwork (name) { - this.blockchain.removeProvider(name) + constructor (blockchain) { + super(profile) + this.blockchain = blockchain + // TODO: See with remix-lib to make sementic coherent + this.blockchain.event.register('contextChanged', (provider) => { + this.emit('providerChanged', provider) + }) + } + + /** Return the current network provider (web3, vm, injected) */ + getNetworkProvider () { + return this.blockchain.getProvider() + } + + /** Return the current network */ + detectNetwork () { + return new Promise((resolve, reject) => { + this.blockchain.detectNetwork((error, network) => { + error ? reject(error) : resolve(network) + }) + }) + } + + /** Return the url only if network provider is 'web3' */ + getEndpoint () { + const provider = this.blockchain.getProvider() + if (provider !== 'web3') { + throw new Error('no endpoint: current provider is either injected or vm') } + return this.blockchain.web3().currentProvider.host + } + + /** Add a custom network to the list of available networks */ + addNetwork (network) { // { name, url } + const provider = network.url === 'ipc' ? new Web3.providers.IpcProvider() : new Web3.providers.HttpProvider(network.url) + this.blockchain.addProvider({ name: network.name, provider }) + } + + /** Remove a network to the list of availble networks */ + removeNetwork (name) { + this.blockchain.removeProvider(name) + } } diff --git a/apps/remix-ide/src/app/tabs/runTab/model/recorder.js b/apps/remix-ide/src/app/tabs/runTab/model/recorder.js index f1d6739117..69868766b1 100644 --- a/apps/remix-ide/src/app/tabs/runTab/model/recorder.js +++ b/apps/remix-ide/src/app/tabs/runTab/model/recorder.js @@ -12,110 +12,110 @@ import { addressToString } from '@remix-ui/helper' const _paq = window._paq = window._paq || [] //eslint-disable-line const profile = { - name: 'recorder', - displayName: 'Recorder', - description: 'Records transactions to save and run', - version: packageJson.version, - methods: [ ] + name: 'recorder', + displayName: 'Recorder', + description: 'Records transactions to save and run', + version: packageJson.version, + methods: [ ] } /** * Record transaction as long as the user create them. */ class Recorder extends Plugin { - constructor (blockchain) { - super(profile) - this.event = new EventManager() - this.blockchain = blockchain - this.data = { _listen: true, _replay: false, journal: [], _createdContracts: {}, _createdContractsReverse: {}, _usedAccounts: {}, _abis: {}, _contractABIReferences: {}, _linkReferences: {} } + constructor (blockchain) { + super(profile) + this.event = new EventManager() + this.blockchain = blockchain + this.data = { _listen: true, _replay: false, journal: [], _createdContracts: {}, _createdContractsReverse: {}, _usedAccounts: {}, _abis: {}, _contractABIReferences: {}, _linkReferences: {} } - this.blockchain.event.register('initiatingTransaction', (timestamp, tx, payLoad) => { - if (tx.useCall) return - var { from, to, value } = tx + this.blockchain.event.register('initiatingTransaction', (timestamp, tx, payLoad) => { + if (tx.useCall) return + var { from, to, value } = tx - // convert to and from to tokens - if (this.data._listen) { - var record = { - value, - inputs: txHelper.serializeInputs(payLoad.funAbi), - parameters: payLoad.funArgs, - name: payLoad.funAbi.name, - type: payLoad.funAbi.type - } - if (!to) { - var abi = payLoad.contractABI - var keccak = bufferToHex(hash.keccakFromString(JSON.stringify(abi))) - record.abi = keccak - record.contractName = payLoad.contractName - record.bytecode = payLoad.contractBytecode - record.linkReferences = payLoad.linkReferences - if (record.linkReferences && Object.keys(record.linkReferences).length) { - for (var file in record.linkReferences) { - for (var lib in record.linkReferences[file]) { - this.data._linkReferences[lib] = '
' - } - } - } - this.data._abis[keccak] = abi + // convert to and from to tokens + if (this.data._listen) { + var record = { + value, + inputs: txHelper.serializeInputs(payLoad.funAbi), + parameters: payLoad.funArgs, + name: payLoad.funAbi.name, + type: payLoad.funAbi.type + } + if (!to) { + var abi = payLoad.contractABI + var keccak = bufferToHex(hash.keccakFromString(JSON.stringify(abi))) + record.abi = keccak + record.contractName = payLoad.contractName + record.bytecode = payLoad.contractBytecode + record.linkReferences = payLoad.linkReferences + if (record.linkReferences && Object.keys(record.linkReferences).length) { + for (var file in record.linkReferences) { + for (var lib in record.linkReferences[file]) { + this.data._linkReferences[lib] = '
' + } + } + } + this.data._abis[keccak] = abi - this.data._contractABIReferences[timestamp] = keccak - } else { - var creationTimestamp = this.data._createdContracts[to] - record.to = `created{${creationTimestamp}}` - record.abi = this.data._contractABIReferences[creationTimestamp] - } - for (var p in record.parameters) { - var thisarg = record.parameters[p] - var thistimestamp = this.data._createdContracts[thisarg] - if (thistimestamp) record.parameters[p] = `created{${thistimestamp}}` - } + this.data._contractABIReferences[timestamp] = keccak + } else { + var creationTimestamp = this.data._createdContracts[to] + record.to = `created{${creationTimestamp}}` + record.abi = this.data._contractABIReferences[creationTimestamp] + } + for (var p in record.parameters) { + var thisarg = record.parameters[p] + var thistimestamp = this.data._createdContracts[thisarg] + if (thistimestamp) record.parameters[p] = `created{${thistimestamp}}` + } - this.blockchain.getAccounts((error, accounts) => { - if (error) return console.log(error) - record.from = `account{${accounts.indexOf(from)}}` - this.data._usedAccounts[record.from] = from - this.append(timestamp, record) - }) - } + this.blockchain.getAccounts((error, accounts) => { + if (error) return console.log(error) + record.from = `account{${accounts.indexOf(from)}}` + this.data._usedAccounts[record.from] = from + this.append(timestamp, record) }) + } + }) - this.blockchain.event.register('transactionExecuted', (error, from, to, data, call, txResult, timestamp, _payload) => { - if (error) return console.log(error) - if (call) return - const rawAddress = txResult.receipt.contractAddress - if (!rawAddress) return // not a contract creation - const address = addressToString(rawAddress) - // save back created addresses for the convertion from tokens to real adresses - this.data._createdContracts[address] = timestamp - this.data._createdContractsReverse[timestamp] = address - }) - this.blockchain.event.register('contextChanged', this.clearAll.bind(this)) - this.event.register('newTxRecorded', (count) => { - this.event.trigger('recorderCountChange', [count]) - }) - this.event.register('cleared', () => { - this.event.trigger('recorderCountChange', [0]) - }) - } + this.blockchain.event.register('transactionExecuted', (error, from, to, data, call, txResult, timestamp, _payload) => { + if (error) return console.log(error) + if (call) return + const rawAddress = txResult.receipt.contractAddress + if (!rawAddress) return // not a contract creation + const address = addressToString(rawAddress) + // save back created addresses for the convertion from tokens to real adresses + this.data._createdContracts[address] = timestamp + this.data._createdContractsReverse[timestamp] = address + }) + this.blockchain.event.register('contextChanged', this.clearAll.bind(this)) + this.event.register('newTxRecorded', (count) => { + this.event.trigger('recorderCountChange', [count]) + }) + this.event.register('cleared', () => { + this.event.trigger('recorderCountChange', [0]) + }) + } - /** + /** * stop/start saving txs. If not listenning, is basically in replay mode * * @param {Bool} listen */ - setListen (listen) { - this.data._listen = listen - this.data._replay = !listen - } + setListen (listen) { + this.data._listen = listen + this.data._replay = !listen + } - extractTimestamp (value) { - var stamp = /created{(.*)}/g.exec(value) - if (stamp) { - return stamp[1] - } - return null + extractTimestamp (value) { + var stamp = /created{(.*)}/g.exec(value) + if (stamp) { + return stamp[1] } + return null + } - /** + /** * convert back from/to from tokens to real addresses * * @param {Object} record @@ -123,66 +123,66 @@ class Recorder extends Plugin { * @param {Object} options * */ - resolveAddress (record, accounts, options) { - if (record.to) { - var stamp = this.extractTimestamp(record.to) - if (stamp) { - record.to = this.data._createdContractsReverse[stamp] - } - } - record.from = accounts[record.from] - // @TODO: writing browser test - return record + resolveAddress (record, accounts, options) { + if (record.to) { + var stamp = this.extractTimestamp(record.to) + if (stamp) { + record.to = this.data._createdContractsReverse[stamp] + } } + record.from = accounts[record.from] + // @TODO: writing browser test + return record + } - /** + /** * save the given @arg record * * @param {Number/String} timestamp * @param {Object} record * */ - append (timestamp, record) { - this.data.journal.push({ timestamp, record }) - this.event.trigger('newTxRecorded', [this.data.journal.length]) - } + append (timestamp, record) { + this.data.journal.push({ timestamp, record }) + this.event.trigger('newTxRecorded', [this.data.journal.length]) + } - /** + /** * basically return the records + associate values (like abis / accounts) * */ - getAll () { - var records = [].concat(this.data.journal) - return { - accounts: this.data._usedAccounts, - linkReferences: this.data._linkReferences, - transactions: records.sort((A, B) => { - var stampA = A.timestamp - var stampB = B.timestamp - return stampA - stampB - }), - abis: this.data._abis - } + getAll () { + var records = [].concat(this.data.journal) + return { + accounts: this.data._usedAccounts, + linkReferences: this.data._linkReferences, + transactions: records.sort((A, B) => { + var stampA = A.timestamp + var stampB = B.timestamp + return stampA - stampB + }), + abis: this.data._abis } + } - /** + /** * delete the seen transactions * */ - clearAll () { - this.data._listen = true - this.data._replay = false - this.data.journal = [] - this.data._createdContracts = {} - this.data._createdContractsReverse = {} - this.data._usedAccounts = {} - this.data._abis = {} - this.data._contractABIReferences = {} - this.data._linkReferences = {} - this.event.trigger('cleared', []) - } + clearAll () { + this.data._listen = true + this.data._replay = false + this.data.journal = [] + this.data._createdContracts = {} + this.data._createdContractsReverse = {} + this.data._usedAccounts = {} + this.data._abis = {} + this.data._contractABIReferences = {} + this.data._linkReferences = {} + this.event.trigger('cleared', []) + } - /** + /** * run the list of records * * @param {Object} records @@ -199,134 +199,134 @@ class Recorder extends Plugin { * @param {Function} newContractFn * */ - run (records, accounts, options, abis, linkReferences, confirmationCb, continueCb, promptCb, alertCb, logCallBack, liveMode, newContractFn) { - this.setListen(false) - const liveMsg = liveMode ? ' with updated contracts' : '' - logCallBack(`Running ${records.length} transaction(s)${liveMsg} ...`) - async.eachOfSeries(records, async (tx, index, cb) => { - if (liveMode && tx.record.type === 'constructor') { - // resolve the bytecode and ABI using the contract name, this ensure getting the last compiled one. - const data = await this.call('compilerArtefacts', 'getArtefactsByContractName', tx.record.contractName) - tx.record.bytecode = data.artefact.evm.bytecode.object - const updatedABIKeccak = bufferToHex(hash.keccakFromString(JSON.stringify(data.artefact.abi))) - abis[updatedABIKeccak] = data.artefact.abi - tx.record.abi = updatedABIKeccak - } - var record = this.resolveAddress(tx.record, accounts, options) - var abi = abis[tx.record.abi] - if (!abi) { - return alertCb('cannot find ABI for ' + tx.record.abi + '. Execution stopped at ' + index) - } - /* Resolve Library */ - if (record.linkReferences && Object.keys(record.linkReferences).length) { - for (var k in linkReferences) { - var link = linkReferences[k] - var timestamp = this.extractTimestamp(link) - if (timestamp && this.data._createdContractsReverse[timestamp]) { - link = this.data._createdContractsReverse[timestamp] - } - tx.record.bytecode = format.linkLibraryStandardFromlinkReferences(k, link.replace('0x', ''), tx.record.bytecode, tx.record.linkReferences) - } - } - /* Encode params */ - var fnABI - if (tx.record.type === 'constructor') { - fnABI = txHelper.getConstructorInterface(abi) - } else if (tx.record.type === 'fallback') { - fnABI = txHelper.getFallbackInterface(abi) - } else if (tx.record.type === 'receive') { - fnABI = txHelper.getReceiveInterface(abi) - } else { - fnABI = txHelper.getFunction(abi, record.name + record.inputs) - } - if (!fnABI) { - alertCb('cannot resolve abi of ' + JSON.stringify(record, null, '\t') + '. Execution stopped at ' + index) - return cb('cannot resolve abi') - } - if (tx.record.parameters) { - /* check if we have some params to resolve */ - try { - tx.record.parameters.forEach((value, index) => { - var isString = true - if (typeof value !== 'string') { - isString = false - value = JSON.stringify(value) - } - for (var timestamp in this.data._createdContractsReverse) { - value = value.replace(new RegExp('created\\{' + timestamp + '\\}', 'g'), this.data._createdContractsReverse[timestamp]) - } - if (!isString) value = JSON.parse(value) - tx.record.parameters[index] = value - }) - } catch (e) { - return alertCb('cannot resolve input parameters ' + JSON.stringify(tx.record.parameters) + '. Execution stopped at ' + index) - } - } - var data = format.encodeData(fnABI, tx.record.parameters, tx.record.bytecode) - if (data.error) { - alertCb(data.error + '. Record:' + JSON.stringify(record, null, '\t') + '. Execution stopped at ' + index) - return cb(data.error) - } - logCallBack(`(${index}) ${JSON.stringify(record, null, '\t')}`) - logCallBack(`(${index}) data: ${data.data}`) - record.data = { dataHex: data.data, funArgs: tx.record.parameters, funAbi: fnABI, contractBytecode: tx.record.bytecode, contractName: tx.record.contractName, timestamp: tx.timestamp } - - this.blockchain.runTx(record, confirmationCb, continueCb, promptCb, - (err, txResult, rawAddress) => { - if (err) { - console.error(err) - return logCallBack(err + '. Execution failed at ' + index) - } - if (rawAddress) { - const address = addressToString(rawAddress) - // save back created addresses for the convertion from tokens to real adresses - this.data._createdContracts[address] = tx.timestamp - this.data._createdContractsReverse[tx.timestamp] = address - newContractFn(abi, address, record.contractName) - } - cb(err) - } - ) - }, () => { this.setListen(true) }) - } - - runScenario (liveMode, json, continueCb, promptCb, alertCb, confirmationCb, logCallBack, cb) { - _paq.push(['trackEvent', 'run', 'recorder', 'start']) - if (!json) { - _paq.push(['trackEvent', 'run', 'recorder', 'wrong-json']) - return cb('a json content must be provided') - } - if (typeof json === 'string') { - try { - json = JSON.parse(json) - } catch (e) { - return cb('A scenario file is required. It must be json formatted') - } + run (records, accounts, options, abis, linkReferences, confirmationCb, continueCb, promptCb, alertCb, logCallBack, liveMode, newContractFn) { + this.setListen(false) + const liveMsg = liveMode ? ' with updated contracts' : '' + logCallBack(`Running ${records.length} transaction(s)${liveMsg} ...`) + async.eachOfSeries(records, async (tx, index, cb) => { + if (liveMode && tx.record.type === 'constructor') { + // resolve the bytecode and ABI using the contract name, this ensure getting the last compiled one. + const data = await this.call('compilerArtefacts', 'getArtefactsByContractName', tx.record.contractName) + tx.record.bytecode = data.artefact.evm.bytecode.object + const updatedABIKeccak = bufferToHex(hash.keccakFromString(JSON.stringify(data.artefact.abi))) + abis[updatedABIKeccak] = data.artefact.abi + tx.record.abi = updatedABIKeccak + } + var record = this.resolveAddress(tx.record, accounts, options) + var abi = abis[tx.record.abi] + if (!abi) { + return alertCb('cannot find ABI for ' + tx.record.abi + '. Execution stopped at ' + index) + } + /* Resolve Library */ + if (record.linkReferences && Object.keys(record.linkReferences).length) { + for (var k in linkReferences) { + var link = linkReferences[k] + var timestamp = this.extractTimestamp(link) + if (timestamp && this.data._createdContractsReverse[timestamp]) { + link = this.data._createdContractsReverse[timestamp] + } + tx.record.bytecode = format.linkLibraryStandardFromlinkReferences(k, link.replace('0x', ''), tx.record.bytecode, tx.record.linkReferences) } - - let txArray - let accounts - let options - let abis - let linkReferences + } + /* Encode params */ + var fnABI + if (tx.record.type === 'constructor') { + fnABI = txHelper.getConstructorInterface(abi) + } else if (tx.record.type === 'fallback') { + fnABI = txHelper.getFallbackInterface(abi) + } else if (tx.record.type === 'receive') { + fnABI = txHelper.getReceiveInterface(abi) + } else { + fnABI = txHelper.getFunction(abi, record.name + record.inputs) + } + if (!fnABI) { + alertCb('cannot resolve abi of ' + JSON.stringify(record, null, '\t') + '. Execution stopped at ' + index) + return cb('cannot resolve abi') + } + if (tx.record.parameters) { + /* check if we have some params to resolve */ try { - txArray = json.transactions || [] - accounts = json.accounts || [] - options = json.options || {} - abis = json.abis || {} - linkReferences = json.linkReferences || {} + tx.record.parameters.forEach((value, index) => { + var isString = true + if (typeof value !== 'string') { + isString = false + value = JSON.stringify(value) + } + for (var timestamp in this.data._createdContractsReverse) { + value = value.replace(new RegExp('created\\{' + timestamp + '\\}', 'g'), this.data._createdContractsReverse[timestamp]) + } + if (!isString) value = JSON.parse(value) + tx.record.parameters[index] = value + }) } catch (e) { - return cb('Invalid scenario file. Please try again') + return alertCb('cannot resolve input parameters ' + JSON.stringify(tx.record.parameters) + '. Execution stopped at ' + index) } + } + var data = format.encodeData(fnABI, tx.record.parameters, tx.record.bytecode) + if (data.error) { + alertCb(data.error + '. Record:' + JSON.stringify(record, null, '\t') + '. Execution stopped at ' + index) + return cb(data.error) + } + logCallBack(`(${index}) ${JSON.stringify(record, null, '\t')}`) + logCallBack(`(${index}) data: ${data.data}`) + record.data = { dataHex: data.data, funArgs: tx.record.parameters, funAbi: fnABI, contractBytecode: tx.record.bytecode, contractName: tx.record.contractName, timestamp: tx.timestamp } - if (!txArray.length) { - return cb('No transactions found in scenario file') + this.blockchain.runTx(record, confirmationCb, continueCb, promptCb, + (err, txResult, rawAddress) => { + if (err) { + console.error(err) + return logCallBack(err + '. Execution failed at ' + index) + } + if (rawAddress) { + const address = addressToString(rawAddress) + // save back created addresses for the convertion from tokens to real adresses + this.data._createdContracts[address] = tx.timestamp + this.data._createdContractsReverse[tx.timestamp] = address + newContractFn(abi, address, record.contractName) + } + cb(err) } + ) + }, () => { this.setListen(true) }) + } - this.run(txArray, accounts, options, abis, linkReferences, confirmationCb, continueCb, promptCb, alertCb, logCallBack, liveMode, (abi, address, contractName) => { - cb(null, abi, address, contractName) - }) + runScenario (liveMode, json, continueCb, promptCb, alertCb, confirmationCb, logCallBack, cb) { + _paq.push(['trackEvent', 'run', 'recorder', 'start']) + if (!json) { + _paq.push(['trackEvent', 'run', 'recorder', 'wrong-json']) + return cb('a json content must be provided') + } + if (typeof json === 'string') { + try { + json = JSON.parse(json) + } catch (e) { + return cb('A scenario file is required. It must be json formatted') + } } + + let txArray + let accounts + let options + let abis + let linkReferences + try { + txArray = json.transactions || [] + accounts = json.accounts || [] + options = json.options || {} + abis = json.abis || {} + linkReferences = json.linkReferences || {} + } catch (e) { + return cb('Invalid scenario file. Please try again') + } + + if (!txArray.length) { + return cb('No transactions found in scenario file') + } + + this.run(txArray, accounts, options, abis, linkReferences, confirmationCb, continueCb, promptCb, alertCb, logCallBack, liveMode, (abi, address, contractName) => { + cb(null, abi, address, contractName) + }) + } } module.exports = Recorder diff --git a/apps/remix-ide/src/app/tabs/search.tsx b/apps/remix-ide/src/app/tabs/search.tsx index e742281d3e..70346e57f7 100644 --- a/apps/remix-ide/src/app/tabs/search.tsx +++ b/apps/remix-ide/src/app/tabs/search.tsx @@ -3,31 +3,31 @@ import * as packageJson from '../../../../../package.json' import React from 'react' // eslint-disable-line import { SearchTab } from '@remix-ui/search' const profile = { - name: 'search', - displayName: 'Search in files', - methods: [''], - events: [], - icon: 'assets/img/search_icon.webp', - description: 'Find and replace in file explorer', - kind: '', - location: 'sidePanel', - documentation: '', - version: packageJson.version, - maintainedBy: 'Remix' + name: 'search', + displayName: 'Search in files', + methods: [''], + events: [], + icon: 'assets/img/search_icon.webp', + description: 'Find and replace in file explorer', + kind: '', + location: 'sidePanel', + documentation: '', + version: packageJson.version, + maintainedBy: 'Remix' } export class SearchPlugin extends ViewPlugin { - constructor () { - super(profile) - } + constructor () { + super(profile) + } - render() { - return ( -
- -
- ); - } + render() { + return ( +
+ +
+ ); + } } diff --git a/apps/remix-ide/src/app/tabs/settings-tab.tsx b/apps/remix-ide/src/app/tabs/settings-tab.tsx index b68cc0f7f0..8fd6a5ecd9 100644 --- a/apps/remix-ide/src/app/tabs/settings-tab.tsx +++ b/apps/remix-ide/src/app/tabs/settings-tab.tsx @@ -7,81 +7,81 @@ import Registry from '../state/registry' import { PluginViewWrapper } from '@remix-ui/helper' const profile = { - name: 'settings', - displayName: 'Settings', - methods: ['get'], - events: [], - icon: 'assets/img/settings.webp', - description: 'Remix-IDE settings', - kind: 'settings', - location: 'sidePanel', - documentation: 'https://remix-ide.readthedocs.io/en/latest/settings.html', - version: packageJson.version, - permission: true, - maintainedBy: "Remix" + name: 'settings', + displayName: 'Settings', + methods: ['get'], + events: [], + icon: 'assets/img/settings.webp', + description: 'Remix-IDE settings', + kind: 'settings', + location: 'sidePanel', + documentation: 'https://remix-ide.readthedocs.io/en/latest/settings.html', + version: packageJson.version, + permission: true, + maintainedBy: "Remix" } module.exports = class SettingsTab extends ViewPlugin { - config: any = {} - editor: any - private _deps: { + config: any = {} + editor: any + private _deps: { themeModule: any localeModule: any } - element: HTMLDivElement - public useMatomoAnalytics: any - dispatch: React.Dispatch = () => {} - constructor (config, editor) { - super(profile) - this.config = config - this.config.events.on('configChanged', (changedConfig) => { - this.emit('configChanged', changedConfig) - }) - this.editor = editor - this._deps = { - themeModule: Registry.getInstance().get('themeModule').api, - localeModule: Registry.getInstance().get('localeModule').api - } - this.element = document.createElement('div') - this.element.setAttribute('id', 'settingsTab') - this.useMatomoAnalytics = null + element: HTMLDivElement + public useMatomoAnalytics: any + dispatch: React.Dispatch = () => {} + constructor (config, editor) { + super(profile) + this.config = config + this.config.events.on('configChanged', (changedConfig) => { + this.emit('configChanged', changedConfig) + }) + this.editor = editor + this._deps = { + themeModule: Registry.getInstance().get('themeModule').api, + localeModule: Registry.getInstance().get('localeModule').api } + this.element = document.createElement('div') + this.element.setAttribute('id', 'settingsTab') + this.useMatomoAnalytics = null + } - setDispatch (dispatch: React.Dispatch) { - this.dispatch = dispatch - this.renderComponent() - } + setDispatch (dispatch: React.Dispatch) { + this.dispatch = dispatch + this.renderComponent() + } - render() { - return
- -
- } + render() { + return
+ +
+ } - updateComponent(state: any){ - return - } + updateComponent(state: any){ + return + } - renderComponent () { - this.dispatch(this) - } + renderComponent () { + this.dispatch(this) + } - get (key) { - return this.config.get(key) - } + get (key) { + return this.config.get(key) + } - updateMatomoAnalyticsChoice (isChecked) { - this.config.set('settings/matomo-analytics', isChecked) - this.useMatomoAnalytics = isChecked - this.dispatch({ - ...this - }) - } + updateMatomoAnalyticsChoice (isChecked) { + this.config.set('settings/matomo-analytics', isChecked) + this.useMatomoAnalytics = isChecked + this.dispatch({ + ...this + }) + } } diff --git a/apps/remix-ide/src/app/tabs/test-tab.js b/apps/remix-ide/src/app/tabs/test-tab.js index df16f47574..4021ad577a 100644 --- a/apps/remix-ide/src/app/tabs/test-tab.js +++ b/apps/remix-ide/src/app/tabs/test-tab.js @@ -11,147 +11,147 @@ import { PluginViewWrapper } from '@remix-ui/helper' var { UnitTestRunner, assertLibCode } = require('@remix-project/remix-tests') const profile = { - name: 'solidityUnitTesting', - displayName: 'Solidity unit testing', - methods: ['testFromPath', 'testFromSource', 'setTestFolderPath', 'getTestlibs', 'createTestLibs'], - events: [], - icon: 'assets/img/unitTesting.webp', - description: 'Write and run unit tests for your contracts in Solidity', - location: 'sidePanel', - documentation: 'https://remix-ide.readthedocs.io/en/latest/unittesting.html', - maintainedBy: 'Remix' + name: 'solidityUnitTesting', + displayName: 'Solidity unit testing', + methods: ['testFromPath', 'testFromSource', 'setTestFolderPath', 'getTestlibs', 'createTestLibs'], + events: [], + icon: 'assets/img/unitTesting.webp', + description: 'Write and run unit tests for your contracts in Solidity', + location: 'sidePanel', + documentation: 'https://remix-ide.readthedocs.io/en/latest/unittesting.html', + maintainedBy: 'Remix' } module.exports = class TestTab extends ViewPlugin { - constructor (fileManager, offsetToLineColumnConverter, filePanel, compileTab, appManager, contentImport) { - super(profile) - this.compileTab = compileTab - this.contentImport = contentImport - this.fileManager = fileManager - this.filePanel = filePanel - this.appManager = appManager - this.testRunner = new UnitTestRunner() - this.testTabLogic = new TestTabLogic(this.fileManager, helper) - this.offsetToLineColumnConverter = offsetToLineColumnConverter - this.allFilesInvolved = ['.deps/remix-tests/remix_tests.sol', '.deps/remix-tests/remix_accounts.sol'] - this.element = document.createElement('div') - this.dispatch = null + constructor (fileManager, offsetToLineColumnConverter, filePanel, compileTab, appManager, contentImport) { + super(profile) + this.compileTab = compileTab + this.contentImport = contentImport + this.fileManager = fileManager + this.filePanel = filePanel + this.appManager = appManager + this.testRunner = new UnitTestRunner() + this.testTabLogic = new TestTabLogic(this.fileManager, helper) + this.offsetToLineColumnConverter = offsetToLineColumnConverter + this.allFilesInvolved = ['.deps/remix-tests/remix_tests.sol', '.deps/remix-tests/remix_accounts.sol'] + this.element = document.createElement('div') + this.dispatch = null + } + + onActivationInternal () { + this.listenToEvents() + this.call('filePanel', 'registerContextMenuItem', { + id: 'solidityUnitTesting', + name: 'setTestFolderPath', + label: 'Set path for Unit Testing', + type: ['folder'], + extension: [], + path: [], + pattern: [] + }) + } + + async setTestFolderPath (event) { + if (event.path.length > 0) { + this.renderComponent(event.path[0]) } + } - onActivationInternal () { - this.listenToEvents() - this.call('filePanel', 'registerContextMenuItem', { - id: 'solidityUnitTesting', - name: 'setTestFolderPath', - label: 'Set path for Unit Testing', - type: ['folder'], - extension: [], - path: [], - pattern: [] - }) - } - - async setTestFolderPath (event) { - if (event.path.length > 0) { - this.renderComponent(event.path[0]) - } - } - - getTestlibs () { - return { assertLibCode, accountsLibCode: this.testRunner.accountsLibCode } - } - - async createTestLibs () { - const provider = await this.fileManager.currentFileProvider() - if (provider) { - await provider.addExternal('.deps/remix-tests/remix_tests.sol', assertLibCode, 'remix_tests.sol') - await provider.addExternal('.deps/remix-tests/remix_accounts.sol', this.testRunner.accountsLibCode, 'remix_accounts.sol') - } - } - - async onActivation () { - const isSolidityActive = await this.call('manager', 'isActive', 'solidity') - if (!isSolidityActive) { - await this.call('manager', 'activatePlugin', 'solidity') - } - await this.testRunner.init(await this.call('blockchain', 'web3VM')) - await this.createTestLibs() - } - - onDeactivation () { - this.off('filePanel', 'newTestFileCreated') - this.off('filePanel', 'setWorkspace') - // 'currentFileChanged' event is added more than once - this.fileManager.events.removeAllListeners('currentFileChanged') - } + getTestlibs () { + return { assertLibCode, accountsLibCode: this.testRunner.accountsLibCode } + } - listenToEvents () { - this.on('filePanel', 'workspaceCreated', async () => { - this.createTestLibs() - }) - - this.testRunner.event.on('compilationFinished', (success, data, source, input, version) => { - if (success) { - this.allFilesInvolved.push(...Object.keys(data.sources)) - // forwarding the event to the appManager infra - // This is listened by compilerArtefacts to show data while debugging - this.emit('compilationFinished', source.target, source, 'soljson', data, input, version) - } - }) + async createTestLibs () { + const provider = await this.fileManager.currentFileProvider() + if (provider) { + await provider.addExternal('.deps/remix-tests/remix_tests.sol', assertLibCode, 'remix_tests.sol') + await provider.addExternal('.deps/remix-tests/remix_accounts.sol', this.testRunner.accountsLibCode, 'remix_accounts.sol') } + } - async testFromPath (path) { - const fileContent = await this.fileManager.readFile(path) - return this.testFromSource(fileContent, path) + async onActivation () { + const isSolidityActive = await this.call('manager', 'isActive', 'solidity') + if (!isSolidityActive) { + await this.call('manager', 'activatePlugin', 'solidity') } - - /* + await this.testRunner.init(await this.call('blockchain', 'web3VM')) + await this.createTestLibs() + } + + onDeactivation () { + this.off('filePanel', 'newTestFileCreated') + this.off('filePanel', 'setWorkspace') + // 'currentFileChanged' event is added more than once + this.fileManager.events.removeAllListeners('currentFileChanged') + } + + listenToEvents () { + this.on('filePanel', 'workspaceCreated', async () => { + this.createTestLibs() + }) + + this.testRunner.event.on('compilationFinished', (success, data, source, input, version) => { + if (success) { + this.allFilesInvolved.push(...Object.keys(data.sources)) + // forwarding the event to the appManager infra + // This is listened by compilerArtefacts to show data while debugging + this.emit('compilationFinished', source.target, source, 'soljson', data, input, version) + } + }) + } + + async testFromPath (path) { + const fileContent = await this.fileManager.readFile(path) + return this.testFromSource(fileContent, path) + } + + /* Test is not associated with the UI */ - async testFromSource (content, path = 'browser/unit_test.sol') { - const web3 = await this.call('blockchain', 'web3VM') - await this.testRunner.init(web3) - await this.createTestLibs() - return new Promise((resolve, reject) => { - const runningTest = {} - runningTest[path] = { content } - const { currentVersion, evmVersion, optimize, runs } = this.compileTab.getCurrentCompilerConfig() - const currentCompilerUrl = urlFromVersion(currentVersion) - const compilerConfig = { - currentCompilerUrl, - evmVersion, - optimize, - usingWorker: canUseWorker(currentVersion), - runs - } - this.testRunner.runTestSources(runningTest, compilerConfig, () => { /* Do nothing. */ }, () => { /* Do nothing. */ }, null, (error, result) => { - if (error) return reject(error) - resolve(result) - }, (url, cb) => { - return this.contentImport.resolveAndSave(url).then((result) => cb(null, result)).catch((error) => cb(error.message)) - }, {}) - }) - } - - setDispatch (dispatch) { - this.dispatch = dispatch - this.renderComponent('tests') - } - - render () { - this.onActivationInternal() - return
- } - - updateComponent(state) { - return - } - - renderComponent (testDirPath) { - this.dispatch({ - testTab: this, - helper: helper, - testDirPath: testDirPath - }) - } + async testFromSource (content, path = 'browser/unit_test.sol') { + const web3 = await this.call('blockchain', 'web3VM') + await this.testRunner.init(web3) + await this.createTestLibs() + return new Promise((resolve, reject) => { + const runningTest = {} + runningTest[path] = { content } + const { currentVersion, evmVersion, optimize, runs } = this.compileTab.getCurrentCompilerConfig() + const currentCompilerUrl = urlFromVersion(currentVersion) + const compilerConfig = { + currentCompilerUrl, + evmVersion, + optimize, + usingWorker: canUseWorker(currentVersion), + runs + } + this.testRunner.runTestSources(runningTest, compilerConfig, () => { /* Do nothing. */ }, () => { /* Do nothing. */ }, null, (error, result) => { + if (error) return reject(error) + resolve(result) + }, (url, cb) => { + return this.contentImport.resolveAndSave(url).then((result) => cb(null, result)).catch((error) => cb(error.message)) + }, {}) + }) + } + + setDispatch (dispatch) { + this.dispatch = dispatch + this.renderComponent('tests') + } + + render () { + this.onActivationInternal() + return
+ } + + updateComponent(state) { + return + } + + renderComponent (testDirPath) { + this.dispatch({ + testTab: this, + helper: helper, + testDirPath: testDirPath + }) + } } diff --git a/apps/remix-ide/src/app/tabs/theme-module.js b/apps/remix-ide/src/app/tabs/theme-module.js index e1e066c4b5..d77f666638 100644 --- a/apps/remix-ide/src/app/tabs/theme-module.js +++ b/apps/remix-ide/src/app/tabs/theme-module.js @@ -6,128 +6,128 @@ import Registry from '../state/registry' const _paq = window._paq = window._paq || [] const themes = [ - { name: 'Dark', quality: 'dark', url: 'assets/css/themes/remix-dark_tvx1s2.css' }, - { name: 'Light', quality: 'light', url: 'assets/css/themes/remix-light_powaqg.css' }, - { name: 'Violet', quality: 'light', url: 'assets/css/themes/remix-violet.css' }, - { name: 'Unicorn', quality: 'light', url: 'assets/css/themes/remix-unicorn.css' }, - { name: 'Midcentury', quality: 'light', url: 'assets/css/themes/remix-midcentury_hrzph3.css' }, - { name: 'Black', quality: 'dark', url: 'assets/css/themes/remix-black_undtds.css' }, - { name: 'Candy', quality: 'light', url: 'assets/css/themes/remix-candy_ikhg4m.css' }, - { name: 'HackerOwl', quality: 'dark', url: 'assets/css/themes/remix-hacker_owl.css' }, + { name: 'Dark', quality: 'dark', url: 'assets/css/themes/remix-dark_tvx1s2.css' }, + { name: 'Light', quality: 'light', url: 'assets/css/themes/remix-light_powaqg.css' }, + { name: 'Violet', quality: 'light', url: 'assets/css/themes/remix-violet.css' }, + { name: 'Unicorn', quality: 'light', url: 'assets/css/themes/remix-unicorn.css' }, + { name: 'Midcentury', quality: 'light', url: 'assets/css/themes/remix-midcentury_hrzph3.css' }, + { name: 'Black', quality: 'dark', url: 'assets/css/themes/remix-black_undtds.css' }, + { name: 'Candy', quality: 'light', url: 'assets/css/themes/remix-candy_ikhg4m.css' }, + { name: 'HackerOwl', quality: 'dark', url: 'assets/css/themes/remix-hacker_owl.css' }, - { name: 'Cerulean', quality: 'light', url: 'assets/css/themes/bootstrap-cerulean.min.css' }, - { name: 'Flatly', quality: 'light', url: 'assets/css/themes/bootstrap-flatly.min.css' }, - { name: 'Spacelab', quality: 'light', url: 'assets/css/themes/bootstrap-spacelab.min.css' }, - { name: 'Cyborg', quality: 'dark', url: 'assets/css/themes/bootstrap-cyborg.min.css' } + { name: 'Cerulean', quality: 'light', url: 'assets/css/themes/bootstrap-cerulean.min.css' }, + { name: 'Flatly', quality: 'light', url: 'assets/css/themes/bootstrap-flatly.min.css' }, + { name: 'Spacelab', quality: 'light', url: 'assets/css/themes/bootstrap-spacelab.min.css' }, + { name: 'Cyborg', quality: 'dark', url: 'assets/css/themes/bootstrap-cyborg.min.css' } ] const profile = { - name: 'theme', - events: ['themeChanged'], - methods: ['switchTheme', 'getThemes', 'currentTheme', 'fixInvert'], - version: packageJson.version, - kind: 'theme' + name: 'theme', + events: ['themeChanged'], + methods: ['switchTheme', 'getThemes', 'currentTheme', 'fixInvert'], + version: packageJson.version, + kind: 'theme' } export class ThemeModule extends Plugin { - constructor () { - super(profile) - this.events = new EventEmitter() - this._deps = { - config: Registry.getInstance().get('config') && Registry.getInstance().get('config').api - } - this.themes = {} - themes.map((theme) => { - this.themes[theme.name.toLocaleLowerCase()] = { - ...theme, - url: window.location.origin + ( window.location.pathname.startsWith('/address/') || window.location.pathname.endsWith('.sol') ? '/' : window.location.pathname ) + theme.url - } - }) - this._paq = _paq - let queryTheme = (new QueryParams()).get().theme - queryTheme = queryTheme && queryTheme.toLocaleLowerCase() - queryTheme = this.themes[queryTheme] ? queryTheme : null - let currentTheme = (this._deps.config && this._deps.config.get('settings/theme')) || null - currentTheme = currentTheme && currentTheme.toLocaleLowerCase() - currentTheme = this.themes[currentTheme] ? currentTheme : null - this.currentThemeState = { queryTheme, currentTheme } - this.active = queryTheme || currentTheme || 'dark' - this.forced = !!queryTheme + constructor () { + super(profile) + this.events = new EventEmitter() + this._deps = { + config: Registry.getInstance().get('config') && Registry.getInstance().get('config').api } + this.themes = {} + themes.map((theme) => { + this.themes[theme.name.toLocaleLowerCase()] = { + ...theme, + url: window.location.origin + ( window.location.pathname.startsWith('/address/') || window.location.pathname.endsWith('.sol') ? '/' : window.location.pathname ) + theme.url + } + }) + this._paq = _paq + let queryTheme = (new QueryParams()).get().theme + queryTheme = queryTheme && queryTheme.toLocaleLowerCase() + queryTheme = this.themes[queryTheme] ? queryTheme : null + let currentTheme = (this._deps.config && this._deps.config.get('settings/theme')) || null + currentTheme = currentTheme && currentTheme.toLocaleLowerCase() + currentTheme = this.themes[currentTheme] ? currentTheme : null + this.currentThemeState = { queryTheme, currentTheme } + this.active = queryTheme || currentTheme || 'dark' + this.forced = !!queryTheme + } - /** Return the active theme + /** Return the active theme * @return {{ name: string, quality: string, url: string }} - The active theme */ - currentTheme () { - return this.themes[this.active] - } + currentTheme () { + return this.themes[this.active] + } - /** Returns all themes as an array */ - getThemes () { - return Object.keys(this.themes).map(key => this.themes[key]) - } + /** Returns all themes as an array */ + getThemes () { + return Object.keys(this.themes).map(key => this.themes[key]) + } - /** + /** * Init the theme */ - initTheme (callback) { // callback is setTimeOut in app.js which is always passed - if (callback) this.initCallback = callback - if (this.active) { - document.getElementById('theme-link') ? document.getElementById('theme-link').remove():null - const nextTheme = this.themes[this.active] // Theme - document.documentElement.style.setProperty('--theme', nextTheme.quality) + initTheme (callback) { // callback is setTimeOut in app.js which is always passed + if (callback) this.initCallback = callback + if (this.active) { + document.getElementById('theme-link') ? document.getElementById('theme-link').remove():null + const nextTheme = this.themes[this.active] // Theme + document.documentElement.style.setProperty('--theme', nextTheme.quality) - const theme = document.createElement('link') - theme.setAttribute('rel', 'stylesheet') - theme.setAttribute('href', nextTheme.url) - theme.setAttribute('id', 'theme-link') - theme.addEventListener('load', () => { - if (callback) callback() - }) - document.head.insertBefore(theme, document.head.firstChild) - } + const theme = document.createElement('link') + theme.setAttribute('rel', 'stylesheet') + theme.setAttribute('href', nextTheme.url) + theme.setAttribute('id', 'theme-link') + theme.addEventListener('load', () => { + if (callback) callback() + }) + document.head.insertBefore(theme, document.head.firstChild) } + } - /** + /** * Change the current theme * @param {string} [themeName] - The name of the theme */ - switchTheme (themeName) { - themeName = themeName && themeName.toLocaleLowerCase() - if (themeName && !Object.keys(this.themes).includes(themeName)) { - throw new Error(`Theme ${themeName} doesn't exist`) - } - const next = themeName || this.active // Name - if (next === this.active) return // --> exit out of this method - _paq.push(['trackEvent', 'themeModule', 'switchTo', next]) - const nextTheme = this.themes[next] // Theme - if (!this.forced) this._deps.config.set('settings/theme', next) - document.getElementById('theme-link') ? document.getElementById('theme-link').remove():null - - const theme = document.createElement('link') - theme.setAttribute('rel', 'stylesheet') - theme.setAttribute('href', nextTheme.url) - theme.setAttribute('id', 'theme-link') - theme.addEventListener('load', () => { - this.emit('themeLoaded', nextTheme) - this.events.emit('themeLoaded', nextTheme) - }) - document.head.insertBefore(theme, document.head.firstChild) - document.documentElement.style.setProperty('--theme', nextTheme.quality) - if (themeName) this.active = themeName - // TODO: Only keep `this.emit` (issue#2210) - this.emit('themeChanged', nextTheme) - this.events.emit('themeChanged', nextTheme) + switchTheme (themeName) { + themeName = themeName && themeName.toLocaleLowerCase() + if (themeName && !Object.keys(this.themes).includes(themeName)) { + throw new Error(`Theme ${themeName} doesn't exist`) } + const next = themeName || this.active // Name + if (next === this.active) return // --> exit out of this method + _paq.push(['trackEvent', 'themeModule', 'switchTo', next]) + const nextTheme = this.themes[next] // Theme + if (!this.forced) this._deps.config.set('settings/theme', next) + document.getElementById('theme-link') ? document.getElementById('theme-link').remove():null + + const theme = document.createElement('link') + theme.setAttribute('rel', 'stylesheet') + theme.setAttribute('href', nextTheme.url) + theme.setAttribute('id', 'theme-link') + theme.addEventListener('load', () => { + this.emit('themeLoaded', nextTheme) + this.events.emit('themeLoaded', nextTheme) + }) + document.head.insertBefore(theme, document.head.firstChild) + document.documentElement.style.setProperty('--theme', nextTheme.quality) + if (themeName) this.active = themeName + // TODO: Only keep `this.emit` (issue#2210) + this.emit('themeChanged', nextTheme) + this.events.emit('themeChanged', nextTheme) + } - /** + /** * fixes the invertion for images since this should be adjusted when we switch between dark/light qualified themes * @param {element} [image] - the dom element which invert should be fixed to increase visibility */ - fixInvert (image) { - const invert = this.currentTheme().quality === 'dark' ? 1 : 0 - if (image) { - image.style.filter = `invert(${invert})` - } + fixInvert (image) { + const invert = this.currentTheme().quality === 'dark' ? 1 : 0 + if (image) { + image.style.filter = `invert(${invert})` } + } } diff --git a/apps/remix-ide/src/app/tabs/web3-provider.js b/apps/remix-ide/src/app/tabs/web3-provider.js index 0158ff11f9..cf4d165cd3 100644 --- a/apps/remix-ide/src/app/tabs/web3-provider.js +++ b/apps/remix-ide/src/app/tabs/web3-provider.js @@ -2,83 +2,83 @@ import { Plugin } from '@remixproject/engine' import * as packageJson from '../../../../../package.json' export const profile = { - name: 'web3Provider', - displayName: 'Global Web3 Provider', - description: 'Represent the current web3 provider used by the app at global scope', - methods: ['sendAsync'], - version: packageJson.version, - kind: 'provider' + name: 'web3Provider', + displayName: 'Global Web3 Provider', + description: 'Represent the current web3 provider used by the app at global scope', + methods: ['sendAsync'], + version: packageJson.version, + kind: 'provider' } export class Web3ProviderModule extends Plugin { - constructor(blockchain) { - super(profile) - this.blockchain = blockchain - } + constructor(blockchain) { + super(profile) + this.blockchain = blockchain + } - /* + /* that is used by plugins to call the current ethereum provider. Should be taken carefully and probably not be release as it is now. */ - sendAsync(payload) { + sendAsync(payload) { - return new Promise((resolve, reject) => { - this.askUserPermission('sendAsync', `Calling ${payload.method} with parameters ${JSON.stringify(payload.params, null, '\t')}`).then( - async (result) => { - if (result) { - const provider = this.blockchain.web3().currentProvider - // see https://github.com/ethereum/web3.js/pull/1018/files#diff-d25786686c1053b786cc2626dc6e048675050593c0ebaafbf0814e1996f22022R129 - provider[provider.sendAsync ? 'sendAsync' : 'send'](payload, async (error, message) => { - if (error) { - // Handle 'The method "debug_traceTransaction" does not exist / is not available.' error - if(error.message && error.code && error.code === -32601) { - this.call('terminal', 'log', { value: error.message, type: 'error' } ) - return reject(error.message) - } else { - const errorData = error.data || error.message || error - // See: https://github.com/ethers-io/ethers.js/issues/901 - if (!(typeof errorData === 'string' && errorData.includes("unknown method eth_chainId"))) this.call('terminal', 'log', { value: error.data || error.message, type: 'error' } ) - return reject(errorData) - } - } - if (payload.method === 'eth_sendTransaction') { - if (payload.params.length && !payload.params[0].to && message.result) { - setTimeout(async () => { - const receipt = await this.tryTillReceiptAvailable(message.result) - if (!receipt.contractAddress) { - console.log('receipt available but contract address not present', receipt) - return - } - const contractData = await this.call('compilerArtefacts', 'getContractDataFromAddress', receipt.contractAddress) - if (contractData) this.call('udapp', 'addInstance', receipt.contractAddress, contractData.contract.abi, contractData.name) - }, 50) - } - } - resolve(message) - }) - } else { - reject(new Error('User denied permission')) + return new Promise((resolve, reject) => { + this.askUserPermission('sendAsync', `Calling ${payload.method} with parameters ${JSON.stringify(payload.params, null, '\t')}`).then( + async (result) => { + if (result) { + const provider = this.blockchain.web3().currentProvider + // see https://github.com/ethereum/web3.js/pull/1018/files#diff-d25786686c1053b786cc2626dc6e048675050593c0ebaafbf0814e1996f22022R129 + provider[provider.sendAsync ? 'sendAsync' : 'send'](payload, async (error, message) => { + if (error) { + // Handle 'The method "debug_traceTransaction" does not exist / is not available.' error + if(error.message && error.code && error.code === -32601) { + this.call('terminal', 'log', { value: error.message, type: 'error' } ) + return reject(error.message) + } else { + const errorData = error.data || error.message || error + // See: https://github.com/ethers-io/ethers.js/issues/901 + if (!(typeof errorData === 'string' && errorData.includes("unknown method eth_chainId"))) this.call('terminal', 'log', { value: error.data || error.message, type: 'error' } ) + return reject(errorData) + } + } + if (payload.method === 'eth_sendTransaction') { + if (payload.params.length && !payload.params[0].to && message.result) { + setTimeout(async () => { + const receipt = await this.tryTillReceiptAvailable(message.result) + if (!receipt.contractAddress) { + console.log('receipt available but contract address not present', receipt) + return } - }).catch((e) => { - reject(e) + const contractData = await this.call('compilerArtefacts', 'getContractDataFromAddress', receipt.contractAddress) + if (contractData) this.call('udapp', 'addInstance', receipt.contractAddress, contractData.contract.abi, contractData.name) + }, 50) + } + } + resolve(message) }) - }) - } + } else { + reject(new Error('User denied permission')) + } + }).catch((e) => { + reject(e) + }) + }) + } - async tryTillReceiptAvailable(txhash) { - try { - const receipt = await this.call('blockchain', 'getTransactionReceipt', txhash) - if (receipt) return receipt - } catch (e) { - // do nothing - } - await this.pause() - return await this.tryTillReceiptAvailable(txhash) + async tryTillReceiptAvailable(txhash) { + try { + const receipt = await this.call('blockchain', 'getTransactionReceipt', txhash) + if (receipt) return receipt + } catch (e) { + // do nothing } + await this.pause() + return await this.tryTillReceiptAvailable(txhash) + } - async pause() { - return new Promise((resolve, reject) => { - setTimeout(resolve, 500) - }) - } + async pause() { + return new Promise((resolve, reject) => { + setTimeout(resolve, 500) + }) + } } diff --git a/apps/remix-ide/src/app/udapp/make-udapp.js b/apps/remix-ide/src/app/udapp/make-udapp.js index 0b8b13433a..50bc90e2a5 100644 --- a/apps/remix-ide/src/app/udapp/make-udapp.js +++ b/apps/remix-ide/src/app/udapp/make-udapp.js @@ -4,37 +4,37 @@ var remixLib = require('@remix-project/remix-lib') var EventsDecoder = remixLib.execution.EventsDecoder export function makeUdapp (blockchain, compilersArtefacts, logHtmlCallback) { - // ----------------- Tx listener ----------------- - const _transactionReceipts = {} - const transactionReceiptResolver = (tx, cb) => { - if (_transactionReceipts[tx.hash]) { - return cb(null, _transactionReceipts[tx.hash]) - } - blockchain.web3().eth.getTransactionReceipt(tx.hash, (error, receipt) => { - if (error) { - return cb(error) - } - _transactionReceipts[tx.hash] = receipt - cb(null, receipt) - }) + // ----------------- Tx listener ----------------- + const _transactionReceipts = {} + const transactionReceiptResolver = (tx, cb) => { + if (_transactionReceipts[tx.hash]) { + return cb(null, _transactionReceipts[tx.hash]) } - - const txlistener = blockchain.getTxListener({ - api: { - contracts: function () { - if (compilersArtefacts.__last) return compilersArtefacts.getAllContractDatas() - return null - }, - resolveReceipt: transactionReceiptResolver - } + blockchain.web3().eth.getTransactionReceipt(tx.hash, (error, receipt) => { + if (error) { + return cb(error) + } + _transactionReceipts[tx.hash] = receipt + cb(null, receipt) }) + } + + const txlistener = blockchain.getTxListener({ + api: { + contracts: function () { + if (compilersArtefacts.__last) return compilersArtefacts.getAllContractDatas() + return null + }, + resolveReceipt: transactionReceiptResolver + } + }) - Registry.getInstance().put({ api: txlistener, name: 'txlistener' }) - blockchain.startListening(txlistener) + Registry.getInstance().put({ api: txlistener, name: 'txlistener' }) + blockchain.startListening(txlistener) - const eventsDecoder = new EventsDecoder({ - resolveReceipt: transactionReceiptResolver - }) - txlistener.startListening() - Registry.getInstance().put({ api: eventsDecoder, name: 'eventsDecoder' }) + const eventsDecoder = new EventsDecoder({ + resolveReceipt: transactionReceiptResolver + }) + txlistener.startListening() + Registry.getInstance().put({ api: eventsDecoder, name: 'eventsDecoder' }) } diff --git a/apps/remix-ide/src/app/udapp/run-tab.js b/apps/remix-ide/src/app/udapp/run-tab.js index 49983f5e39..7849d7e6f2 100644 --- a/apps/remix-ide/src/app/udapp/run-tab.js +++ b/apps/remix-ide/src/app/udapp/run-tab.js @@ -9,183 +9,183 @@ const Recorder = require('../tabs/runTab/model/recorder.js') const _paq = window._paq = window._paq || [] const profile = { - name: 'udapp', - displayName: 'Deploy & run transactions', - icon: 'assets/img/deployAndRun.webp', - description: 'Execute, save and replay transactions', - kind: 'udapp', - location: 'sidePanel', - documentation: 'https://remix-ide.readthedocs.io/en/latest/run.html', - version: packageJson.version, - maintainedBy: 'Remix', - permission: true, - events: ['newTransaction'], - methods: ['createVMAccount', 'sendTransaction', 'getAccounts', 'pendingTransactionsCount', 'getSettings', 'setEnvironmentMode', 'clearAllInstances', 'addInstance', 'resolveContractAndAddInstance'] + name: 'udapp', + displayName: 'Deploy & run transactions', + icon: 'assets/img/deployAndRun.webp', + description: 'Execute, save and replay transactions', + kind: 'udapp', + location: 'sidePanel', + documentation: 'https://remix-ide.readthedocs.io/en/latest/run.html', + version: packageJson.version, + maintainedBy: 'Remix', + permission: true, + events: ['newTransaction'], + methods: ['createVMAccount', 'sendTransaction', 'getAccounts', 'pendingTransactionsCount', 'getSettings', 'setEnvironmentMode', 'clearAllInstances', 'addInstance', 'resolveContractAndAddInstance'] } export class RunTab extends ViewPlugin { - constructor (blockchain, config, fileManager, editor, filePanel, compilersArtefacts, networkModule, fileProvider) { - super(profile) - this.event = new EventManager() - this.config = config - this.blockchain = blockchain - this.fileManager = fileManager - this.editor = editor - this.filePanel = filePanel - this.compilersArtefacts = compilersArtefacts - this.networkModule = networkModule - this.fileProvider = fileProvider - this.recorder = new Recorder(blockchain) - this.REACT_API = {} - this.setupEvents() - this.el = document.createElement('div') - } - - setupEvents () { - this.blockchain.events.on('newTransaction', (tx, receipt) => { - this.emit('newTransaction', tx, receipt) - }) - } - - getSettings () { - return new Promise((resolve, reject) => { - resolve({ - selectedAccount: this.REACT_API.accounts.selectedAccount, - selectedEnvMode: this.REACT_API.selectExEnv, - networkEnvironment: this.REACT_API.networkName - }) - }) - } + constructor (blockchain, config, fileManager, editor, filePanel, compilersArtefacts, networkModule, fileProvider) { + super(profile) + this.event = new EventManager() + this.config = config + this.blockchain = blockchain + this.fileManager = fileManager + this.editor = editor + this.filePanel = filePanel + this.compilersArtefacts = compilersArtefacts + this.networkModule = networkModule + this.fileProvider = fileProvider + this.recorder = new Recorder(blockchain) + this.REACT_API = {} + this.setupEvents() + this.el = document.createElement('div') + } + + setupEvents () { + this.blockchain.events.on('newTransaction', (tx, receipt) => { + this.emit('newTransaction', tx, receipt) + }) + } + + getSettings () { + return new Promise((resolve, reject) => { + resolve({ + selectedAccount: this.REACT_API.accounts.selectedAccount, + selectedEnvMode: this.REACT_API.selectExEnv, + networkEnvironment: this.REACT_API.networkName + }) + }) + } + + async setEnvironmentMode (env) { + const canCall = await this.askUserPermission('setEnvironmentMode', 'change the environment used') + if (canCall) { + env = typeof env === 'string' ? { context: env } : env + this.emit('setEnvironmentModeReducer', env, this.currentRequest.from) + } + } + + clearAllInstances () { + this.emit('clearAllInstancesReducer') + } + + addInstance (address, abi, name) { + this.emit('addInstanceReducer', address, abi, name) + } + + createVMAccount (newAccount) { + return this.blockchain.createVMAccount(newAccount) + } + + sendTransaction (tx) { + _paq.push(['trackEvent', 'udapp', 'sendTx', 'udappTransaction']) + return this.blockchain.sendTransaction(tx) + } + + getAccounts (cb) { + return this.blockchain.getAccounts(cb) + } + + pendingTransactionsCount () { + return this.blockchain.pendingTransactionsCount() + } + + render () { + return
+ } + + onReady (api) { + this.REACT_API = api + } + + async onInitDone () { + const udapp = this // eslint-disable-line - async setEnvironmentMode (env) { - const canCall = await this.askUserPermission('setEnvironmentMode', 'change the environment used') - if (canCall) { - env = typeof env === 'string' ? { context: env } : env - this.emit('setEnvironmentModeReducer', env, this.currentRequest.from) + const addProvider = async (name, displayName, isInjected, isVM, fork = '', dataId = '', title = '') => { + await this.call('blockchain', 'addProvider', { + options: {}, + dataId, + name, + displayName, + fork, + isInjected, + isVM, + title, + init: async function () { + const options = await udapp.call(name, 'init') + if (options) { + this.options = options + if (options['fork']) this.fork = options['fork'] + } + }, + provider: { + async sendAsync (payload, callback) { + try { + const result = await udapp.call(name, 'sendAsync', payload) + callback(null, result) + } catch (e) { + callback(e) + } + } } + }) } - clearAllInstances () { - this.emit('clearAllInstancesReducer') - } - - addInstance (address, abi, name) { - this.emit('addInstanceReducer', address, abi, name) - } - - createVMAccount (newAccount) { - return this.blockchain.createVMAccount(newAccount) - } - - sendTransaction (tx) { - _paq.push(['trackEvent', 'udapp', 'sendTx', 'udappTransaction']) - return this.blockchain.sendTransaction(tx) - } - - getAccounts (cb) { - return this.blockchain.getAccounts(cb) - } - - pendingTransactionsCount () { - return this.blockchain.pendingTransactionsCount() - } - - render () { - return
+ // basic injected + // if it's the trust wallet provider, we have a specific provider for that, see below + if (window && window.ethereum && !(window.ethereum.isTrustWallet || window.ethereum.selectedProvider?.isTrustWallet)) { + const displayNameInjected = `Injected Provider${(window && window.ethereum && !(window.ethereum.providers && !window.ethereum.selectedProvider)) ? + window.ethereum.isCoinbaseWallet || window.ethereum.selectedProvider?.isCoinbaseWallet ? ' - Coinbase' : + window.ethereum.isBraveWallet || window.ethereum.selectedProvider?.isBraveWallet ? ' - Brave' : + window.ethereum.isMetaMask || window.ethereum.selectedProvider?.isMetaMask ? ' - MetaMask' : '' : ''}` + await addProvider('injected', displayNameInjected, true, false) + } else if (window && !window.ethereum) { + // we still add "injected" if there's no provider (just so it's visible to the user). + await addProvider('injected', 'Injected Provider', true, false) } - onReady (api) { - this.REACT_API = api + if (window && window.trustwallet) { + const displayNameInjected = `Injected Provider - TrustWallet` + await addProvider('injected-trustwallet', displayNameInjected, true, false) } - - async onInitDone () { - const udapp = this // eslint-disable-line - - const addProvider = async (name, displayName, isInjected, isVM, fork = '', dataId = '', title = '') => { - await this.call('blockchain', 'addProvider', { - options: {}, - dataId, - name, - displayName, - fork, - isInjected, - isVM, - title, - init: async function () { - const options = await udapp.call(name, 'init') - if (options) { - this.options = options - if (options['fork']) this.fork = options['fork'] - } - }, - provider: { - async sendAsync (payload, callback) { - try { - const result = await udapp.call(name, 'sendAsync', payload) - callback(null, result) - } catch (e) { - callback(e) - } - } - } - }) - } - - // basic injected - // if it's the trust wallet provider, we have a specific provider for that, see below - if (window && window.ethereum && !(window.ethereum.isTrustWallet || window.ethereum.selectedProvider?.isTrustWallet)) { - const displayNameInjected = `Injected Provider${(window && window.ethereum && !(window.ethereum.providers && !window.ethereum.selectedProvider)) ? - window.ethereum.isCoinbaseWallet || window.ethereum.selectedProvider?.isCoinbaseWallet ? ' - Coinbase' : - window.ethereum.isBraveWallet || window.ethereum.selectedProvider?.isBraveWallet ? ' - Brave' : - window.ethereum.isMetaMask || window.ethereum.selectedProvider?.isMetaMask ? ' - MetaMask' : '' : ''}` - await addProvider('injected', displayNameInjected, true, false) - } else if (window && !window.ethereum) { - // we still add "injected" if there's no provider (just so it's visible to the user). - await addProvider('injected', 'Injected Provider', true, false) - } - - if (window && window.trustwallet) { - const displayNameInjected = `Injected Provider - TrustWallet` - await addProvider('injected-trustwallet', displayNameInjected, true, false) - } - // VM - const titleVM = 'Execution environment is local to Remix. Data is only saved to browser memory and will vanish upon reload.' - await addProvider('vm-shanghai', 'Remix VM (Shanghai)', false, true, 'shanghai', 'settingsVMShanghaiMode', titleVM) - await addProvider('vm-merge', 'Remix VM (Merge)', false, true, 'merge', 'settingsVMMergeMode', titleVM) - await addProvider('vm-london', 'Remix VM (London)', false, true, 'london', 'settingsVMLondonMode', titleVM) - await addProvider('vm-berlin', 'Remix VM (Berlin)', false, true, 'berlin', 'settingsVMBerlinMode', titleVM) - await addProvider('vm-mainnet-fork', 'Remix VM - Mainnet fork', false, true, 'merge', 'settingsVMMainnetMode', titleVM) - await addProvider('vm-sepolia-fork', 'Remix VM - Sepolia fork', false, true, 'merge', 'settingsVMSepoliaMode', titleVM) - await addProvider('vm-goerli-fork', 'Remix VM - Goerli fork', false, true, 'merge', 'settingsVMGoerliMode', titleVM) - await addProvider('vm-custom-fork', 'Remix VM - Custom fork', false, true, '', 'settingsVMCustomMode', titleVM) - - // wallet connect - await addProvider('walletconnect', 'WalletConnect', false, false) - - // external provider - await addProvider('basic-http-provider', 'Custom - External Http Provider', false, false) - await addProvider('hardhat-provider', 'Dev - Hardhat Provider', false, false) - await addProvider('ganache-provider', 'Dev - Ganache Provider', false, false) - await addProvider('foundry-provider', 'Dev - Foundry Provider', false, false) + // VM + const titleVM = 'Execution environment is local to Remix. Data is only saved to browser memory and will vanish upon reload.' + await addProvider('vm-shanghai', 'Remix VM (Shanghai)', false, true, 'shanghai', 'settingsVMShanghaiMode', titleVM) + await addProvider('vm-merge', 'Remix VM (Merge)', false, true, 'merge', 'settingsVMMergeMode', titleVM) + await addProvider('vm-london', 'Remix VM (London)', false, true, 'london', 'settingsVMLondonMode', titleVM) + await addProvider('vm-berlin', 'Remix VM (Berlin)', false, true, 'berlin', 'settingsVMBerlinMode', titleVM) + await addProvider('vm-mainnet-fork', 'Remix VM - Mainnet fork', false, true, 'merge', 'settingsVMMainnetMode', titleVM) + await addProvider('vm-sepolia-fork', 'Remix VM - Sepolia fork', false, true, 'merge', 'settingsVMSepoliaMode', titleVM) + await addProvider('vm-goerli-fork', 'Remix VM - Goerli fork', false, true, 'merge', 'settingsVMGoerliMode', titleVM) + await addProvider('vm-custom-fork', 'Remix VM - Custom fork', false, true, '', 'settingsVMCustomMode', titleVM) + + // wallet connect + await addProvider('walletconnect', 'WalletConnect', false, false) + + // external provider + await addProvider('basic-http-provider', 'Custom - External Http Provider', false, false) + await addProvider('hardhat-provider', 'Dev - Hardhat Provider', false, false) + await addProvider('ganache-provider', 'Dev - Ganache Provider', false, false) + await addProvider('foundry-provider', 'Dev - Foundry Provider', false, false) - // injected provider - await addProvider('injected-optimism-provider', 'L2 - Optimism Provider', true, false) - await addProvider('injected-arbitrum-one-provider', 'L2 - Arbitrum One Provider', true, false) - } + // injected provider + await addProvider('injected-optimism-provider', 'L2 - Optimism Provider', true, false) + await addProvider('injected-arbitrum-one-provider', 'L2 - Arbitrum One Provider', true, false) + } - writeFile (fileName, content) { - return this.call('fileManager', 'writeFile', fileName, content) - } + writeFile (fileName, content) { + return this.call('fileManager', 'writeFile', fileName, content) + } - readFile (fileName) { - return this.call('fileManager', 'readFile', fileName) - } + readFile (fileName) { + return this.call('fileManager', 'readFile', fileName) + } - resolveContractAndAddInstance (contractObject, address) { - const data = this.compilersArtefacts.getCompilerAbstract(contractObject.contract.file) + resolveContractAndAddInstance (contractObject, address) { + const data = this.compilersArtefacts.getCompilerAbstract(contractObject.contract.file) - this.compilersArtefacts.addResolvedContract(addressToString(address), data) - this.addInstance(address, contractObject.abi, contractObject.name) - } + this.compilersArtefacts.addResolvedContract(addressToString(address), data) + this.addInstance(address, contractObject.abi, contractObject.name) + } } diff --git a/apps/remix-ide/src/app/ui/landing-page/landing-page.js b/apps/remix-ide/src/app/ui/landing-page/landing-page.js index 077d6507f3..52b29e2c1d 100644 --- a/apps/remix-ide/src/app/ui/landing-page/landing-page.js +++ b/apps/remix-ide/src/app/ui/landing-page/landing-page.js @@ -5,33 +5,33 @@ import { ViewPlugin } from '@remixproject/engine-web' import { RemixUiHomeTab } from '@remix-ui/home-tab' // eslint-disable-line const profile = { - name: 'home', - displayName: 'Home', - methods: [], - events: [], - description: 'Remix Home', - icon: 'assets/img/home.webp', - location: 'mainPanel', - version: packageJson.version + name: 'home', + displayName: 'Home', + methods: [], + events: [], + description: 'Remix Home', + icon: 'assets/img/home.webp', + location: 'mainPanel', + version: packageJson.version } export class LandingPage extends ViewPlugin { - constructor (appManager, verticalIcons, fileManager, filePanel, contentImport) { - super(profile) - this.profile = profile - this.fileManager = fileManager - this.filePanel = filePanel - this.contentImport = contentImport - this.appManager = appManager - this.verticalIcons = verticalIcons - this.el = document.createElement('div') - this.el.setAttribute('id', 'landingPageHomeContainer') - this.el.setAttribute('class', 'remixui_homeContainer justify-content-between bg-light d-flex') - this.el.setAttribute('data-id', 'landingPageHomeContainer') - } + constructor (appManager, verticalIcons, fileManager, filePanel, contentImport) { + super(profile) + this.profile = profile + this.fileManager = fileManager + this.filePanel = filePanel + this.contentImport = contentImport + this.appManager = appManager + this.verticalIcons = verticalIcons + this.el = document.createElement('div') + this.el.setAttribute('id', 'landingPageHomeContainer') + this.el.setAttribute('class', 'remixui_homeContainer justify-content-between bg-light d-flex') + this.el.setAttribute('data-id', 'landingPageHomeContainer') + } - render () { - return
- -
- } + render () { + return
+ +
+ } } diff --git a/apps/remix-ide/src/app/ui/styles-guide/styleGuideClean.js b/apps/remix-ide/src/app/ui/styles-guide/styleGuideClean.js index 19ab6ca254..8cc1e8723a 100644 --- a/apps/remix-ide/src/app/ui/styles-guide/styleGuideClean.js +++ b/apps/remix-ide/src/app/ui/styles-guide/styleGuideClean.js @@ -3,282 +3,282 @@ module.exports = styleGuideClean function styleGuideClean () { - /* -------------------------------------------------------------------------- + /* -------------------------------------------------------------------------- CSS PROPERTIES -------------------------------------------------------------------------- */ - var cssProperties = { - /* ------------------------------------------------------ + var cssProperties = { + /* ------------------------------------------------------ COLORS ------------------------------------------------------ */ - colors: { - // BASIC COLORS (B&W and transparent) - transparent: 'transparent', - white: 'hsl(0, 0%, 100%)', - black: 'black', - opacityBlack: 'hsla(0, 0%, 0%, .4)', - - // BLUE - blue: 'hsla(229, 75%, 87%, 1)', - lightBlue: 'hsla(229, 75%, 87%, .5)', - backgroundBlue: 'hsla(229, 100%, 97%, 1)', - brightBlue: 'hsla(233, 91%, 58%, 1)', - azure: '#dbe9f4', - // GREY - grey: 'hsla(0, 0%, 40%, 1)', - lightGrey: 'hsla(0, 0%, 40%, .5)', - veryLightGrey: 'hsla(0, 0%, 40%, .2)', - // RED - strongRed: 'hsla(0, 100%, 71%, 1)', - red: 'hsla(0, 82%, 82%, 1)', - lightRed: 'hsla(0, 82%, 82%, .5)', - // GREEN - green: 'hsla(141, 75%, 84%, 1)', - lightGreen: 'hsla(141, 75%, 84%, .5)', - greenZing: 'hsla(148, 79%, 47%, 1)', - // PINK - pink: 'hsla(300, 69%, 76%, 1)', - lightPink: 'hsla(300, 69%, 76%, .5)', - // ORANGE - orange: 'hsla(44, 100%, 50%, 1)', - lightOrange: 'hsla(44, 100%, 50%, .5)', - // VIOLET - violet: 'hsla(240, 64%, 68%, 1)', - lightViolet: 'hsla(240, 64%, 68%, .5)' - }, - - /* ------------------------------------------------------ + colors: { + // BASIC COLORS (B&W and transparent) + transparent: 'transparent', + white: 'hsl(0, 0%, 100%)', + black: 'black', + opacityBlack: 'hsla(0, 0%, 0%, .4)', + + // BLUE + blue: 'hsla(229, 75%, 87%, 1)', + lightBlue: 'hsla(229, 75%, 87%, .5)', + backgroundBlue: 'hsla(229, 100%, 97%, 1)', + brightBlue: 'hsla(233, 91%, 58%, 1)', + azure: '#dbe9f4', + // GREY + grey: 'hsla(0, 0%, 40%, 1)', + lightGrey: 'hsla(0, 0%, 40%, .5)', + veryLightGrey: 'hsla(0, 0%, 40%, .2)', + // RED + strongRed: 'hsla(0, 100%, 71%, 1)', + red: 'hsla(0, 82%, 82%, 1)', + lightRed: 'hsla(0, 82%, 82%, .5)', + // GREEN + green: 'hsla(141, 75%, 84%, 1)', + lightGreen: 'hsla(141, 75%, 84%, .5)', + greenZing: 'hsla(148, 79%, 47%, 1)', + // PINK + pink: 'hsla(300, 69%, 76%, 1)', + lightPink: 'hsla(300, 69%, 76%, .5)', + // ORANGE + orange: 'hsla(44, 100%, 50%, 1)', + lightOrange: 'hsla(44, 100%, 50%, .5)', + // VIOLET + violet: 'hsla(240, 64%, 68%, 1)', + lightViolet: 'hsla(240, 64%, 68%, .5)' + }, + + /* ------------------------------------------------------ FONTS ------------------------------------------------------ */ - fonts: { - font: '14px/1.5 Lato, "Helvetica Neue", Helvetica, Arial, sans-serif' - }, + fonts: { + font: '14px/1.5 Lato, "Helvetica Neue", Helvetica, Arial, sans-serif' + }, - /* ------------------------------------------------------ + /* ------------------------------------------------------ BORDERS ------------------------------------------------------ */ - borders: { - secondary_borderRadius: '5px' - } + borders: { + secondary_borderRadius: '5px' } + } - /* -------------------------------------------------------------------------- + /* -------------------------------------------------------------------------- APP PROPERTIES -------------------------------------------------------------------------- */ - var appProperties = { + var appProperties = { - /* ------------------------------------------------------ + /* ------------------------------------------------------ ACE THEME ------------------------------------------------------ */ - aceTheme: '', + aceTheme: '', - /* ------------------------------------------------------ + /* ------------------------------------------------------ BACKGROUND COLORS ------------------------------------------------------ */ - primary_BackgroundColor: cssProperties.colors.white, - secondary_BackgroundColor: cssProperties.colors.white, - tertiary_BackgroundColor: cssProperties.colors.white, - quaternary_BackgroundColor: cssProperties.colors.white, - fifth_BackgroundColor: cssProperties.colors.white, - seventh_BackgroundColor: cssProperties.colors.white, - dark_BackgroundColor: cssProperties.colors.black, - light_BackgroundColor: cssProperties.colors.white, - debuggingMode_BackgroundColor: cssProperties.colors.veryLightGrey, - highlight_BackgroundColor: cssProperties.colors.veryLightGrey, - /* ------------------------------------------------------ + primary_BackgroundColor: cssProperties.colors.white, + secondary_BackgroundColor: cssProperties.colors.white, + tertiary_BackgroundColor: cssProperties.colors.white, + quaternary_BackgroundColor: cssProperties.colors.white, + fifth_BackgroundColor: cssProperties.colors.white, + seventh_BackgroundColor: cssProperties.colors.white, + dark_BackgroundColor: cssProperties.colors.black, + light_BackgroundColor: cssProperties.colors.white, + debuggingMode_BackgroundColor: cssProperties.colors.veryLightGrey, + highlight_BackgroundColor: cssProperties.colors.veryLightGrey, + /* ------------------------------------------------------ RESIZING ******************************************************** */ - ghostBar: cssProperties.colors.veryLightGrey, - draggingBar: cssProperties.colors.veryLightGrey, + ghostBar: cssProperties.colors.veryLightGrey, + draggingBar: cssProperties.colors.veryLightGrey, - /* ------------------------------------------------------ + /* ------------------------------------------------------ TEXT COLORS ******************************************************** */ - mainText_Color: cssProperties.colors.black, - supportText_Color: cssProperties.colors.grey, - - sub_supportText_Color: cssProperties.colors.black, - specialText_Color: cssProperties.colors.greenZing, - brightText_Color: cssProperties.colors.brightBlue, - oppositeText_Color: cssProperties.colors.black, - additionalText_Color: cssProperties.colors.veryLightGrey, - - errorText_Color: cssProperties.colors.strongRed, - warningText_Color: cssProperties.colors.orange, - infoText_Color: cssProperties.colors.violet, - greyedText_color: cssProperties.colors.veryLightGrey, - /* ------------------------------------------------------ + mainText_Color: cssProperties.colors.black, + supportText_Color: cssProperties.colors.grey, + + sub_supportText_Color: cssProperties.colors.black, + specialText_Color: cssProperties.colors.greenZing, + brightText_Color: cssProperties.colors.brightBlue, + oppositeText_Color: cssProperties.colors.black, + additionalText_Color: cssProperties.colors.veryLightGrey, + + errorText_Color: cssProperties.colors.strongRed, + warningText_Color: cssProperties.colors.orange, + infoText_Color: cssProperties.colors.violet, + greyedText_color: cssProperties.colors.veryLightGrey, + /* ------------------------------------------------------ ICONS ******************************************************** */ - icon_Color: cssProperties.colors.black, - icon_AltColor: cssProperties.colors.white, - icon_HoverColor: cssProperties.colors.grey, - icon_ConstantColor: cssProperties.colors.black, + icon_Color: cssProperties.colors.black, + icon_AltColor: cssProperties.colors.white, + icon_HoverColor: cssProperties.colors.grey, + icon_ConstantColor: cssProperties.colors.black, - /* ------------------------------------------------------ + /* ------------------------------------------------------ MESSAGES ******************************************************** */ - // Success - success_TextColor: cssProperties.colors.black, - success_BackgroundColor: cssProperties.colors.lightGreen, - success_BorderColor: cssProperties.colors.green, - - // Danger - danger_TextColor: cssProperties.colors.black, - danger_BackgroundColor: cssProperties.colors.lightRed, - danger_BorderColor: cssProperties.colors.red, - - // Warning - warning_TextColor: cssProperties.colors.black, - warning_BackgroundColor: cssProperties.colors.lightOrange, - warning_BorderColor: cssProperties.colors.orange, - - // Tooltip - tooltip_Color: cssProperties.colors.white, - tooltip_BackgroundColor: cssProperties.colors.grey, - tooltip_BorderColor: cssProperties.colors.grey, - - /* ------------------------------------------------------ + // Success + success_TextColor: cssProperties.colors.black, + success_BackgroundColor: cssProperties.colors.lightGreen, + success_BorderColor: cssProperties.colors.green, + + // Danger + danger_TextColor: cssProperties.colors.black, + danger_BackgroundColor: cssProperties.colors.lightRed, + danger_BorderColor: cssProperties.colors.red, + + // Warning + warning_TextColor: cssProperties.colors.black, + warning_BackgroundColor: cssProperties.colors.lightOrange, + warning_BorderColor: cssProperties.colors.orange, + + // Tooltip + tooltip_Color: cssProperties.colors.white, + tooltip_BackgroundColor: cssProperties.colors.grey, + tooltip_BorderColor: cssProperties.colors.grey, + + /* ------------------------------------------------------ DROPDOWN ******************************************************** */ - dropdown_TextColor: cssProperties.colors.black, - dropdown_BackgroundColor: cssProperties.colors.white, - dropdown_SecondaryBackgroundColor: cssProperties.colors.white, - dropdown_BorderColor: cssProperties.colors.veryLightGrey, + dropdown_TextColor: cssProperties.colors.black, + dropdown_BackgroundColor: cssProperties.colors.white, + dropdown_SecondaryBackgroundColor: cssProperties.colors.white, + dropdown_BorderColor: cssProperties.colors.veryLightGrey, - /* ------------------------------------------------------ + /* ------------------------------------------------------ INPUT ******************************************************** */ - input_TextColor: cssProperties.colors.black, - input_BackgroundColor: cssProperties.colors.white, - input_BorderColor: cssProperties.colors.veryLightGrey, + input_TextColor: cssProperties.colors.black, + input_BackgroundColor: cssProperties.colors.white, + input_BorderColor: cssProperties.colors.veryLightGrey, - /* ------------------------------------------------------ + /* ------------------------------------------------------ SOLID BORDER BOX ******************************************************** */ - solidBorderBox_TextColor: cssProperties.colors.black, - solidBorderBox_BackgroundColor: cssProperties.colors.white, - solidBorderBox_BorderColor: cssProperties.colors.white, + solidBorderBox_TextColor: cssProperties.colors.black, + solidBorderBox_BackgroundColor: cssProperties.colors.white, + solidBorderBox_BorderColor: cssProperties.colors.white, - /* ------------------------------------------------------ + /* ------------------------------------------------------ SOLID BOX ******************************************************** */ - solidBox_TextColor: cssProperties.colors.black, - solidBox_BackgroundColor: cssProperties.colors.white, + solidBox_TextColor: cssProperties.colors.black, + solidBox_BackgroundColor: cssProperties.colors.white, - /* ------------------------------------------------------ + /* ------------------------------------------------------ BUTTONS ******************************************************** */ - /* ................. + /* ................. PRIMARY .................. */ - primaryButton_TextColor: cssProperties.colors.black, - primaryButton_BackgroundColor: cssProperties.colors.white, - primaryButton_BorderColor: cssProperties.colors.black, - primaryButton_BorderWidth: '1px', + primaryButton_TextColor: cssProperties.colors.black, + primaryButton_BackgroundColor: cssProperties.colors.white, + primaryButton_BorderColor: cssProperties.colors.black, + primaryButton_BorderWidth: '1px', - /* ................. + /* ................. SECONDARY .................. */ - secondaryButton_TextColor: cssProperties.colors.black, - secondaryButton_BackgroundColor: cssProperties.colors.white, - secondaryButton_BorderColor: cssProperties.colors.black, + secondaryButton_TextColor: cssProperties.colors.black, + secondaryButton_BackgroundColor: cssProperties.colors.white, + secondaryButton_BorderColor: cssProperties.colors.black, - /* ................. + /* ................. Teriary .................. */ - teriaryButton_TextColor: cssProperties.colors.black, - teriaryButton_BackgroundColor: cssProperties.colors.white, - teriaryButton_BorderColor: cssProperties.colors.black, - /* ................. + teriaryButton_TextColor: cssProperties.colors.black, + teriaryButton_BackgroundColor: cssProperties.colors.white, + teriaryButton_BorderColor: cssProperties.colors.black, + /* ................. /* ................. Quaternary .................. */ - quaternaryButton_TextColor: cssProperties.colors.black, - quaternaryButton_BackgroundColor: cssProperties.colors.white, - quaternaryButton_BorderColor: cssProperties.colors.black, - /* ................. + quaternaryButton_TextColor: cssProperties.colors.black, + quaternaryButton_BackgroundColor: cssProperties.colors.white, + quaternaryButton_BorderColor: cssProperties.colors.black, + /* ................. /* ................. Fifth .................. */ - fifthButton_TextColor: cssProperties.colors.black, - fifthButton_BackgroundColor: cssProperties.colors.white, - fifthButton_BorderColor: cssProperties.colors.black, - /* ................. + fifthButton_TextColor: cssProperties.colors.black, + fifthButton_BackgroundColor: cssProperties.colors.white, + fifthButton_BorderColor: cssProperties.colors.black, + /* ................. /* ................. Sixth .................. */ - sixthButton_TextColor: cssProperties.colors.black, - sixthButton_BackgroundColor: cssProperties.colors.white, - sixthButton_BorderColor: cssProperties.colors.black, - /* ................. + sixthButton_TextColor: cssProperties.colors.black, + sixthButton_BackgroundColor: cssProperties.colors.white, + sixthButton_BorderColor: cssProperties.colors.black, + /* ................. SUCCESS .................. */ - successButton_TextColor: cssProperties.colors.white, - successButton_BackgroundColor: cssProperties.colors.green, - successButton_BorderColor: cssProperties.colors.green, + successButton_TextColor: cssProperties.colors.white, + successButton_BackgroundColor: cssProperties.colors.green, + successButton_BorderColor: cssProperties.colors.green, - /* ................. + /* ................. DANGER .................. */ - dangerButton_TextColor: cssProperties.colors.white, - dangerButton_BackgroundColor: cssProperties.colors.red, - dangerButton_BorderColor: cssProperties.colors.red, + dangerButton_TextColor: cssProperties.colors.white, + dangerButton_BackgroundColor: cssProperties.colors.red, + dangerButton_BorderColor: cssProperties.colors.red, - /* ................. + /* ................. WARNING .................. */ - warningButton_TextColor: cssProperties.colors.white, - warningButton_BackgroundColor: cssProperties.colors.lightOrange, - warningButton_BorderColor: cssProperties.colors.lightOrange, + warningButton_TextColor: cssProperties.colors.white, + warningButton_BackgroundColor: cssProperties.colors.lightOrange, + warningButton_BorderColor: cssProperties.colors.lightOrange, - /* ................. + /* ................. INFO .................. */ - infoButton_TextColor: cssProperties.colors.black, - infoButton_BackgroundColor: cssProperties.colors.white, - infoButton_BorderColor: cssProperties.colors.black, + infoButton_TextColor: cssProperties.colors.black, + infoButton_BackgroundColor: cssProperties.colors.white, + infoButton_BorderColor: cssProperties.colors.black, - /* ................. + /* ................. SOLIDITY .................. */ - // CALL - callButton_TextColor: cssProperties.colors.black, - callButton_BackgroundColor: cssProperties.colors.lightBlue, - callButton_BorderColor: cssProperties.colors.lightBlue, + // CALL + callButton_TextColor: cssProperties.colors.black, + callButton_BackgroundColor: cssProperties.colors.lightBlue, + callButton_BorderColor: cssProperties.colors.lightBlue, - // TRANSACTION - transactButton_TextColor: cssProperties.colors.black, - transactButton_BackgroundColor: cssProperties.colors.lightRed, - transactButton_BorderColor: cssProperties.colors.lightRed, + // TRANSACTION + transactButton_TextColor: cssProperties.colors.black, + transactButton_BackgroundColor: cssProperties.colors.lightRed, + transactButton_BorderColor: cssProperties.colors.lightRed, - // CONSTANT - constantButton_TextColor: cssProperties.colors.black, - constantButton_BackgroundColor: cssProperties.colors.lightBlue, - constantButton_BorderColor: cssProperties.colors.lightBlue, + // CONSTANT + constantButton_TextColor: cssProperties.colors.black, + constantButton_BackgroundColor: cssProperties.colors.lightBlue, + constantButton_BorderColor: cssProperties.colors.lightBlue, - // PAYABLE TRANSACTION - transactPayableButton_TextColor: cssProperties.colors.black, - transactPayableButton_BackgroundColor: cssProperties.colors.red, - transactPayableButton_BorderColor: cssProperties.colors.red, + // PAYABLE TRANSACTION + transactPayableButton_TextColor: cssProperties.colors.black, + transactPayableButton_BackgroundColor: cssProperties.colors.red, + transactPayableButton_BorderColor: cssProperties.colors.red, - /* ------------------------------------------------------ + /* ------------------------------------------------------ UI ELEMENTS ******************************************************** */ - uiElements: { - solidBorderBox: (opts = {}) => ` + uiElements: { + solidBorderBox: (opts = {}) => ` background-color : ${opts.BackgroundColor}; border : 1px solid ${opts.BorderColor}; color : ${opts.Color}; @@ -291,7 +291,7 @@ function styleGuideClean () { width : 100%; `, - solidBox: (opts = {}) => ` + solidBox: (opts = {}) => ` background-color : ${opts.BackgroundColor}; color : ${opts.Color}; font-size : 12px; @@ -302,7 +302,7 @@ function styleGuideClean () { width : 100%; `, - dottedBorderBox: (opts = {}) => ` + dottedBorderBox: (opts = {}) => ` background-color : ${opts.BackgroundColor}; border : .2em dotted ${opts.BorderColor}; color : ${opts.Color}; @@ -314,7 +314,7 @@ function styleGuideClean () { word-break : break-word; `, - inputField: (opts = {}) => ` + inputField: (opts = {}) => ` background-color : ${opts.BackgroundColor}; border : 1px solid ${opts.BorderColor}; color : ${opts.Color}; @@ -326,7 +326,7 @@ function styleGuideClean () { word-break : normal; `, - dropdown: (opts = {}) => ` + dropdown: (opts = {}) => ` background-color : ${opts.BackgroundColor}; border : 1px solid ${opts.BorderColor}; color : ${opts.Color}; @@ -343,7 +343,7 @@ function styleGuideClean () { word-break : normal; `, - button: (opts = {}) => ` + button: (opts = {}) => ` margin : 1px; background-color : ${opts.BackgroundColor}; color : ${opts.Color}; @@ -364,492 +364,492 @@ function styleGuideClean () { border-color : ${opts.BorderColor}; border-style : ${opts.BorderStyle}; ` - } } + } - /* -------------------------------------------------------------------------- + /* -------------------------------------------------------------------------- REMIX PROPERTIES -------------------------------------------------------------------------- */ - var remixProperties = { - /* ------------------------------------------------------ + var remixProperties = { + /* ------------------------------------------------------ REMIX GENERAL /* ------------------------------------------------------ */ - remix: { - modalDialog_BackgroundColor_Primary: appProperties.primary_BackgroundColor, - modalDialog_text_Primary: appProperties.mainText_Color, - modalDialog_text_Secondary: appProperties.supportText_Color, - modalDialog_text_Link: appProperties.brightText_Color, - modalDialog_text_Em: appProperties.specialText_Color, - modalDialog_Header_Footer_BackgroundColor: appProperties.secondary_BackgroundColor, - modalDialog_Header_Footer_Color: appProperties.mainText_Color, - modalDialog_BoxDottedBorder_BackgroundColor: appProperties.solidBorderBox_BackgroundColor, - modalDialog_BoxDottedBorder_BorderColor: appProperties.solidBorderBox_BorderColor, - modalDialog_BoxDottedBorder_Color: appProperties.solidBorderBox_TextColor, - - tooltip_CopyToClipboard_BackgroundColor: appProperties.tooltip_BackgroundColor, - tooltip_CopyToClipboard_Color: appProperties.tooltip_Color, - - icon_Color_CopyToClipboard: appProperties.icon_Color, - icon_HoverColor_CopyToClipboard: appProperties.icon_HoverColor, - - solidBox: appProperties.uiElements.solidBorderBox({ - BackgroundColor: appProperties.solidBox_BackgroundColor, - Color: appProperties.solidBox_TextColor - }) - }, - - /* ------------------------------------------------------ + remix: { + modalDialog_BackgroundColor_Primary: appProperties.primary_BackgroundColor, + modalDialog_text_Primary: appProperties.mainText_Color, + modalDialog_text_Secondary: appProperties.supportText_Color, + modalDialog_text_Link: appProperties.brightText_Color, + modalDialog_text_Em: appProperties.specialText_Color, + modalDialog_Header_Footer_BackgroundColor: appProperties.secondary_BackgroundColor, + modalDialog_Header_Footer_Color: appProperties.mainText_Color, + modalDialog_BoxDottedBorder_BackgroundColor: appProperties.solidBorderBox_BackgroundColor, + modalDialog_BoxDottedBorder_BorderColor: appProperties.solidBorderBox_BorderColor, + modalDialog_BoxDottedBorder_Color: appProperties.solidBorderBox_TextColor, + + tooltip_CopyToClipboard_BackgroundColor: appProperties.tooltip_BackgroundColor, + tooltip_CopyToClipboard_Color: appProperties.tooltip_Color, + + icon_Color_CopyToClipboard: appProperties.icon_Color, + icon_HoverColor_CopyToClipboard: appProperties.icon_HoverColor, + + solidBox: appProperties.uiElements.solidBorderBox({ + BackgroundColor: appProperties.solidBox_BackgroundColor, + Color: appProperties.solidBox_TextColor + }) + }, + + /* ------------------------------------------------------ LEFT PANEL (FILE PANEL) /* ------------------------------------------------------ */ - leftPanel: { - backgroundColor_Panel: appProperties.primary_BackgroundColor, - backgroundColor_FileExplorer: appProperties.tertiary_BackgroundColor, + leftPanel: { + backgroundColor_Panel: appProperties.primary_BackgroundColor, + backgroundColor_FileExplorer: appProperties.tertiary_BackgroundColor, - text_Primary: appProperties.mainText_Color, - text_Secondary: appProperties.supportText_Color, - text_Teriary: appProperties.sub_supportText_Color, + text_Primary: appProperties.mainText_Color, + text_Secondary: appProperties.supportText_Color, + text_Teriary: appProperties.sub_supportText_Color, - bar_Ghost: appProperties.ghostBar, - bar_Dragging: appProperties.draggingBar, - dragbarBorderRight: '2px solid ' + cssProperties.colors.veryLightGrey, + bar_Ghost: appProperties.ghostBar, + bar_Dragging: appProperties.draggingBar, + dragbarBorderRight: '2px solid ' + cssProperties.colors.veryLightGrey, - icon_Color_Menu: appProperties.icon_Color, - icon_HoverColor_Menu: appProperties.icon_HoverColor, + icon_Color_Menu: appProperties.icon_Color, + icon_HoverColor_Menu: appProperties.icon_HoverColor, - icon_Color_TogglePanel: appProperties.icon_Color, - icon_HoverColor_TogglePanel: appProperties.icon_HoverColor + icon_Color_TogglePanel: appProperties.icon_Color, + icon_HoverColor_TogglePanel: appProperties.icon_HoverColor - }, + }, - /* ------------------------------------------------------ + /* ------------------------------------------------------ EDITOR /* ------------------------------------------------------ */ - editor: { - backgroundColor_Panel: appProperties.primary_BackgroundColor, - backgroundColor_Editor: appProperties.light_BackgroundColor, - backgroundColor_Tabs_Highlights: appProperties.secondary_BackgroundColor, - backgroundColor_Editor_Context_Highlights: appProperties.secondary_BackgroundColor, - backgroundColor_Editor_Context_Error_Highlights: appProperties.error_BackgroundColor, - backgroundColor_DebuggerMode: appProperties.debuggingMode_BackgroundColor, + editor: { + backgroundColor_Panel: appProperties.primary_BackgroundColor, + backgroundColor_Editor: appProperties.light_BackgroundColor, + backgroundColor_Tabs_Highlights: appProperties.secondary_BackgroundColor, + backgroundColor_Editor_Context_Highlights: appProperties.secondary_BackgroundColor, + backgroundColor_Editor_Context_Error_Highlights: appProperties.error_BackgroundColor, + backgroundColor_DebuggerMode: appProperties.debuggingMode_BackgroundColor, - text_Primary: appProperties.mainText_Color, - text_Secondary: appProperties.supportText_Color, - text_Teriary: appProperties.sub_supportText_Color, - text_Editor: '', + text_Primary: appProperties.mainText_Color, + text_Secondary: appProperties.supportText_Color, + text_Teriary: appProperties.sub_supportText_Color, + text_Editor: '', - icon_Color_Editor: appProperties.icon_Color, - icon_HoverColor_Editor: appProperties.icon_HoverColor + icon_Color_Editor: appProperties.icon_Color, + icon_HoverColor_Editor: appProperties.icon_HoverColor - }, + }, - /* ------------------------------------------------------ + /* ------------------------------------------------------ TERMINAL /* ------------------------------------------------------ */ - terminal: { - backgroundColor_Menu: appProperties.secondary_BackgroundColor, - backgroundColor_Terminal: appProperties.seventh_BackgroundColor, - backgroundColor_TerminalCLI: appProperties.seventh_BackgroundColor, - backgroundImage_Terminal: "url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4NCjwhLS0gR2VuZXJhdG9yOiBBZG9iZSBJbGx1c3RyYXRvciAxNi4wLjAsIFNWRyBFeHBvcnQgUGx1Zy1JbiAuIFNWRyBWZXJzaW9uOiA2LjAwIEJ1aWxkIDApICAtLT4NCjwhRE9DVFlQRSBzdmcgUFVCTElDICItLy9XM0MvL0RURCBTVkcgMS4xLy9FTiIgImh0dHA6Ly93d3cudzMub3JnL0dyYXBoaWNzL1NWRy8xLjEvRFREL3N2ZzExLmR0ZCI+DQo8c3ZnIHZlcnNpb249IjEuMSIgaWQ9IkxheWVyXzEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHg9IjBweCIgeT0iMHB4Ig0KCSB3aWR0aD0iNTEycHgiIGhlaWdodD0iNTEycHgiIHZpZXdCb3g9IjAgMCA1MTIgNTEyIiBlbmFibGUtYmFja2dyb3VuZD0ibmV3IDAgMCA1MTIgNTEyIiB4bWw6c3BhY2U9InByZXNlcnZlIj4NCgk8ZyBvcGFjaXR5PSIwLjEiPg0KCQk8Zz4NCgkJCTxwYXRoIGZpbGw9IiM0MTQwNDIiIGQ9Ik03MC41ODIsNDI4LjkwNGMwLjgxMSwwLDEuNjIyLDAuMjg1LDIuNDM3LDAuODUzYzAuODExLDAuNTcxLDEuMjE4LDEuMzQsMS4yMTgsMi4zMTQNCgkJCQljMCwyLjI3Ny0xLjA1OSwzLjQ5Ni0zLjE2OCwzLjY1NmMtNS4wMzgsMC44MTQtOS4zODEsMi4zNTYtMTMuMDM3LDQuNjNjLTMuNjU1LDIuMjc2LTYuNjYzLDUuMTE3LTkuMDE2LDguNTI4DQoJCQkJYy0yLjM1NywzLjQxMS00LjEwNCw3LjI3Mi01LjIzOSwxMS41NzVjLTEuMTM5LDQuMzA3LTEuNzA2LDguODE0LTEuNzA2LDEzLjUyNHYzMi42NTNjMCwyLjI3My0xLjEzOSwzLjQxMS0zLjQxMiwzLjQxMQ0KCQkJCWMtMi4yNzcsMC0zLjQxMi0xLjEzOC0zLjQxMi0zLjQxMXYtNzQuMzIzYzAtMi4yNzMsMS4xMzUtMy40MTEsMy40MTItMy40MTFjMi4yNzMsMCwzLjQxMiwxLjEzOCwzLjQxMiwzLjQxMXYxNS4xMDgNCgkJCQljMS40NjItMi40MzcsMy4yMDYtNC43NTIsNS4yMzktNi45NDVjMi4wMjktMi4xOTMsNC4yNjQtNC4xNDMsNi43MDEtNS44NDhjMi40MzctMS43MDYsNS4wNzYtMy4wODUsNy45MTktNC4xNDMNCgkJCQlDNjQuNzcxLDQyOS40MzMsNjcuNjU4LDQyOC45MDQsNzAuNTgyLDQyOC45MDR6Ii8+DQoJCQk8cGF0aCBmaWxsPSIjNDE0MDQyIiBkPSJNMTM3Ljc3Myw0MjcuMTk4YzUuNjg1LDAsMTAuOTY2LDEuMTgxLDE1LjgzOSwzLjUzNGM0Ljg3NCwyLjM1Niw5LjA1NSw1LjQ4MiwxMi41NSw5LjM4MQ0KCQkJCWMzLjQ5MiwzLjg5OSw2LjIxNCw4LjQwNyw4LjE2NCwxMy41MjRjMS45NDksNS4xMTcsMi45MjQsMTAuNDQsMi45MjQsMTUuOTYxYzAsMC45NzYtMC4zNjYsMS43OS0xLjA5NywyLjQzOA0KCQkJCWMtMC43MzEsMC42NS0xLjU4MywwLjk3NS0yLjU1OSwwLjk3NWgtNjcuOTg3YzAuNDg3LDQuMjI2LDEuNTg0LDguMjg1LDMuMjksMTIuMTg0YzEuNzA2LDMuODk5LDMuOTM3LDcuMzEyLDYuNzAxLDEwLjIzNA0KCQkJCWMyLjc2MSwyLjkyNSw2LjAwOCw1LjI4MSw5Ljc0OCw3LjA2N2MzLjczNSwxLjc4OSw3Ljg3NywyLjY4MSwxMi40MjgsMi42ODFjMTIuMDIxLDAsMjEuMzYtNC43OSwyOC4wMjMtMTQuMzc3DQoJCQkJYzAuNjQ3LTEuMTM2LDEuNjIyLTEuNzA2LDIuOTI0LTEuNzA2YzIuMjczLDAsMy40MTIsMS4xMzksMy40MTIsMy40MTJjMCwwLjE2My0wLjE2NCwwLjczLTAuNDg3LDEuNzA1DQoJCQkJYy0zLjQxMiw2LjAxMy04LjIwNSwxMC40NzktMTQuMzc3LDEzLjQwMmMtNi4xNzYsMi45MjQtMTIuNjcxLDQuMzg3LTE5LjQ5NSw0LjM4N2MtNS42ODksMC0xMC45MjgtMS4xODEtMTUuNzE4LTMuNTMzDQoJCQkJYy00Ljc5My0yLjM1NC04LjkzNi01LjQ4My0xMi40MjgtOS4zODJjLTMuNDk1LTMuODk5LTYuMjE0LTguNDA3LTguMTYzLTEzLjUyNGMtMS45NS01LjExOC0yLjkyNC0xMC40MzctMi45MjQtMTUuOTYyDQoJCQkJYzAtNS41MjEsMC45NzUtMTAuODQ0LDIuOTI0LTE1Ljk2MWMxLjk0OS01LjExNyw0LjY2OC05LjYyNSw4LjE2My0xMy41MjRjMy40OTItMy44OTgsNy42MzQtNy4wMjQsMTIuNDI4LTkuMzgxDQoJCQkJQzEyNi44NDYsNDI4LjM3OSwxMzIuMDg0LDQyNy4xOTgsMTM3Ljc3Myw0MjcuMTk4eiBNMTY5Ljk0LDQ2Ni4xODhjLTAuMzI4LTQuMjIzLTEuMzQxLTguMjg1LTMuMDQ2LTEyLjE4NA0KCQkJCWMtMS43MDYtMy44OTktMy45ODItNy4zMTItNi44MjMtMTAuMjM1Yy0yLjg0NC0yLjkyNC02LjE3NS01LjI3Ny05Ljk5MS03LjA2N2MtMy44MTktMS43ODUtNy45Mi0yLjY4LTEyLjMwNi0yLjY4DQoJCQkJYy00LjU1LDAtOC42OTIsMC44OTUtMTIuNDI4LDIuNjhjLTMuNzM5LDEuNzktNi45ODcsNC4xNDQtOS43NDgsNy4wNjdjLTIuNzY0LDIuOTI0LTQuOTk1LDYuMzM2LTYuNzAxLDEwLjIzNQ0KCQkJCWMtMS43MDYsMy44OTgtMi44MDIsNy45NjEtMy4yOSwxMi4xODRIMTY5Ljk0eiIvPg0KCQkJPHBhdGggZmlsbD0iIzQxNDA0MiIgZD0iTTMwNC42OSw0MjcuNDQxYzUuMDM0LDAsOS41MDQsMS4wMTgsMTMuNDAyLDMuMDQ3YzMuODk5LDIuMDMzLDcuMTg5LDQuNjcyLDkuODcsNy45Mg0KCQkJCWMyLjY4LDMuMjUxLDQuNzA5LDcuMDY2LDYuMDkyLDExLjQ1MmMxLjM3OSw0LjM4NywyLjA3LDguODU2LDIuMDcsMTMuNDAydjQzLjYyYzAsMC45NzUtMC4zNjUsMS43ODktMS4wOTcsMi40MzgNCgkJCQljLTAuNzMsMC42NDYtMS41MDMsMC45NzUtMi4zMTMsMC45NzVjLTIuMjc2LDAtMy40MTItMS4xNC0zLjQxMi0zLjQxMnYtNDMuNjJjMC0zLjU3MS0wLjUyOS03LjEwNC0xLjU4NC0xMC42DQoJCQkJYy0xLjA1OS0zLjQ5MS0yLjYwMi02LjYxOC00LjYzLTkuMzgyYy0yLjAzMy0yLjc2MS00LjU5Mi00Ljk1My03LjY3Ny02LjU4Yy0zLjA4OC0xLjYyMS02LjY2Mi0yLjQzNi0xMC43MjItMi40MzYNCgkJCQljLTUuMiwwLTkuNTg3LDEuMjE4LTEzLjE1OSwzLjY1NGMtMy41NzQsMi40MzgtNi40NTcsNS41NjYtOC42NSw5LjM4MmMtMi4xOTMsMy44MTktMy44MTgsOC4wNDItNC44NzQsMTIuNjcyDQoJCQkJYy0xLjA1OSw0LjYzLTEuNTg0LDkuMDU4LTEuNTg0LDEzLjI4djMzLjYyOWMwLDAuOTc1LTAuMzY1LDEuNzg5LTEuMDk2LDIuNDM4Yy0wLjczMSwwLjY0Ni0xLjUwNSwwLjk3NS0yLjMxNSwwLjk3NQ0KCQkJCWMtMi4yNzYsMC0zLjQxMS0xLjE0LTMuNDExLTMuNDEydi00My42MmMwLTMuNTcxLTAuNTMtNy4xMDQtMS41ODUtMTAuNmMtMS4wNTgtMy40OTEtMi42MDEtNi42MTgtNC42MjktOS4zODINCgkJCQljLTIuMDM0LTIuNzYxLTQuNTkyLTQuOTUzLTcuNjc3LTYuNThjLTMuMDg3LTEuNjIxLTYuNjYzLTIuNDM2LTEwLjcyMi0yLjQzNmMtNS4wMzcsMC05LjM0NCwwLjg5NS0xMi45MTUsMi42OA0KCQkJCWMtMy41NzUsMS43OS02LjU0Miw0LjI2Ni04Ljg5NSw3LjQzM2MtMi4zNTcsMy4xNjctNC4wNjMsNi45NDQtNS4xMTcsMTEuMzMxYy0xLjA1OSw0LjM4Ni0xLjU4NCw5LjEtMS41ODQsMTQuMTM0djMuODk5djAuMjQzDQoJCQkJdjMyLjg5N2MwLDIuMjcyLTEuMTM4LDMuNDEyLTMuNDEyLDMuNDEyYy0yLjI3NiwwLTMuNDExLTEuMTQtMy40MTEtMy40MTJ2LTc0LjU2N2MwLTIuMjczLDEuMTM1LTMuNDExLDMuNDExLTMuNDExDQoJCQkJYzIuMjczLDAsMy40MTIsMS4xMzgsMy40MTIsMy40MTF2MTIuNDI4YzIuOTI0LTUuMTk3LDYuODYxLTkuMzgyLDExLjgxOS0xMi41NWM0Ljk1NC0zLjE2NywxMC41MTctNC43NTIsMTYuNjkyLTQuNzUyDQoJCQkJYzYuOTgzLDAsMTIuOTk1LDEuOTkxLDE4LjAzMiw1Ljk3YzUuMDMzLDMuOTgzLDguNjg4LDkuMjIzLDEwLjk2NiwxNS43MTljMi43Ni02LjMzNiw2LjczOS0xMS41MzMsMTEuOTQtMTUuNTk2DQoJCQkJQzI5MS4xMjUsNDI5LjQ3NSwyOTcuMzgsNDI3LjQ0MSwzMDQuNjksNDI3LjQ0MXoiLz4NCgkJCTxwYXRoIGZpbGw9IiM0MTQwNDIiIGQ9Ik0zNzguNzUzLDQyOS4zOTJjMC44MTEsMCwxLjU4NCwwLjM2NSwyLjMxNCwxLjA5N2MwLjczMSwwLjczLDEuMDk3LDEuNTA0LDEuMDk3LDIuMzE0djc0LjA4DQoJCQkJYzAsMC44MTQtMC4zNjUsMS41ODQtMS4wOTcsMi4zMTVjLTAuNzMsMC43My0xLjUwNCwxLjA5Ny0yLjMxNCwxLjA5N2MtMC45NzUsMC0xLjc5LTAuMzY2LTIuNDM4LTEuMDk3DQoJCQkJYy0wLjY1LTAuNzMxLTAuOTc1LTEuNTAxLTAuOTc1LTIuMzE1di03NC4wOGMwLTAuODExLDAuMzI0LTEuNTg0LDAuOTc1LTIuMzE0QzM3Ni45NjMsNDI5Ljc1NywzNzcuNzc4LDQyOS4zOTIsMzc4Ljc1Myw0MjkuMzkyeiINCgkJCQkvPg0KCQkJPHBhdGggZmlsbD0iIzQxNDA0MiIgZD0iTTQ3My4zNCw0MjguNjZjMi4yNzMsMCwzLjQxMiwxLjEzOSwzLjQxMiwzLjQxMWwtMC40ODcsMS45NWwtMjQuMzY4LDM1LjMzNGwyNC4zNjgsMzUuNTc3DQoJCQkJYzAuMzIzLDAuOTc2LDAuNDg3LDEuNjI2LDAuNDg3LDEuOTVjMCwyLjI3Mi0xLjEzOSwzLjQxMi0zLjQxMiwzLjQxMmMtMS4zMDIsMC0yLjE5My0wLjQ4OC0yLjY4LTEuNDYzbC0yMi45MDYtMzMuMzg0DQoJCQkJbC0yMi42NjMsMzMuMzg0Yy0wLjgxNCwwLjk3NS0xLjc5LDEuNDYzLTIuOTI0LDEuNDYzYy0yLjI3NywwLTMuNDExLTEuMTQtMy40MTEtMy40MTJjMC0wLjMyNCwwLjE1OS0wLjk3NSwwLjQ4Ni0xLjk1DQoJCQkJbDI0LjM2OS0zNS41NzdsLTI0LjM2OS0zNS4zMzRsLTAuNDg2LTEuOTVjMC0yLjI3MiwxLjEzNC0zLjQxMSwzLjQxMS0zLjQxMWMxLjEzNCwwLDIuMTA5LDAuNDg3LDIuOTI0LDEuNDYybDIyLjY2MywzMy4xNDENCgkJCQlsMjIuOTA2LTMzLjE0MUM0NzEuMTQ2LDQyOS4xNDcsNDcyLjAzOCw0MjguNjYsNDczLjM0LDQyOC42NnoiLz4NCgkJPC9nPg0KCQk8Zz4NCgkJCTxnPg0KCQkJCTxnIG9wYWNpdHk9IjAuNDUiPg0KCQkJCQk8Zz4NCgkJCQkJCTxwb2x5Z29uIGZpbGw9IiMwMTAxMDEiIHBvaW50cz0iMTUwLjczNCwxOTYuMjEyIDI1NS45NjksMzQ0LjUwOCAyNTUuOTY5LDI1OC4zODciLz4NCgkJCQkJPC9nPg0KCQkJCTwvZz4NCgkJCQk8ZyBvcGFjaXR5PSIwLjgiPg0KCQkJCQk8Zz4NCgkJCQkJCTxwb2x5Z29uIGZpbGw9IiMwMTAxMDEiIHBvaW50cz0iMjU1Ljk2OSwyNTguMzg3IDI1NS45NjksMzQ0LjUwOCAzNjEuMjY3LDE5Ni4yMTIiLz4NCgkJCQkJPC9nPg0KCQkJCTwvZz4NCgkJCQk8ZyBvcGFjaXR5PSIwLjYiPg0KCQkJCQk8Zz4NCgkJCQkJCTxwb2x5Z29uIGZpbGw9IiMwMTAxMDEiIHBvaW50cz0iMjU1Ljk2OSwxMjYuNzgxIDE1MC43MzMsMTc0LjYxMSAyNTUuOTY5LDIzNi44MTggMzYxLjIwNCwxNzQuNjExIi8+DQoJCQkJCTwvZz4NCgkJCQk8L2c+DQoJCQkJPGcgb3BhY2l0eT0iMC40NSI+DQoJCQkJCTxnPg0KCQkJCQkJPHBvbHlnb24gZmlsbD0iIzAxMDEwMSIgcG9pbnRzPSIxNTAuNzM0LDE3NC42MTIgMjU1Ljk2OSwyMzYuODE4IDI1NS45NjksMTI2Ljc4MiAyNTUuOTY5LDAuMDAxIi8+DQoJCQkJCTwvZz4NCgkJCQk8L2c+DQoJCQkJPGcgb3BhY2l0eT0iMC44Ij4NCgkJCQkJPGc+DQoJCQkJCQk8cG9seWdvbiBmaWxsPSIjMDEwMTAxIiBwb2ludHM9IjI1NS45NjksMCAyNTUuOTY5LDEyNi43ODEgMjU1Ljk2OSwyMzYuODE4IDM2MS4yMDQsMTc0LjYxMSIvPg0KCQkJCQk8L2c+DQoJCQkJPC9nPg0KCQkJPC9nPg0KCQk8L2c+DQoJPC9nPg0KPC9zdmc+DQo=')", - - text_Primary: appProperties.mainText_Color, - text_Secondary: appProperties.supportText_Color, - text_RegularLog: appProperties.mainText_Color, - text_InfoLog: appProperties.supportText_Color, - text_ErrorLog: appProperties.errorText_Color, - text_WarnLog: appProperties.warningText_Color, - text_Title_TransactionLog: appProperties.infoText_Color, - text_Regular_TransactionLog: appProperties.supportText_Color, - text_Button: appProperties.oppositeText_Color, - - blockBorderTop: '2px solid ' + cssProperties.colors.veryLightGrey, - - icon_Color_Log_Succeed: appProperties.success_BorderColor, - icon_Color_Log_Failed: appProperties.errorText_Color, - icon_BackgroundColor_Log_Call: appProperties.infoText_Color, - icon_Color_Log_Call: appProperties.icon_AltColor, - - icon_Color_TogglePanel: appProperties.icon_Color, - icon_HoverColor_TogglePanel: appProperties.icon_HoverColor, - icon_Color_Menu: appProperties.icon_Color, - icon_HoverColor_Menu: appProperties.icon_HoverColor, - - bar_Ghost: appProperties.ghostBar, - bar_Dragging: appProperties.draggingBar, - - input_Search_MenuBar: appProperties.uiElements.inputField({ - BackgroundColor: appProperties.input_BackgroundColor, - BorderColor: appProperties.input_BorderColor, - Color: appProperties.input_TextColor - }), - - dropdown_Filter_MenuBar: appProperties.uiElements.dropdown({ - BackgroundColor: appProperties.dropdown_BackgroundColor, - BorderColor: appProperties.dropdown_BorderColor, - Color: appProperties.dropdown_TextColor - }), - - button_Log_Debug: appProperties.uiElements.button({ - BackgroundColor: appProperties.quaternaryButton_BackgroundColor, - BorderColor: appProperties.infoButton_BorderColor, - Color: appProperties.infoButton_TextColor - }), - - button_Log_Details: appProperties.uiElements.button({ - BackgroundColor: appProperties.quaternaryButton_BackgroundColor, - BorderColor: appProperties.quaternaryButton_BorderColor, - Color: appProperties.quaternaryButton_TextColor - }) - - }, - - /* ------------------------------------------------------ + terminal: { + backgroundColor_Menu: appProperties.secondary_BackgroundColor, + backgroundColor_Terminal: appProperties.seventh_BackgroundColor, + backgroundColor_TerminalCLI: appProperties.seventh_BackgroundColor, + backgroundImage_Terminal: "url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4NCjwhLS0gR2VuZXJhdG9yOiBBZG9iZSBJbGx1c3RyYXRvciAxNi4wLjAsIFNWRyBFeHBvcnQgUGx1Zy1JbiAuIFNWRyBWZXJzaW9uOiA2LjAwIEJ1aWxkIDApICAtLT4NCjwhRE9DVFlQRSBzdmcgUFVCTElDICItLy9XM0MvL0RURCBTVkcgMS4xLy9FTiIgImh0dHA6Ly93d3cudzMub3JnL0dyYXBoaWNzL1NWRy8xLjEvRFREL3N2ZzExLmR0ZCI+DQo8c3ZnIHZlcnNpb249IjEuMSIgaWQ9IkxheWVyXzEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHg9IjBweCIgeT0iMHB4Ig0KCSB3aWR0aD0iNTEycHgiIGhlaWdodD0iNTEycHgiIHZpZXdCb3g9IjAgMCA1MTIgNTEyIiBlbmFibGUtYmFja2dyb3VuZD0ibmV3IDAgMCA1MTIgNTEyIiB4bWw6c3BhY2U9InByZXNlcnZlIj4NCgk8ZyBvcGFjaXR5PSIwLjEiPg0KCQk8Zz4NCgkJCTxwYXRoIGZpbGw9IiM0MTQwNDIiIGQ9Ik03MC41ODIsNDI4LjkwNGMwLjgxMSwwLDEuNjIyLDAuMjg1LDIuNDM3LDAuODUzYzAuODExLDAuNTcxLDEuMjE4LDEuMzQsMS4yMTgsMi4zMTQNCgkJCQljMCwyLjI3Ny0xLjA1OSwzLjQ5Ni0zLjE2OCwzLjY1NmMtNS4wMzgsMC44MTQtOS4zODEsMi4zNTYtMTMuMDM3LDQuNjNjLTMuNjU1LDIuMjc2LTYuNjYzLDUuMTE3LTkuMDE2LDguNTI4DQoJCQkJYy0yLjM1NywzLjQxMS00LjEwNCw3LjI3Mi01LjIzOSwxMS41NzVjLTEuMTM5LDQuMzA3LTEuNzA2LDguODE0LTEuNzA2LDEzLjUyNHYzMi42NTNjMCwyLjI3My0xLjEzOSwzLjQxMS0zLjQxMiwzLjQxMQ0KCQkJCWMtMi4yNzcsMC0zLjQxMi0xLjEzOC0zLjQxMi0zLjQxMXYtNzQuMzIzYzAtMi4yNzMsMS4xMzUtMy40MTEsMy40MTItMy40MTFjMi4yNzMsMCwzLjQxMiwxLjEzOCwzLjQxMiwzLjQxMXYxNS4xMDgNCgkJCQljMS40NjItMi40MzcsMy4yMDYtNC43NTIsNS4yMzktNi45NDVjMi4wMjktMi4xOTMsNC4yNjQtNC4xNDMsNi43MDEtNS44NDhjMi40MzctMS43MDYsNS4wNzYtMy4wODUsNy45MTktNC4xNDMNCgkJCQlDNjQuNzcxLDQyOS40MzMsNjcuNjU4LDQyOC45MDQsNzAuNTgyLDQyOC45MDR6Ii8+DQoJCQk8cGF0aCBmaWxsPSIjNDE0MDQyIiBkPSJNMTM3Ljc3Myw0MjcuMTk4YzUuNjg1LDAsMTAuOTY2LDEuMTgxLDE1LjgzOSwzLjUzNGM0Ljg3NCwyLjM1Niw5LjA1NSw1LjQ4MiwxMi41NSw5LjM4MQ0KCQkJCWMzLjQ5MiwzLjg5OSw2LjIxNCw4LjQwNyw4LjE2NCwxMy41MjRjMS45NDksNS4xMTcsMi45MjQsMTAuNDQsMi45MjQsMTUuOTYxYzAsMC45NzYtMC4zNjYsMS43OS0xLjA5NywyLjQzOA0KCQkJCWMtMC43MzEsMC42NS0xLjU4MywwLjk3NS0yLjU1OSwwLjk3NWgtNjcuOTg3YzAuNDg3LDQuMjI2LDEuNTg0LDguMjg1LDMuMjksMTIuMTg0YzEuNzA2LDMuODk5LDMuOTM3LDcuMzEyLDYuNzAxLDEwLjIzNA0KCQkJCWMyLjc2MSwyLjkyNSw2LjAwOCw1LjI4MSw5Ljc0OCw3LjA2N2MzLjczNSwxLjc4OSw3Ljg3NywyLjY4MSwxMi40MjgsMi42ODFjMTIuMDIxLDAsMjEuMzYtNC43OSwyOC4wMjMtMTQuMzc3DQoJCQkJYzAuNjQ3LTEuMTM2LDEuNjIyLTEuNzA2LDIuOTI0LTEuNzA2YzIuMjczLDAsMy40MTIsMS4xMzksMy40MTIsMy40MTJjMCwwLjE2My0wLjE2NCwwLjczLTAuNDg3LDEuNzA1DQoJCQkJYy0zLjQxMiw2LjAxMy04LjIwNSwxMC40NzktMTQuMzc3LDEzLjQwMmMtNi4xNzYsMi45MjQtMTIuNjcxLDQuMzg3LTE5LjQ5NSw0LjM4N2MtNS42ODksMC0xMC45MjgtMS4xODEtMTUuNzE4LTMuNTMzDQoJCQkJYy00Ljc5My0yLjM1NC04LjkzNi01LjQ4My0xMi40MjgtOS4zODJjLTMuNDk1LTMuODk5LTYuMjE0LTguNDA3LTguMTYzLTEzLjUyNGMtMS45NS01LjExOC0yLjkyNC0xMC40MzctMi45MjQtMTUuOTYyDQoJCQkJYzAtNS41MjEsMC45NzUtMTAuODQ0LDIuOTI0LTE1Ljk2MWMxLjk0OS01LjExNyw0LjY2OC05LjYyNSw4LjE2My0xMy41MjRjMy40OTItMy44OTgsNy42MzQtNy4wMjQsMTIuNDI4LTkuMzgxDQoJCQkJQzEyNi44NDYsNDI4LjM3OSwxMzIuMDg0LDQyNy4xOTgsMTM3Ljc3Myw0MjcuMTk4eiBNMTY5Ljk0LDQ2Ni4xODhjLTAuMzI4LTQuMjIzLTEuMzQxLTguMjg1LTMuMDQ2LTEyLjE4NA0KCQkJCWMtMS43MDYtMy44OTktMy45ODItNy4zMTItNi44MjMtMTAuMjM1Yy0yLjg0NC0yLjkyNC02LjE3NS01LjI3Ny05Ljk5MS03LjA2N2MtMy44MTktMS43ODUtNy45Mi0yLjY4LTEyLjMwNi0yLjY4DQoJCQkJYy00LjU1LDAtOC42OTIsMC44OTUtMTIuNDI4LDIuNjhjLTMuNzM5LDEuNzktNi45ODcsNC4xNDQtOS43NDgsNy4wNjdjLTIuNzY0LDIuOTI0LTQuOTk1LDYuMzM2LTYuNzAxLDEwLjIzNQ0KCQkJCWMtMS43MDYsMy44OTgtMi44MDIsNy45NjEtMy4yOSwxMi4xODRIMTY5Ljk0eiIvPg0KCQkJPHBhdGggZmlsbD0iIzQxNDA0MiIgZD0iTTMwNC42OSw0MjcuNDQxYzUuMDM0LDAsOS41MDQsMS4wMTgsMTMuNDAyLDMuMDQ3YzMuODk5LDIuMDMzLDcuMTg5LDQuNjcyLDkuODcsNy45Mg0KCQkJCWMyLjY4LDMuMjUxLDQuNzA5LDcuMDY2LDYuMDkyLDExLjQ1MmMxLjM3OSw0LjM4NywyLjA3LDguODU2LDIuMDcsMTMuNDAydjQzLjYyYzAsMC45NzUtMC4zNjUsMS43ODktMS4wOTcsMi40MzgNCgkJCQljLTAuNzMsMC42NDYtMS41MDMsMC45NzUtMi4zMTMsMC45NzVjLTIuMjc2LDAtMy40MTItMS4xNC0zLjQxMi0zLjQxMnYtNDMuNjJjMC0zLjU3MS0wLjUyOS03LjEwNC0xLjU4NC0xMC42DQoJCQkJYy0xLjA1OS0zLjQ5MS0yLjYwMi02LjYxOC00LjYzLTkuMzgyYy0yLjAzMy0yLjc2MS00LjU5Mi00Ljk1My03LjY3Ny02LjU4Yy0zLjA4OC0xLjYyMS02LjY2Mi0yLjQzNi0xMC43MjItMi40MzYNCgkJCQljLTUuMiwwLTkuNTg3LDEuMjE4LTEzLjE1OSwzLjY1NGMtMy41NzQsMi40MzgtNi40NTcsNS41NjYtOC42NSw5LjM4MmMtMi4xOTMsMy44MTktMy44MTgsOC4wNDItNC44NzQsMTIuNjcyDQoJCQkJYy0xLjA1OSw0LjYzLTEuNTg0LDkuMDU4LTEuNTg0LDEzLjI4djMzLjYyOWMwLDAuOTc1LTAuMzY1LDEuNzg5LTEuMDk2LDIuNDM4Yy0wLjczMSwwLjY0Ni0xLjUwNSwwLjk3NS0yLjMxNSwwLjk3NQ0KCQkJCWMtMi4yNzYsMC0zLjQxMS0xLjE0LTMuNDExLTMuNDEydi00My42MmMwLTMuNTcxLTAuNTMtNy4xMDQtMS41ODUtMTAuNmMtMS4wNTgtMy40OTEtMi42MDEtNi42MTgtNC42MjktOS4zODINCgkJCQljLTIuMDM0LTIuNzYxLTQuNTkyLTQuOTUzLTcuNjc3LTYuNThjLTMuMDg3LTEuNjIxLTYuNjYzLTIuNDM2LTEwLjcyMi0yLjQzNmMtNS4wMzcsMC05LjM0NCwwLjg5NS0xMi45MTUsMi42OA0KCQkJCWMtMy41NzUsMS43OS02LjU0Miw0LjI2Ni04Ljg5NSw3LjQzM2MtMi4zNTcsMy4xNjctNC4wNjMsNi45NDQtNS4xMTcsMTEuMzMxYy0xLjA1OSw0LjM4Ni0xLjU4NCw5LjEtMS41ODQsMTQuMTM0djMuODk5djAuMjQzDQoJCQkJdjMyLjg5N2MwLDIuMjcyLTEuMTM4LDMuNDEyLTMuNDEyLDMuNDEyYy0yLjI3NiwwLTMuNDExLTEuMTQtMy40MTEtMy40MTJ2LTc0LjU2N2MwLTIuMjczLDEuMTM1LTMuNDExLDMuNDExLTMuNDExDQoJCQkJYzIuMjczLDAsMy40MTIsMS4xMzgsMy40MTIsMy40MTF2MTIuNDI4YzIuOTI0LTUuMTk3LDYuODYxLTkuMzgyLDExLjgxOS0xMi41NWM0Ljk1NC0zLjE2NywxMC41MTctNC43NTIsMTYuNjkyLTQuNzUyDQoJCQkJYzYuOTgzLDAsMTIuOTk1LDEuOTkxLDE4LjAzMiw1Ljk3YzUuMDMzLDMuOTgzLDguNjg4LDkuMjIzLDEwLjk2NiwxNS43MTljMi43Ni02LjMzNiw2LjczOS0xMS41MzMsMTEuOTQtMTUuNTk2DQoJCQkJQzI5MS4xMjUsNDI5LjQ3NSwyOTcuMzgsNDI3LjQ0MSwzMDQuNjksNDI3LjQ0MXoiLz4NCgkJCTxwYXRoIGZpbGw9IiM0MTQwNDIiIGQ9Ik0zNzguNzUzLDQyOS4zOTJjMC44MTEsMCwxLjU4NCwwLjM2NSwyLjMxNCwxLjA5N2MwLjczMSwwLjczLDEuMDk3LDEuNTA0LDEuMDk3LDIuMzE0djc0LjA4DQoJCQkJYzAsMC44MTQtMC4zNjUsMS41ODQtMS4wOTcsMi4zMTVjLTAuNzMsMC43My0xLjUwNCwxLjA5Ny0yLjMxNCwxLjA5N2MtMC45NzUsMC0xLjc5LTAuMzY2LTIuNDM4LTEuMDk3DQoJCQkJYy0wLjY1LTAuNzMxLTAuOTc1LTEuNTAxLTAuOTc1LTIuMzE1di03NC4wOGMwLTAuODExLDAuMzI0LTEuNTg0LDAuOTc1LTIuMzE0QzM3Ni45NjMsNDI5Ljc1NywzNzcuNzc4LDQyOS4zOTIsMzc4Ljc1Myw0MjkuMzkyeiINCgkJCQkvPg0KCQkJPHBhdGggZmlsbD0iIzQxNDA0MiIgZD0iTTQ3My4zNCw0MjguNjZjMi4yNzMsMCwzLjQxMiwxLjEzOSwzLjQxMiwzLjQxMWwtMC40ODcsMS45NWwtMjQuMzY4LDM1LjMzNGwyNC4zNjgsMzUuNTc3DQoJCQkJYzAuMzIzLDAuOTc2LDAuNDg3LDEuNjI2LDAuNDg3LDEuOTVjMCwyLjI3Mi0xLjEzOSwzLjQxMi0zLjQxMiwzLjQxMmMtMS4zMDIsMC0yLjE5My0wLjQ4OC0yLjY4LTEuNDYzbC0yMi45MDYtMzMuMzg0DQoJCQkJbC0yMi42NjMsMzMuMzg0Yy0wLjgxNCwwLjk3NS0xLjc5LDEuNDYzLTIuOTI0LDEuNDYzYy0yLjI3NywwLTMuNDExLTEuMTQtMy40MTEtMy40MTJjMC0wLjMyNCwwLjE1OS0wLjk3NSwwLjQ4Ni0xLjk1DQoJCQkJbDI0LjM2OS0zNS41NzdsLTI0LjM2OS0zNS4zMzRsLTAuNDg2LTEuOTVjMC0yLjI3MiwxLjEzNC0zLjQxMSwzLjQxMS0zLjQxMWMxLjEzNCwwLDIuMTA5LDAuNDg3LDIuOTI0LDEuNDYybDIyLjY2MywzMy4xNDENCgkJCQlsMjIuOTA2LTMzLjE0MUM0NzEuMTQ2LDQyOS4xNDcsNDcyLjAzOCw0MjguNjYsNDczLjM0LDQyOC42NnoiLz4NCgkJPC9nPg0KCQk8Zz4NCgkJCTxnPg0KCQkJCTxnIG9wYWNpdHk9IjAuNDUiPg0KCQkJCQk8Zz4NCgkJCQkJCTxwb2x5Z29uIGZpbGw9IiMwMTAxMDEiIHBvaW50cz0iMTUwLjczNCwxOTYuMjEyIDI1NS45NjksMzQ0LjUwOCAyNTUuOTY5LDI1OC4zODciLz4NCgkJCQkJPC9nPg0KCQkJCTwvZz4NCgkJCQk8ZyBvcGFjaXR5PSIwLjgiPg0KCQkJCQk8Zz4NCgkJCQkJCTxwb2x5Z29uIGZpbGw9IiMwMTAxMDEiIHBvaW50cz0iMjU1Ljk2OSwyNTguMzg3IDI1NS45NjksMzQ0LjUwOCAzNjEuMjY3LDE5Ni4yMTIiLz4NCgkJCQkJPC9nPg0KCQkJCTwvZz4NCgkJCQk8ZyBvcGFjaXR5PSIwLjYiPg0KCQkJCQk8Zz4NCgkJCQkJCTxwb2x5Z29uIGZpbGw9IiMwMTAxMDEiIHBvaW50cz0iMjU1Ljk2OSwxMjYuNzgxIDE1MC43MzMsMTc0LjYxMSAyNTUuOTY5LDIzNi44MTggMzYxLjIwNCwxNzQuNjExIi8+DQoJCQkJCTwvZz4NCgkJCQk8L2c+DQoJCQkJPGcgb3BhY2l0eT0iMC40NSI+DQoJCQkJCTxnPg0KCQkJCQkJPHBvbHlnb24gZmlsbD0iIzAxMDEwMSIgcG9pbnRzPSIxNTAuNzM0LDE3NC42MTIgMjU1Ljk2OSwyMzYuODE4IDI1NS45NjksMTI2Ljc4MiAyNTUuOTY5LDAuMDAxIi8+DQoJCQkJCTwvZz4NCgkJCQk8L2c+DQoJCQkJPGcgb3BhY2l0eT0iMC44Ij4NCgkJCQkJPGc+DQoJCQkJCQk8cG9seWdvbiBmaWxsPSIjMDEwMTAxIiBwb2ludHM9IjI1NS45NjksMCAyNTUuOTY5LDEyNi43ODEgMjU1Ljk2OSwyMzYuODE4IDM2MS4yMDQsMTc0LjYxMSIvPg0KCQkJCQk8L2c+DQoJCQkJPC9nPg0KCQkJPC9nPg0KCQk8L2c+DQoJPC9nPg0KPC9zdmc+DQo=')", + + text_Primary: appProperties.mainText_Color, + text_Secondary: appProperties.supportText_Color, + text_RegularLog: appProperties.mainText_Color, + text_InfoLog: appProperties.supportText_Color, + text_ErrorLog: appProperties.errorText_Color, + text_WarnLog: appProperties.warningText_Color, + text_Title_TransactionLog: appProperties.infoText_Color, + text_Regular_TransactionLog: appProperties.supportText_Color, + text_Button: appProperties.oppositeText_Color, + + blockBorderTop: '2px solid ' + cssProperties.colors.veryLightGrey, + + icon_Color_Log_Succeed: appProperties.success_BorderColor, + icon_Color_Log_Failed: appProperties.errorText_Color, + icon_BackgroundColor_Log_Call: appProperties.infoText_Color, + icon_Color_Log_Call: appProperties.icon_AltColor, + + icon_Color_TogglePanel: appProperties.icon_Color, + icon_HoverColor_TogglePanel: appProperties.icon_HoverColor, + icon_Color_Menu: appProperties.icon_Color, + icon_HoverColor_Menu: appProperties.icon_HoverColor, + + bar_Ghost: appProperties.ghostBar, + bar_Dragging: appProperties.draggingBar, + + input_Search_MenuBar: appProperties.uiElements.inputField({ + BackgroundColor: appProperties.input_BackgroundColor, + BorderColor: appProperties.input_BorderColor, + Color: appProperties.input_TextColor + }), + + dropdown_Filter_MenuBar: appProperties.uiElements.dropdown({ + BackgroundColor: appProperties.dropdown_BackgroundColor, + BorderColor: appProperties.dropdown_BorderColor, + Color: appProperties.dropdown_TextColor + }), + + button_Log_Debug: appProperties.uiElements.button({ + BackgroundColor: appProperties.quaternaryButton_BackgroundColor, + BorderColor: appProperties.infoButton_BorderColor, + Color: appProperties.infoButton_TextColor + }), + + button_Log_Details: appProperties.uiElements.button({ + BackgroundColor: appProperties.quaternaryButton_BackgroundColor, + BorderColor: appProperties.quaternaryButton_BorderColor, + Color: appProperties.quaternaryButton_TextColor + }) + + }, + + /* ------------------------------------------------------ RIGHT PANEL /* ------------------------------------------------------ */ - rightPanel: { - backgroundColor_Panel: appProperties.fifth_BackgroundColor, - backgroundColor_Tab: appProperties.fifth_BackgroundColor, - BackgroundColor_Pre: appProperties.primary_BackgroundColor, + rightPanel: { + backgroundColor_Panel: appProperties.fifth_BackgroundColor, + backgroundColor_Tab: appProperties.fifth_BackgroundColor, + BackgroundColor_Pre: appProperties.primary_BackgroundColor, - text_Primary: appProperties.mainText_Color, - text_Secondary: appProperties.supportText_Color, - text_Teriary: appProperties.sub_supportText_Color, - text_link: appProperties.brightText_Color, + text_Primary: appProperties.mainText_Color, + text_Secondary: appProperties.supportText_Color, + text_Teriary: appProperties.sub_supportText_Color, + text_link: appProperties.brightText_Color, - bar_Ghost: appProperties.ghostBar, - bar_Dragging: appProperties.draggingBar, - dragbarWidth: '2px', - dragbarBackgroundColor: cssProperties.colors.veryLightGrey, + bar_Ghost: appProperties.ghostBar, + bar_Dragging: appProperties.draggingBar, + dragbarWidth: '2px', + dragbarBackgroundColor: cssProperties.colors.veryLightGrey, - icon_Color_TogglePanel: appProperties.icon_Color, - icon_HoverColor_TogglePanel: appProperties.icon_HoverColor, + icon_Color_TogglePanel: appProperties.icon_Color, + icon_HoverColor_TogglePanel: appProperties.icon_HoverColor, - message_Warning_BackgroundColor: appProperties.warning_BackgroundColor, - message_Warning_BorderColor: appProperties.warning_BorderColor, - message_Warning_Color: appProperties.warning_TextColor, + message_Warning_BackgroundColor: appProperties.warning_BackgroundColor, + message_Warning_BorderColor: appProperties.warning_BorderColor, + message_Warning_Color: appProperties.warning_TextColor, - message_Error_BackgroundColor: appProperties.danger_BackgroundColor, - message_Error_BorderColor: appProperties.danger_BorderColor, - message_Error_Color: appProperties.danger_TextColor, + message_Error_BackgroundColor: appProperties.danger_BackgroundColor, + message_Error_BorderColor: appProperties.danger_BorderColor, + message_Error_Color: appProperties.danger_TextColor, - message_Success_BackgroundColor: appProperties.success_BackgroundColor, - message_Success_BorderColor: appProperties.success_BorderColor, - message_Success_Color: appProperties.success_TextColor, + message_Success_BackgroundColor: appProperties.success_BackgroundColor, + message_Success_BorderColor: appProperties.success_BorderColor, + message_Success_Color: appProperties.success_TextColor, - /* :::::::::::::: + /* :::::::::::::: COMPILE TAB ::::::::::::::: */ - compileTab: { - button_Compile: appProperties.uiElements.button({ - BackgroundColor: appProperties.primaryButton_BackgroundColor, - BorderColor: appProperties.primaryButton_BorderColor, - Color: appProperties.primaryButton_TextColor, - BorderWidth: appProperties.primaryButton_BorderWidth, - BorderRadius: cssProperties.borders.primary_borderRadius, - BorderStyle: 'solid' - }), - - button_Details: appProperties.uiElements.button({ - BackgroundColor: appProperties.secondaryButton_BackgroundColor, - BorderColor: appProperties.secondaryButton_BorderColor, - Color: appProperties.secondaryButton_TextColor, - BorderWidth: appProperties.primaryButton_BorderWidth, - BorderRadius: cssProperties.borders.primary_borderRadius, - BorderStyle: 'solid' - }), - - button_Publish: appProperties.uiElements.button({ - BackgroundColor: appProperties.secondaryButton_BackgroundColor, - BorderColor: appProperties.secondaryButton_BorderColor, - Color: appProperties.secondaryButton_TextColor, - BorderWidth: appProperties.primaryButton_BorderWidth, - BorderRadius: cssProperties.borders.primary_borderRadius, - BorderStyle: 'solid' - }), - - dropdown_CompileContract: appProperties.uiElements.dropdown({ - BackgroundColor: appProperties.dropdown_BackgroundColor, - BorderColor: appProperties.dropdown_BorderColor, - Color: appProperties.dropdown_TextColor, - BorderWidth: appProperties.primaryButton_BorderWidth, - BorderRadius: cssProperties.borders.primary_borderRadius, - BorderStyle: 'solid' - }), - - box_CompileContainer: appProperties.uiElements.solidBorderBox({ - BackgroundColor: appProperties.quaternary_BackgroundColor, - BorderColor: appProperties.solidBorderBox_BackgroundColor, - Color: appProperties.solidBorderBox_TextColor - }), - - icon_WarnCompilation_Color: appProperties.warning_BackgroundColor - - }, - - /* :::::::::::::: + compileTab: { + button_Compile: appProperties.uiElements.button({ + BackgroundColor: appProperties.primaryButton_BackgroundColor, + BorderColor: appProperties.primaryButton_BorderColor, + Color: appProperties.primaryButton_TextColor, + BorderWidth: appProperties.primaryButton_BorderWidth, + BorderRadius: cssProperties.borders.primary_borderRadius, + BorderStyle: 'solid' + }), + + button_Details: appProperties.uiElements.button({ + BackgroundColor: appProperties.secondaryButton_BackgroundColor, + BorderColor: appProperties.secondaryButton_BorderColor, + Color: appProperties.secondaryButton_TextColor, + BorderWidth: appProperties.primaryButton_BorderWidth, + BorderRadius: cssProperties.borders.primary_borderRadius, + BorderStyle: 'solid' + }), + + button_Publish: appProperties.uiElements.button({ + BackgroundColor: appProperties.secondaryButton_BackgroundColor, + BorderColor: appProperties.secondaryButton_BorderColor, + Color: appProperties.secondaryButton_TextColor, + BorderWidth: appProperties.primaryButton_BorderWidth, + BorderRadius: cssProperties.borders.primary_borderRadius, + BorderStyle: 'solid' + }), + + dropdown_CompileContract: appProperties.uiElements.dropdown({ + BackgroundColor: appProperties.dropdown_BackgroundColor, + BorderColor: appProperties.dropdown_BorderColor, + Color: appProperties.dropdown_TextColor, + BorderWidth: appProperties.primaryButton_BorderWidth, + BorderRadius: cssProperties.borders.primary_borderRadius, + BorderStyle: 'solid' + }), + + box_CompileContainer: appProperties.uiElements.solidBorderBox({ + BackgroundColor: appProperties.quaternary_BackgroundColor, + BorderColor: appProperties.solidBorderBox_BackgroundColor, + Color: appProperties.solidBorderBox_TextColor + }), + + icon_WarnCompilation_Color: appProperties.warning_BackgroundColor + + }, + + /* :::::::::::::: RUN TAB ::::::::::::::: */ - runTab: { - - additionalText_Color: appProperties.additionalText_Color, - - box_RunTab: appProperties.uiElements.solidBorderBox({ - BackgroundColor: appProperties.solidBox_BackgroundColor, - Color: appProperties.solidBox_TextColor - }), - - box_Info_RunTab: appProperties.uiElements.dottedBorderBox({ - BackgroundColor: appProperties.solidBorderBox_BackgroundColor, - BorderColor: appProperties.solidBorderBox_BorderColor, - Color: appProperties.solidBorderBox_TextColor - }), - - dropdown_RunTab: appProperties.uiElements.dropdown({ - BackgroundColor: appProperties.dropdown_BackgroundColor, - BorderColor: appProperties.dropdown_BorderColor, - Color: appProperties.dropdown_TextColor - }), - titlebox_RunTab: appProperties.uiElements.dropdown({ - BackgroundColor: appProperties.dropdown_SecondaryBackgroundColor, - BorderColor: appProperties.dropdown_BorderColor, - Color: appProperties.dropdown_TextColor - }), - - input_RunTab: appProperties.uiElements.inputField({ - BackgroundColor: appProperties.input_BackgroundColor, - BorderColor: appProperties.input_BorderColor, - Color: appProperties.input_TextColor - }), - - box_Instance: appProperties.uiElements.solidBox({ - BackgroundColor: appProperties.solidBox_BackgroundColor, - Color: appProperties.solidBox_TextColor - }), - - borderBox_Instance: appProperties.uiElements.solidBorderBox({ - BackgroundColor: appProperties.solidBox_BackgroundColor, - Color: appProperties.solidBox_TextColor, - BorderColor: appProperties.solidBorderBox_BorderColor - }), - - button_atAddress: appProperties.uiElements.button({ - BackgroundColor: appProperties.primaryButton_BackgroundColor, - BorderColor: appProperties.primaryButton_BorderColor, - Color: appProperties.primaryButton_TextColor, - BorderWidth: appProperties.primaryButton_BorderWidth, - BorderRadius: cssProperties.borders.primary_borderRadius, - BorderStyle: 'solid' - }), - button_Create: appProperties.uiElements.button({ - BackgroundColor: appProperties.transactButton_BackgroundColor, - BorderColor: appProperties.transactButton_BorderColor, - Color: appProperties.transactButton_TextColor, - BorderWidth: appProperties.primaryButton_BorderWidth, - BorderRadius: cssProperties.borders.primary_borderRadius, - BorderStyle: 'solid' - }), - button_Constant: appProperties.uiElements.button({ - BackgroundColor: appProperties.constantButton_BackgroundColor, - BorderColor: appProperties.constantButton_BorderColor, - Color: appProperties.constantButton_TextColor, - BorderWidth: appProperties.primaryButton_BorderWidth, - BorderRadius: cssProperties.borders.primary_borderRadius, - BorderStyle: 'solid' - }), - button_Instance_Call: appProperties.uiElements.button({ - BackgroundColor: appProperties.callButton_BackgroundColor, - BorderColor: appProperties.callButton_BorderColor, - Color: appProperties.callButton_TextColor, - BorderWidth: appProperties.primaryButton_BorderWidth, - BorderRadius: cssProperties.borders.primary_borderRadius, - BorderStyle: 'solid' - }), - button_Instance_Transact: appProperties.uiElements.button({ - BackgroundColor: appProperties.transactButton_BackgroundColor, - BorderColor: appProperties.transactButton_BorderColor, - Color: appProperties.transactButton_TextColor, - BorderWidth: appProperties.primaryButton_BorderWidth, - BorderRadius: cssProperties.borders.primary_borderRadius, - BorderStyle: 'solid' - }), - - button_Instance_TransactPayable: appProperties.uiElements.button({ - BackgroundColor: appProperties.transactPayableButton_BackgroundColor, - BorderColor: appProperties.transactPayableButton_BorderColor, - Color: appProperties.transactPayableButton_TextColor, - BorderWidth: appProperties.primaryButton_BorderWidth, - BorderRadius: cssProperties.borders.primary_borderRadius, - BorderStyle: 'solid' - }), - - icon_Color_Instance_CopyToClipboard: appProperties.icon_Color, - icon_AltColor_Instance_CopyToClipboard: appProperties.icon_AltColor, - icon_HoverColor_Instance_CopyToClipboard: appProperties.icon_HoverColor, - - icon_Color: appProperties.icon_Color, - icon_HoverColor: appProperties.icon_HoverColor - - }, - - /* :::::::::::::: + runTab: { + + additionalText_Color: appProperties.additionalText_Color, + + box_RunTab: appProperties.uiElements.solidBorderBox({ + BackgroundColor: appProperties.solidBox_BackgroundColor, + Color: appProperties.solidBox_TextColor + }), + + box_Info_RunTab: appProperties.uiElements.dottedBorderBox({ + BackgroundColor: appProperties.solidBorderBox_BackgroundColor, + BorderColor: appProperties.solidBorderBox_BorderColor, + Color: appProperties.solidBorderBox_TextColor + }), + + dropdown_RunTab: appProperties.uiElements.dropdown({ + BackgroundColor: appProperties.dropdown_BackgroundColor, + BorderColor: appProperties.dropdown_BorderColor, + Color: appProperties.dropdown_TextColor + }), + titlebox_RunTab: appProperties.uiElements.dropdown({ + BackgroundColor: appProperties.dropdown_SecondaryBackgroundColor, + BorderColor: appProperties.dropdown_BorderColor, + Color: appProperties.dropdown_TextColor + }), + + input_RunTab: appProperties.uiElements.inputField({ + BackgroundColor: appProperties.input_BackgroundColor, + BorderColor: appProperties.input_BorderColor, + Color: appProperties.input_TextColor + }), + + box_Instance: appProperties.uiElements.solidBox({ + BackgroundColor: appProperties.solidBox_BackgroundColor, + Color: appProperties.solidBox_TextColor + }), + + borderBox_Instance: appProperties.uiElements.solidBorderBox({ + BackgroundColor: appProperties.solidBox_BackgroundColor, + Color: appProperties.solidBox_TextColor, + BorderColor: appProperties.solidBorderBox_BorderColor + }), + + button_atAddress: appProperties.uiElements.button({ + BackgroundColor: appProperties.primaryButton_BackgroundColor, + BorderColor: appProperties.primaryButton_BorderColor, + Color: appProperties.primaryButton_TextColor, + BorderWidth: appProperties.primaryButton_BorderWidth, + BorderRadius: cssProperties.borders.primary_borderRadius, + BorderStyle: 'solid' + }), + button_Create: appProperties.uiElements.button({ + BackgroundColor: appProperties.transactButton_BackgroundColor, + BorderColor: appProperties.transactButton_BorderColor, + Color: appProperties.transactButton_TextColor, + BorderWidth: appProperties.primaryButton_BorderWidth, + BorderRadius: cssProperties.borders.primary_borderRadius, + BorderStyle: 'solid' + }), + button_Constant: appProperties.uiElements.button({ + BackgroundColor: appProperties.constantButton_BackgroundColor, + BorderColor: appProperties.constantButton_BorderColor, + Color: appProperties.constantButton_TextColor, + BorderWidth: appProperties.primaryButton_BorderWidth, + BorderRadius: cssProperties.borders.primary_borderRadius, + BorderStyle: 'solid' + }), + button_Instance_Call: appProperties.uiElements.button({ + BackgroundColor: appProperties.callButton_BackgroundColor, + BorderColor: appProperties.callButton_BorderColor, + Color: appProperties.callButton_TextColor, + BorderWidth: appProperties.primaryButton_BorderWidth, + BorderRadius: cssProperties.borders.primary_borderRadius, + BorderStyle: 'solid' + }), + button_Instance_Transact: appProperties.uiElements.button({ + BackgroundColor: appProperties.transactButton_BackgroundColor, + BorderColor: appProperties.transactButton_BorderColor, + Color: appProperties.transactButton_TextColor, + BorderWidth: appProperties.primaryButton_BorderWidth, + BorderRadius: cssProperties.borders.primary_borderRadius, + BorderStyle: 'solid' + }), + + button_Instance_TransactPayable: appProperties.uiElements.button({ + BackgroundColor: appProperties.transactPayableButton_BackgroundColor, + BorderColor: appProperties.transactPayableButton_BorderColor, + Color: appProperties.transactPayableButton_TextColor, + BorderWidth: appProperties.primaryButton_BorderWidth, + BorderRadius: cssProperties.borders.primary_borderRadius, + BorderStyle: 'solid' + }), + + icon_Color_Instance_CopyToClipboard: appProperties.icon_Color, + icon_AltColor_Instance_CopyToClipboard: appProperties.icon_AltColor, + icon_HoverColor_Instance_CopyToClipboard: appProperties.icon_HoverColor, + + icon_Color: appProperties.icon_Color, + icon_HoverColor: appProperties.icon_HoverColor + + }, + + /* :::::::::::::: TEST TAB ::::::::::::::: */ - testTab: { - box_listTests: appProperties.uiElements.solidBorderBox({ - BackgroundColor: appProperties.solidBorderBox_BackgroundColor, - BorderColor: appProperties.solidBorderBox_BackgroundColor, - Color: appProperties.solidBorderBox_TextColor - }), - - button_runTests: appProperties.uiElements.button({ - BackgroundColor: appProperties.primaryButton_BackgroundColor, - BorderColor: appProperties.primaryButton_BorderColor, - Color: appProperties.primaryButton_TextColor, - BorderWidth: appProperties.primaryButton_BorderWidth, - BorderRadius: cssProperties.borders.primary_borderRadius, - BorderStyle: 'solid' - }), - - button_generateTestFile: appProperties.uiElements.button({ - BackgroundColor: appProperties.primaryButton_BackgroundColor, - BorderColor: appProperties.primaryButton_BorderColor, - Color: appProperties.primaryButton_TextColor, - BorderWidth: appProperties.primaryButton_BorderWidth, - BorderRadius: cssProperties.borders.primary_borderRadius, - BorderStyle: 'solid' - }), - - color_testPass: appProperties.success_BackgroundColor, - color_testFail: appProperties.danger_BackgroundColor - }, - - /* :::::::::::::: + testTab: { + box_listTests: appProperties.uiElements.solidBorderBox({ + BackgroundColor: appProperties.solidBorderBox_BackgroundColor, + BorderColor: appProperties.solidBorderBox_BackgroundColor, + Color: appProperties.solidBorderBox_TextColor + }), + + button_runTests: appProperties.uiElements.button({ + BackgroundColor: appProperties.primaryButton_BackgroundColor, + BorderColor: appProperties.primaryButton_BorderColor, + Color: appProperties.primaryButton_TextColor, + BorderWidth: appProperties.primaryButton_BorderWidth, + BorderRadius: cssProperties.borders.primary_borderRadius, + BorderStyle: 'solid' + }), + + button_generateTestFile: appProperties.uiElements.button({ + BackgroundColor: appProperties.primaryButton_BackgroundColor, + BorderColor: appProperties.primaryButton_BorderColor, + Color: appProperties.primaryButton_TextColor, + BorderWidth: appProperties.primaryButton_BorderWidth, + BorderRadius: cssProperties.borders.primary_borderRadius, + BorderStyle: 'solid' + }), + + color_testPass: appProperties.success_BackgroundColor, + color_testFail: appProperties.danger_BackgroundColor + }, + + /* :::::::::::::: SETTINGS TAB ::::::::::::::: */ - settingsTab: { - box_SolidityVersionInfo: appProperties.uiElements.dottedBorderBox({ - BackgroundColor: appProperties.solidBorderBox_BackgroundColor, - BorderColor: appProperties.solidBorderBox_BorderColor, - Color: appProperties.solidBorderBox_TextColor - }), - - dropdown_SelectCompiler: appProperties.uiElements.dropdown({ - BackgroundColor: appProperties.dropdown_BackgroundColor, - BorderColor: appProperties.dropdown_BorderColor, - Color: appProperties.dropdown_TextColor - }), - - button_LoadPlugin: appProperties.uiElements.button({ - BackgroundColor: appProperties.secondaryButton_BackgroundColor, - BorderColor: appProperties.secondaryButton_BorderColor, - Color: appProperties.secondaryButton_TextColor, - BorderWidth: appProperties.primaryButton_BorderWidth, - BorderRadius: cssProperties.borders.primary_borderRadius, - BorderStyle: 'solid' - }), - button_initPlugin: appProperties.uiElements.button({ - BackgroundColor: appProperties.transactButton_BackgroundColor, - BorderColor: appProperties.transactButton_BorderColor, - Color: appProperties.secondaryButton_TextColor, - BorderWidth: appProperties.primaryButton_BorderWidth, - BorderRadius: cssProperties.borders.primary_borderRadius, - BorderStyle: 'solid' - }) - }, - - /* :::::::::::::: + settingsTab: { + box_SolidityVersionInfo: appProperties.uiElements.dottedBorderBox({ + BackgroundColor: appProperties.solidBorderBox_BackgroundColor, + BorderColor: appProperties.solidBorderBox_BorderColor, + Color: appProperties.solidBorderBox_TextColor + }), + + dropdown_SelectCompiler: appProperties.uiElements.dropdown({ + BackgroundColor: appProperties.dropdown_BackgroundColor, + BorderColor: appProperties.dropdown_BorderColor, + Color: appProperties.dropdown_TextColor + }), + + button_LoadPlugin: appProperties.uiElements.button({ + BackgroundColor: appProperties.secondaryButton_BackgroundColor, + BorderColor: appProperties.secondaryButton_BorderColor, + Color: appProperties.secondaryButton_TextColor, + BorderWidth: appProperties.primaryButton_BorderWidth, + BorderRadius: cssProperties.borders.primary_borderRadius, + BorderStyle: 'solid' + }), + button_initPlugin: appProperties.uiElements.button({ + BackgroundColor: appProperties.transactButton_BackgroundColor, + BorderColor: appProperties.transactButton_BorderColor, + Color: appProperties.secondaryButton_TextColor, + BorderWidth: appProperties.primaryButton_BorderWidth, + BorderRadius: cssProperties.borders.primary_borderRadius, + BorderStyle: 'solid' + }) + }, + + /* :::::::::::::: DEBUGGER TAB ::::::::::::::: */ - debuggerTab: { - text_Primary: appProperties.mainText_Color, - text_Secondary: appProperties.supportText_Color, - text_BgHighlight: appProperties.highlight_BackgroundColor, - - box_Debugger: appProperties.uiElements.solidBorderBox({ - BackgroundColor: appProperties.solidBorderBox_BackgroundColor, - BorderColor: appProperties.solidBorderBox_BackgroundColor, - Color: appProperties.solidBorderBox_TextColor - }), - - button_Debugger: appProperties.uiElements.button({ - BackgroundColor: appProperties.secondaryButton_BackgroundColor, - BorderColor: appProperties.secondaryButton_BorderColor, - Color: appProperties.secondaryButton_TextColor, - BorderWidth: appProperties.primaryButton_BorderWidth, - BorderRadius: cssProperties.borders.primary_borderRadius, - BorderStyle: 'solid' - }), - - button_Debugger_icon_Color: appProperties.icon_ConstantColor, - button_Debugger_icon_HoverColor: appProperties.icon_HoverColor, - - dropdown_Debugger: appProperties.uiElements.dropdown({ - BackgroundColor: cssProperties.colors.veryLightGrey, - BorderColor: appProperties.dropdown_BorderColor, - Color: appProperties.dropdown_TextColor - }), - - input_Debugger: appProperties.uiElements.inputField({ - BackgroundColor: appProperties.input_BackgroundColor, - BorderColor: appProperties.input_BorderColor, - Color: appProperties.input_TextColor - }), - - debuggerDropdowns_Instructions_Highlight_BackgroundColor: appProperties.secondary_BackgroundColor - - }, - - /* :::::::::::::: + debuggerTab: { + text_Primary: appProperties.mainText_Color, + text_Secondary: appProperties.supportText_Color, + text_BgHighlight: appProperties.highlight_BackgroundColor, + + box_Debugger: appProperties.uiElements.solidBorderBox({ + BackgroundColor: appProperties.solidBorderBox_BackgroundColor, + BorderColor: appProperties.solidBorderBox_BackgroundColor, + Color: appProperties.solidBorderBox_TextColor + }), + + button_Debugger: appProperties.uiElements.button({ + BackgroundColor: appProperties.secondaryButton_BackgroundColor, + BorderColor: appProperties.secondaryButton_BorderColor, + Color: appProperties.secondaryButton_TextColor, + BorderWidth: appProperties.primaryButton_BorderWidth, + BorderRadius: cssProperties.borders.primary_borderRadius, + BorderStyle: 'solid' + }), + + button_Debugger_icon_Color: appProperties.icon_ConstantColor, + button_Debugger_icon_HoverColor: appProperties.icon_HoverColor, + + dropdown_Debugger: appProperties.uiElements.dropdown({ + BackgroundColor: cssProperties.colors.veryLightGrey, + BorderColor: appProperties.dropdown_BorderColor, + Color: appProperties.dropdown_TextColor + }), + + input_Debugger: appProperties.uiElements.inputField({ + BackgroundColor: appProperties.input_BackgroundColor, + BorderColor: appProperties.input_BorderColor, + Color: appProperties.input_TextColor + }), + + debuggerDropdowns_Instructions_Highlight_BackgroundColor: appProperties.secondary_BackgroundColor + + }, + + /* :::::::::::::: ANALYSIS TAB ::::::::::::::: */ - analysisTab: { - button_Run_AnalysisTab: appProperties.uiElements.button({ - BackgroundColor: appProperties.primaryButton_BackgroundColor, - BorderColor: appProperties.primaryButton_BorderColor, - Color: appProperties.primaryButton_TextColor, - BorderWidth: appProperties.primaryButton_BorderWidth, - BorderRadius: cssProperties.borders.primary_borderRadius, - BorderStyle: 'solid' - }), - - box_AnalysisContainer: appProperties.uiElements.solidBorderBox({ - BackgroundColor: appProperties.solidBorderBox_BackgroundColor, - BorderColor: appProperties.solidBorderBox_BackgroundColor, - Color: appProperties.solidBorderBox_TextColor - }) - }, - - /* :::::::::::::: + analysisTab: { + button_Run_AnalysisTab: appProperties.uiElements.button({ + BackgroundColor: appProperties.primaryButton_BackgroundColor, + BorderColor: appProperties.primaryButton_BorderColor, + Color: appProperties.primaryButton_TextColor, + BorderWidth: appProperties.primaryButton_BorderWidth, + BorderRadius: cssProperties.borders.primary_borderRadius, + BorderStyle: 'solid' + }), + + box_AnalysisContainer: appProperties.uiElements.solidBorderBox({ + BackgroundColor: appProperties.solidBorderBox_BackgroundColor, + BorderColor: appProperties.solidBorderBox_BackgroundColor, + Color: appProperties.solidBorderBox_TextColor + }) + }, + + /* :::::::::::::: SUPPORT TAB ::::::::::::::: */ - supportTab: { - box_IframeContainer: appProperties.uiElements.solidBorderBox({ - BackgroundColor: appProperties.solidBorderBox_BackgroundColor, - BorderColor: appProperties.solidBorderBox_BackgroundColor, - Color: appProperties.solidBorderBox_TextColor - }), - - box_SupportInfo: appProperties.uiElements.dottedBorderBox({ - BackgroundColor: appProperties.solidBorderBox_BackgroundColor, - BorderColor: appProperties.solidBorderBox_BorderColor, - Color: appProperties.solidBorderBox_TextColor - }) - - } - - } - } + supportTab: { + box_IframeContainer: appProperties.uiElements.solidBorderBox({ + BackgroundColor: appProperties.solidBorderBox_BackgroundColor, + BorderColor: appProperties.solidBorderBox_BackgroundColor, + Color: appProperties.solidBorderBox_TextColor + }), + + box_SupportInfo: appProperties.uiElements.dottedBorderBox({ + BackgroundColor: appProperties.solidBorderBox_BackgroundColor, + BorderColor: appProperties.solidBorderBox_BorderColor, + Color: appProperties.solidBorderBox_TextColor + }) + + } - return { - colors: cssProperties.colors, - appProperties: appProperties, - borders: cssProperties.borders, - leftPanel: remixProperties.leftPanel, - editor: remixProperties.editor, - terminal: remixProperties.terminal, - rightPanel: remixProperties.rightPanel, - remix: remixProperties.remix } + } + + return { + colors: cssProperties.colors, + appProperties: appProperties, + borders: cssProperties.borders, + leftPanel: remixProperties.leftPanel, + editor: remixProperties.editor, + terminal: remixProperties.terminal, + rightPanel: remixProperties.rightPanel, + remix: remixProperties.remix + } } diff --git a/apps/remix-ide/src/blockchain/blockchain.tsx b/apps/remix-ide/src/blockchain/blockchain.tsx index 61aeeb74df..10b13af4cc 100644 --- a/apps/remix-ide/src/blockchain/blockchain.tsx +++ b/apps/remix-ide/src/blockchain/blockchain.tsx @@ -21,11 +21,11 @@ import * as packageJson from '../../../../package.json' const _paq = window._paq = window._paq || [] //eslint-disable-line const profile = { - name: 'blockchain', - displayName: 'Blockchain', - description: 'Blockchain - Logic', - methods: ['getCode', 'getTransactionReceipt', 'addProvider', 'removeProvider', 'getCurrentFork', 'getAccounts', 'web3VM', 'getProvider'], - version: packageJson.version + name: 'blockchain', + displayName: 'Blockchain', + description: 'Blockchain - Logic', + methods: ['getCode', 'getTransactionReceipt', 'addProvider', 'removeProvider', 'getCurrentFork', 'getAccounts', 'web3VM', 'getProvider'], + version: packageJson.version } export type TransactionContextAPI = { @@ -46,810 +46,810 @@ export type Transaction = { } export class Blockchain extends Plugin { - active: boolean - event: EventManager - events: EventEmitter - executionContext: ExecutionContext - config: Config - txRunner: any // TxRunner - networkcallid: number - networkStatus: { + active: boolean + event: EventManager + events: EventEmitter + executionContext: ExecutionContext + config: Config + txRunner: any // TxRunner + networkcallid: number + networkStatus: { network: { name: string, id: string } error?: string } - providers: { [key: string]: VMProvider | InjectedProvider | NodeProvider } - transactionContextAPI: TransactionContextAPI - - // NOTE: the config object will need to be refactored out in remix-lib - constructor (config: Config) { - super(profile) - this.active = false - this.event = new EventManager() - this.executionContext = new ExecutionContext() - - this.events = new EventEmitter() - this.config = config - const web3Runner = new TxRunnerWeb3({ - config: this.config, - detectNetwork: (cb) => { - this.executionContext.detectNetwork(cb) - }, - isVM: () => { return this.executionContext.isVM() }, - personalMode: () => { - return this.getProvider() === 'web3' ? this.config.get('settings/personal-mode') : false - } - }, _ => this.executionContext.web3(), _ => this.executionContext.currentblockGasLimit()) - this.txRunner = new TxRunner(web3Runner, {}) - - this.networkcallid = 0 - this.networkStatus = { network: { name: ' - ', id: ' - ' } } - this.setupEvents() - this.setupProviders() - } - - _triggerEvent (name, args) { - if (!this.active) return - this.event.trigger(name, args) - this.emit(name, ...args) - } - - onActivation () { - this.active = true - this.on('injected', 'chainChanged', () => { - this.detectNetwork((error, network) => { - this.networkStatus = { network, error } - this._triggerEvent('networkStatus', [this.networkStatus]) - }) - }) - - this.on('injected-trustwallet', 'chainChanged', () => { - this.detectNetwork((error, network) => { - this.networkStatus = { network, error } - this._triggerEvent('networkStatus', [this.networkStatus]) - }) - }) - - this.on('walletconnect', 'chainChanged', () => { - this.detectNetwork((error, network) => { - this.networkStatus = { network, error } - this._triggerEvent('networkStatus', [this.networkStatus]) - }) - }) - } - - onDeactivation () { - this.active = false - this.off('injected', 'chainChanged') - this.off('injected-trustwallet', 'chainChanged') - this.off('walletconnect', 'chainChanged') - this.off('walletconnect', 'accountsChanged') - } - - setupEvents () { - this.executionContext.event.register('contextChanged', async (context) => { - await this.resetEnvironment() - this._triggerEvent('contextChanged', [context]) - this.detectNetwork((error, network) => { - this.networkStatus = { network, error } - this._triggerEvent('networkStatus', [this.networkStatus]) - }) - }) - - this.executionContext.event.register('addProvider', (network) => { - this._triggerEvent('addProvider', [network]) - }) - - this.executionContext.event.register('removeProvider', (name) => { - this._triggerEvent('removeProvider', [name]) - }) - - setInterval(() => { - this.detectNetwork((error, network) => { - this.networkStatus = { network, error } - this._triggerEvent('networkStatus', [this.networkStatus]) - }) - }, 30000) - } - - getCurrentNetworkStatus () { - return this.networkStatus - } - - setupProviders () { - const vmProvider = new VMProvider(this.executionContext) - this.providers = {} - this.providers['vm'] = vmProvider - this.providers.injected = new InjectedProvider(this.executionContext) - this.providers.web3 = new NodeProvider(this.executionContext, this.config) - } - - getCurrentProvider () { - const provider = this.getProvider() - if (provider && provider.startsWith('vm')) return this.providers['vm'] - if (this.providers[provider]) return this.providers[provider] - return this.providers.web3 // default to the common type of provider - } - - /** Return the list of accounts */ - // note: the dual promise/callback is kept for now as it was before - getAccounts (cb) { - return new Promise((resolve, reject) => { - this.getCurrentProvider().getAccounts((error, accounts) => { - if (cb) { - return cb(error, accounts) - } - if (error) { - reject(error) - } - resolve(accounts) - }) - }) - } - - deployContractAndLibraries (selectedContract, args, contractMetadata, compilerContracts, callbacks, confirmationCb) { - const { continueCb, promptCb, statusCb, finalCb } = callbacks - const constructor = selectedContract.getConstructorInterface() - txFormat.buildData(selectedContract.name, selectedContract.object, compilerContracts, true, constructor, args, (error, data) => { - if (error) { - return statusCb(`creation of ${selectedContract.name} errored: ${error.message ? error.message : error}`) - } - - statusCb(`creation of ${selectedContract.name} pending...`) - this.createContract(selectedContract, data, continueCb, promptCb, confirmationCb, finalCb) - }, statusCb, (data, runTxCallback) => { - // called for libraries deployment - this.runTx(data, confirmationCb, continueCb, promptCb, runTxCallback) - }) - } - - deployContractWithLibrary (selectedContract, args, contractMetadata, compilerContracts, callbacks, confirmationCb) { - const { continueCb, promptCb, statusCb, finalCb } = callbacks - const constructor = selectedContract.getConstructorInterface() - txFormat.encodeConstructorCallAndLinkLibraries(selectedContract.object, args, constructor, contractMetadata.linkReferences, selectedContract.bytecodeLinkReferences, (error, data) => { - if (error) { - return statusCb(`creation of ${selectedContract.name} errored: ${error.message ? error.message : error}`) - } - - statusCb(`creation of ${selectedContract.name} pending...`) - this.createContract(selectedContract, data, continueCb, promptCb, confirmationCb, finalCb) - }) - } - - async deployProxy (proxyData, implementationContractObject) { - const proxyModal = { - id: 'confirmProxyDeployment', - title: 'Confirm Deploy Proxy (ERC1967)', - message: `Confirm you want to deploy an ERC1967 proxy contract that is connected to your implementation. - For more info on ERC1967, see: https://docs.openzeppelin.com/contracts/4.x/api/proxy#ERC1967Proxy`, - modalType: 'modal', - okLabel: 'OK', - cancelLabel: 'Cancel', - okFn: () => { - this.runProxyTx(proxyData, implementationContractObject) - _paq.push(['trackEvent', 'blockchain', 'Deploy With Proxy', 'modal ok confirmation']) - }, - cancelFn: () => { - this.call('notification', 'toast', cancelProxyMsg()) - _paq.push(['trackEvent', 'blockchain', 'Deploy With Proxy', 'cancel proxy deployment']) - }, - hideFn: () => null + providers: { [key: string]: VMProvider | InjectedProvider | NodeProvider } + transactionContextAPI: TransactionContextAPI + + // NOTE: the config object will need to be refactored out in remix-lib + constructor (config: Config) { + super(profile) + this.active = false + this.event = new EventManager() + this.executionContext = new ExecutionContext() + + this.events = new EventEmitter() + this.config = config + const web3Runner = new TxRunnerWeb3({ + config: this.config, + detectNetwork: (cb) => { + this.executionContext.detectNetwork(cb) + }, + isVM: () => { return this.executionContext.isVM() }, + personalMode: () => { + return this.getProvider() === 'web3' ? this.config.get('settings/personal-mode') : false + } + }, _ => this.executionContext.web3(), _ => this.executionContext.currentblockGasLimit()) + this.txRunner = new TxRunner(web3Runner, {}) + + this.networkcallid = 0 + this.networkStatus = { network: { name: ' - ', id: ' - ' } } + this.setupEvents() + this.setupProviders() + } + + _triggerEvent (name, args) { + if (!this.active) return + this.event.trigger(name, args) + this.emit(name, ...args) + } + + onActivation () { + this.active = true + this.on('injected', 'chainChanged', () => { + this.detectNetwork((error, network) => { + this.networkStatus = { network, error } + this._triggerEvent('networkStatus', [this.networkStatus]) + }) + }) + + this.on('injected-trustwallet', 'chainChanged', () => { + this.detectNetwork((error, network) => { + this.networkStatus = { network, error } + this._triggerEvent('networkStatus', [this.networkStatus]) + }) + }) + + this.on('walletconnect', 'chainChanged', () => { + this.detectNetwork((error, network) => { + this.networkStatus = { network, error } + this._triggerEvent('networkStatus', [this.networkStatus]) + }) + }) + } + + onDeactivation () { + this.active = false + this.off('injected', 'chainChanged') + this.off('injected-trustwallet', 'chainChanged') + this.off('walletconnect', 'chainChanged') + this.off('walletconnect', 'accountsChanged') + } + + setupEvents () { + this.executionContext.event.register('contextChanged', async (context) => { + await this.resetEnvironment() + this._triggerEvent('contextChanged', [context]) + this.detectNetwork((error, network) => { + this.networkStatus = { network, error } + this._triggerEvent('networkStatus', [this.networkStatus]) + }) + }) + + this.executionContext.event.register('addProvider', (network) => { + this._triggerEvent('addProvider', [network]) + }) + + this.executionContext.event.register('removeProvider', (name) => { + this._triggerEvent('removeProvider', [name]) + }) + + setInterval(() => { + this.detectNetwork((error, network) => { + this.networkStatus = { network, error } + this._triggerEvent('networkStatus', [this.networkStatus]) + }) + }, 30000) + } + + getCurrentNetworkStatus () { + return this.networkStatus + } + + setupProviders () { + const vmProvider = new VMProvider(this.executionContext) + this.providers = {} + this.providers['vm'] = vmProvider + this.providers.injected = new InjectedProvider(this.executionContext) + this.providers.web3 = new NodeProvider(this.executionContext, this.config) + } + + getCurrentProvider () { + const provider = this.getProvider() + if (provider && provider.startsWith('vm')) return this.providers['vm'] + if (this.providers[provider]) return this.providers[provider] + return this.providers.web3 // default to the common type of provider + } + + /** Return the list of accounts */ + // note: the dual promise/callback is kept for now as it was before + getAccounts (cb) { + return new Promise((resolve, reject) => { + this.getCurrentProvider().getAccounts((error, accounts) => { + if (cb) { + return cb(error, accounts) } - this.call('notification', 'modal', proxyModal) - } - - async runProxyTx (proxyData, implementationContractObject) { - const args = { useCall: false, data: proxyData } - let networkInfo - const confirmationCb = (network, tx, gasEstimation, continueTxExecution, cancelCb) => { - networkInfo = network - // continue using original authorization given by user - continueTxExecution(null) + if (error) { + reject(error) } - const continueCb = (error, continueTxExecution, cancelCb) => { continueTxExecution() } - const promptCb = (okCb, cancelCb) => { okCb() } - const finalCb = async (error, txResult, address, returnValue) => { - if (error) { - const log = logBuilder(error) + resolve(accounts) + }) + }) + } + + deployContractAndLibraries (selectedContract, args, contractMetadata, compilerContracts, callbacks, confirmationCb) { + const { continueCb, promptCb, statusCb, finalCb } = callbacks + const constructor = selectedContract.getConstructorInterface() + txFormat.buildData(selectedContract.name, selectedContract.object, compilerContracts, true, constructor, args, (error, data) => { + if (error) { + return statusCb(`creation of ${selectedContract.name} errored: ${error.message ? error.message : error}`) + } + + statusCb(`creation of ${selectedContract.name} pending...`) + this.createContract(selectedContract, data, continueCb, promptCb, confirmationCb, finalCb) + }, statusCb, (data, runTxCallback) => { + // called for libraries deployment + this.runTx(data, confirmationCb, continueCb, promptCb, runTxCallback) + }) + } + + deployContractWithLibrary (selectedContract, args, contractMetadata, compilerContracts, callbacks, confirmationCb) { + const { continueCb, promptCb, statusCb, finalCb } = callbacks + const constructor = selectedContract.getConstructorInterface() + txFormat.encodeConstructorCallAndLinkLibraries(selectedContract.object, args, constructor, contractMetadata.linkReferences, selectedContract.bytecodeLinkReferences, (error, data) => { + if (error) { + return statusCb(`creation of ${selectedContract.name} errored: ${error.message ? error.message : error}`) + } + + statusCb(`creation of ${selectedContract.name} pending...`) + this.createContract(selectedContract, data, continueCb, promptCb, confirmationCb, finalCb) + }) + } + + async deployProxy (proxyData, implementationContractObject) { + const proxyModal = { + id: 'confirmProxyDeployment', + title: 'Confirm Deploy Proxy (ERC1967)', + message: `Confirm you want to deploy an ERC1967 proxy contract that is connected to your implementation. + For more info on ERC1967, see: https://docs.openzeppelin.com/contracts/4.x/api/proxy#ERC1967Proxy`, + modalType: 'modal', + okLabel: 'OK', + cancelLabel: 'Cancel', + okFn: () => { + this.runProxyTx(proxyData, implementationContractObject) + _paq.push(['trackEvent', 'blockchain', 'Deploy With Proxy', 'modal ok confirmation']) + }, + cancelFn: () => { + this.call('notification', 'toast', cancelProxyMsg()) + _paq.push(['trackEvent', 'blockchain', 'Deploy With Proxy', 'cancel proxy deployment']) + }, + hideFn: () => null + } + this.call('notification', 'modal', proxyModal) + } + + async runProxyTx (proxyData, implementationContractObject) { + const args = { useCall: false, data: proxyData } + let networkInfo + const confirmationCb = (network, tx, gasEstimation, continueTxExecution, cancelCb) => { + networkInfo = network + // continue using original authorization given by user + continueTxExecution(null) + } + const continueCb = (error, continueTxExecution, cancelCb) => { continueTxExecution() } + const promptCb = (okCb, cancelCb) => { okCb() } + const finalCb = async (error, txResult, address, returnValue) => { + if (error) { + const log = logBuilder(error) - _paq.push(['trackEvent', 'blockchain', 'Deploy With Proxy', 'Proxy deployment failed: ' + error]) - return this.call('terminal', 'logHtml', log) - } - await this.saveDeployedContractStorageLayout(implementationContractObject, address, networkInfo) - this.events.emit('newProxyDeployment', address, new Date().toISOString(), implementationContractObject.contractName) - _paq.push(['trackEvent', 'blockchain', 'Deploy With Proxy', 'Proxy deployment successful']) - this.call('udapp', 'addInstance', addressToString(address), implementationContractObject.abi, implementationContractObject.name) + _paq.push(['trackEvent', 'blockchain', 'Deploy With Proxy', 'Proxy deployment failed: ' + error]) + return this.call('terminal', 'logHtml', log) + } + await this.saveDeployedContractStorageLayout(implementationContractObject, address, networkInfo) + this.events.emit('newProxyDeployment', address, new Date().toISOString(), implementationContractObject.contractName) + _paq.push(['trackEvent', 'blockchain', 'Deploy With Proxy', 'Proxy deployment successful']) + this.call('udapp', 'addInstance', addressToString(address), implementationContractObject.abi, implementationContractObject.name) + } + + this.runTx(args, confirmationCb, continueCb, promptCb, finalCb) + } + + async upgradeProxy(proxyAddress, newImplAddress, data, newImplementationContractObject) { + const upgradeModal = { + id: 'confirmProxyDeployment', + title: 'Confirm Update Proxy (ERC1967)', + message: `Confirm you want to update your proxy contract with the new implementation contract's address: ${newImplAddress}.`, + modalType: 'modal', + okLabel: 'OK', + cancelLabel: 'Cancel', + okFn: () => { + this.runUpgradeTx(proxyAddress, data, newImplementationContractObject) + _paq.push(['trackEvent', 'blockchain', 'Upgrade With Proxy', 'proxy upgrade confirmation click']) + }, + cancelFn: () => { + this.call('notification', 'toast', cancelUpgradeMsg()) + _paq.push(['trackEvent', 'blockchain', 'Upgrade With Proxy', 'proxy upgrade cancel click']) + }, + hideFn: () => null + } + this.call('notification', 'modal', upgradeModal) + } + + async runUpgradeTx (proxyAddress, data, newImplementationContractObject) { + const args = { useCall: false, data, to: proxyAddress } + let networkInfo + const confirmationCb = (network, tx, gasEstimation, continueTxExecution, cancelCb) => { + // continue using original authorization given by user + networkInfo = network + continueTxExecution(null) + } + const continueCb = (error, continueTxExecution, cancelCb) => { continueTxExecution() } + const promptCb = (okCb, cancelCb) => { okCb() } + const finalCb = async (error, txResult, address, returnValue) => { + if (error) { + const log = logBuilder(error) + + _paq.push(['trackEvent', 'blockchain', 'Upgrade With Proxy', 'Upgrade failed']) + return this.call('terminal', 'logHtml', log) + } + await this.saveDeployedContractStorageLayout(newImplementationContractObject, proxyAddress, networkInfo) + _paq.push(['trackEvent', 'blockchain', 'Upgrade With Proxy', 'Upgrade Successful']) + this.call('udapp', 'addInstance', addressToString(proxyAddress), newImplementationContractObject.abi, newImplementationContractObject.name) + } + this.runTx(args, confirmationCb, continueCb, promptCb, finalCb) + } + + async saveDeployedContractStorageLayout (contractObject, proxyAddress, networkInfo) { + const { contractName, implementationAddress } = contractObject + const networkName = networkInfo.name === 'custom' ? networkInfo.name + '-' + networkInfo.id : networkInfo.name + const hasPreviousDeploys = await this.call('fileManager', 'exists', `.deploys/upgradeable-contracts/${networkName}/UUPS.json`) + // TODO: make deploys folder read only. + if (hasPreviousDeploys) { + const deployments = await this.call('fileManager', 'readFile', `.deploys/upgradeable-contracts/${networkName}/UUPS.json`) + const parsedDeployments = JSON.parse(deployments) + const proxyDeployment = parsedDeployments.deployments[proxyAddress] + + if (proxyDeployment) { + const oldImplementationAddress = proxyDeployment.implementationAddress + const hasPreviousBuild = await this.call('fileManager', 'exists', `.deploys/upgradeable-contracts/${networkName}/solc-${oldImplementationAddress}.json`) + + if (hasPreviousBuild) await this.call('fileManager', 'remove', `.deploys/upgradeable-contracts/${networkName}/solc-${oldImplementationAddress}.json`) + } + parsedDeployments.deployments[proxyAddress] = { + date: new Date().toISOString(), + contractName: contractName, + fork: networkInfo.currentFork, + implementationAddress: implementationAddress, + solcOutput: contractObject.compiler.data, + solcInput: contractObject.compiler.source + } + await this.call('fileManager', 'writeFile', `.deploys/upgradeable-contracts/${networkName}/solc-${implementationAddress}.json`, JSON.stringify({ + solcInput: contractObject.compiler.source, + solcOutput: contractObject.compiler.data + }, null, 2)) + await this.call('fileManager', 'writeFile', `.deploys/upgradeable-contracts/${networkName}/UUPS.json`, JSON.stringify(parsedDeployments, null, 2)) + } else { + await this.call('fileManager', 'writeFile', `.deploys/upgradeable-contracts/${networkName}/solc-${implementationAddress}.json`, JSON.stringify({ + solcInput: contractObject.compiler.source, + solcOutput: contractObject.compiler.data + }, null, 2)) + await this.call('fileManager', 'writeFile', `.deploys/upgradeable-contracts/${networkName}/UUPS.json`, JSON.stringify({ + id: networkInfo.id, + network: networkInfo.name, + deployments: { + [proxyAddress]: { + date: new Date().toISOString(), + contractName: contractName, + fork: networkInfo.currentFork, + implementationAddress: implementationAddress + } } - - this.runTx(args, confirmationCb, continueCb, promptCb, finalCb) - } - - async upgradeProxy(proxyAddress, newImplAddress, data, newImplementationContractObject) { - const upgradeModal = { - id: 'confirmProxyDeployment', - title: 'Confirm Update Proxy (ERC1967)', - message: `Confirm you want to update your proxy contract with the new implementation contract's address: ${newImplAddress}.`, - modalType: 'modal', - okLabel: 'OK', - cancelLabel: 'Cancel', - okFn: () => { - this.runUpgradeTx(proxyAddress, data, newImplementationContractObject) - _paq.push(['trackEvent', 'blockchain', 'Upgrade With Proxy', 'proxy upgrade confirmation click']) - }, - cancelFn: () => { - this.call('notification', 'toast', cancelUpgradeMsg()) - _paq.push(['trackEvent', 'blockchain', 'Upgrade With Proxy', 'proxy upgrade cancel click']) - }, - hideFn: () => null - } - this.call('notification', 'modal', upgradeModal) - } - - async runUpgradeTx (proxyAddress, data, newImplementationContractObject) { - const args = { useCall: false, data, to: proxyAddress } - let networkInfo - const confirmationCb = (network, tx, gasEstimation, continueTxExecution, cancelCb) => { - // continue using original authorization given by user - networkInfo = network - continueTxExecution(null) - } - const continueCb = (error, continueTxExecution, cancelCb) => { continueTxExecution() } - const promptCb = (okCb, cancelCb) => { okCb() } - const finalCb = async (error, txResult, address, returnValue) => { - if (error) { - const log = logBuilder(error) - - _paq.push(['trackEvent', 'blockchain', 'Upgrade With Proxy', 'Upgrade failed']) - return this.call('terminal', 'logHtml', log) - } - await this.saveDeployedContractStorageLayout(newImplementationContractObject, proxyAddress, networkInfo) - _paq.push(['trackEvent', 'blockchain', 'Upgrade With Proxy', 'Upgrade Successful']) - this.call('udapp', 'addInstance', addressToString(proxyAddress), newImplementationContractObject.abi, newImplementationContractObject.name) - } - this.runTx(args, confirmationCb, continueCb, promptCb, finalCb) - } - - async saveDeployedContractStorageLayout (contractObject, proxyAddress, networkInfo) { - const { contractName, implementationAddress } = contractObject - const networkName = networkInfo.name === 'custom' ? networkInfo.name + '-' + networkInfo.id : networkInfo.name - const hasPreviousDeploys = await this.call('fileManager', 'exists', `.deploys/upgradeable-contracts/${networkName}/UUPS.json`) - // TODO: make deploys folder read only. - if (hasPreviousDeploys) { - const deployments = await this.call('fileManager', 'readFile', `.deploys/upgradeable-contracts/${networkName}/UUPS.json`) - const parsedDeployments = JSON.parse(deployments) - const proxyDeployment = parsedDeployments.deployments[proxyAddress] - - if (proxyDeployment) { - const oldImplementationAddress = proxyDeployment.implementationAddress - const hasPreviousBuild = await this.call('fileManager', 'exists', `.deploys/upgradeable-contracts/${networkName}/solc-${oldImplementationAddress}.json`) - - if (hasPreviousBuild) await this.call('fileManager', 'remove', `.deploys/upgradeable-contracts/${networkName}/solc-${oldImplementationAddress}.json`) - } - parsedDeployments.deployments[proxyAddress] = { - date: new Date().toISOString(), - contractName: contractName, - fork: networkInfo.currentFork, - implementationAddress: implementationAddress, - solcOutput: contractObject.compiler.data, - solcInput: contractObject.compiler.source - } - await this.call('fileManager', 'writeFile', `.deploys/upgradeable-contracts/${networkName}/solc-${implementationAddress}.json`, JSON.stringify({ - solcInput: contractObject.compiler.source, - solcOutput: contractObject.compiler.data - }, null, 2)) - await this.call('fileManager', 'writeFile', `.deploys/upgradeable-contracts/${networkName}/UUPS.json`, JSON.stringify(parsedDeployments, null, 2)) - } else { - await this.call('fileManager', 'writeFile', `.deploys/upgradeable-contracts/${networkName}/solc-${implementationAddress}.json`, JSON.stringify({ - solcInput: contractObject.compiler.source, - solcOutput: contractObject.compiler.data - }, null, 2)) - await this.call('fileManager', 'writeFile', `.deploys/upgradeable-contracts/${networkName}/UUPS.json`, JSON.stringify({ - id: networkInfo.id, - network: networkInfo.name, - deployments: { - [proxyAddress]: { - date: new Date().toISOString(), - contractName: contractName, - fork: networkInfo.currentFork, - implementationAddress: implementationAddress - } - } - }, null, 2)) - } - } - - async getEncodedFunctionHex (args, funABI) { - return new Promise((resolve, reject) => { - txFormat.encodeFunctionCall(args, funABI, (error, data) => { - if (error) return reject(error) - resolve(data.dataHex) - }) - }) - } - - async getEncodedParams (args, funABI) { - return new Promise((resolve, reject) => { - txFormat.encodeParams(args, funABI, (error, encodedParams) => { - if (error) return reject(error) - return resolve(encodedParams.dataHex) - }) - }) - } - - createContract (selectedContract, data, continueCb, promptCb, confirmationCb, finalCb) { - if (data) { - data.contractName = selectedContract.name - data.linkReferences = selectedContract.bytecodeLinkReferences - data.contractABI = selectedContract.abi - } - - this.runTx({ data: data, useCall: false }, confirmationCb, continueCb, promptCb, - (error, txResult, address) => { - if (error) { - return finalCb(`creation of ${selectedContract.name} errored: ${error.message ? error.message : error}`) - } - if (txResult.receipt.status === false || txResult.receipt.status === '0x0' || txResult.receipt.status === 0) { - return finalCb(`creation of ${selectedContract.name} errored: transaction execution failed`) - } - finalCb(null, selectedContract, address) - } - ) - } - - determineGasPrice (cb) { - this.getCurrentProvider().getGasPrice((error, gasPrice) => { - const warnMessage = ' Please fix this issue before sending any transaction. ' - if (error) { - return cb('Unable to retrieve the current network gas price.' + warnMessage + error) - } - try { - const gasPriceValue = this.fromWei(gasPrice, false, 'gwei') - cb(null, gasPriceValue) - } catch (e) { - cb(warnMessage + e.message, null, false) - } - }) - } - - getInputs (funABI) { - if (!funABI.inputs) { - return '' + }, null, 2)) + } + } + + async getEncodedFunctionHex (args, funABI) { + return new Promise((resolve, reject) => { + txFormat.encodeFunctionCall(args, funABI, (error, data) => { + if (error) return reject(error) + resolve(data.dataHex) + }) + }) + } + + async getEncodedParams (args, funABI) { + return new Promise((resolve, reject) => { + txFormat.encodeParams(args, funABI, (error, encodedParams) => { + if (error) return reject(error) + return resolve(encodedParams.dataHex) + }) + }) + } + + createContract (selectedContract, data, continueCb, promptCb, confirmationCb, finalCb) { + if (data) { + data.contractName = selectedContract.name + data.linkReferences = selectedContract.bytecodeLinkReferences + data.contractABI = selectedContract.abi + } + + this.runTx({ data: data, useCall: false }, confirmationCb, continueCb, promptCb, + (error, txResult, address) => { + if (error) { + return finalCb(`creation of ${selectedContract.name} errored: ${error.message ? error.message : error}`) } - return txHelper.inputParametersDeclarationToString(funABI.inputs) - } - - fromWei (value, doTypeConversion, unit) { - if (doTypeConversion) { - return Web3.utils.fromWei(typeConversion.toInt(value), unit || 'ether') + if (txResult.receipt.status === false || txResult.receipt.status === '0x0' || txResult.receipt.status === 0) { + return finalCb(`creation of ${selectedContract.name} errored: transaction execution failed`) } - return Web3.utils.fromWei(value.toString(10), unit || 'ether') - } - - toWei (value, unit) { - return Web3.utils.toWei(value, unit || 'gwei') - } - - calculateFee (gas, gasPrice, unit?) { - return Web3.utils.toBN(gas).mul(Web3.utils.toBN(Web3.utils.toWei(gasPrice.toString(10) as string, unit || 'gwei'))) - } - - determineGasFees (tx) { - const determineGasFeesCb = (gasPrice, cb) => { - let txFeeText, priceStatus - // TODO: this try catch feels like an anti pattern, can/should be - // removed, but for now keeping the original logic - try { - const fee = this.calculateFee(tx.gas, gasPrice) - txFeeText = ' ' + this.fromWei(fee, false, 'ether') + ' Ether' - priceStatus = true - } catch (e) { - txFeeText = ' Please fix this issue before sending any transaction. ' + e.message - priceStatus = false - } - cb(txFeeText, priceStatus) - } - - return determineGasFeesCb - } - - changeExecutionContext (context, confirmCb, infoCb, cb) { - return this.executionContext.executionContextChange(context, null, confirmCb, infoCb, cb) - } - - detectNetwork (cb) { - return this.executionContext.detectNetwork(cb) - } - - getProvider () { - return this.executionContext.getProvider() - } - - getInjectedWeb3Address () { - return this.executionContext.getSelectedAddress() - } - - /** + finalCb(null, selectedContract, address) + } + ) + } + + determineGasPrice (cb) { + this.getCurrentProvider().getGasPrice((error, gasPrice) => { + const warnMessage = ' Please fix this issue before sending any transaction. ' + if (error) { + return cb('Unable to retrieve the current network gas price.' + warnMessage + error) + } + try { + const gasPriceValue = this.fromWei(gasPrice, false, 'gwei') + cb(null, gasPriceValue) + } catch (e) { + cb(warnMessage + e.message, null, false) + } + }) + } + + getInputs (funABI) { + if (!funABI.inputs) { + return '' + } + return txHelper.inputParametersDeclarationToString(funABI.inputs) + } + + fromWei (value, doTypeConversion, unit) { + if (doTypeConversion) { + return Web3.utils.fromWei(typeConversion.toInt(value), unit || 'ether') + } + return Web3.utils.fromWei(value.toString(10), unit || 'ether') + } + + toWei (value, unit) { + return Web3.utils.toWei(value, unit || 'gwei') + } + + calculateFee (gas, gasPrice, unit?) { + return Web3.utils.toBN(gas).mul(Web3.utils.toBN(Web3.utils.toWei(gasPrice.toString(10) as string, unit || 'gwei'))) + } + + determineGasFees (tx) { + const determineGasFeesCb = (gasPrice, cb) => { + let txFeeText, priceStatus + // TODO: this try catch feels like an anti pattern, can/should be + // removed, but for now keeping the original logic + try { + const fee = this.calculateFee(tx.gas, gasPrice) + txFeeText = ' ' + this.fromWei(fee, false, 'ether') + ' Ether' + priceStatus = true + } catch (e) { + txFeeText = ' Please fix this issue before sending any transaction. ' + e.message + priceStatus = false + } + cb(txFeeText, priceStatus) + } + + return determineGasFeesCb + } + + changeExecutionContext (context, confirmCb, infoCb, cb) { + return this.executionContext.executionContextChange(context, null, confirmCb, infoCb, cb) + } + + detectNetwork (cb) { + return this.executionContext.detectNetwork(cb) + } + + getProvider () { + return this.executionContext.getProvider() + } + + getInjectedWeb3Address () { + return this.executionContext.getSelectedAddress() + } + + /** * return the fork name applied to the current envionment * @return {String} - fork name */ - getCurrentFork () { - return this.executionContext.getCurrentFork() - } - - isWeb3Provider () { - const isVM = this.executionContext.isVM() - const isInjected = this.getProvider() === 'injected' - return (!isVM && !isInjected) - } - - isInjectedWeb3 () { - return this.getProvider() === 'injected' - } - - signMessage (message, account, passphrase, cb) { - this.getCurrentProvider().signMessage(message, account, passphrase, cb) - } - - web3VM () { - return (this.providers.vm as VMProvider).web3 - } - - web3 () { - // @todo(https://github.com/ethereum/remix-project/issues/431) - const isVM = this.executionContext.isVM() - if (isVM) { - return (this.providers.vm as VMProvider).web3 + getCurrentFork () { + return this.executionContext.getCurrentFork() + } + + isWeb3Provider () { + const isVM = this.executionContext.isVM() + const isInjected = this.getProvider() === 'injected' + return (!isVM && !isInjected) + } + + isInjectedWeb3 () { + return this.getProvider() === 'injected' + } + + signMessage (message, account, passphrase, cb) { + this.getCurrentProvider().signMessage(message, account, passphrase, cb) + } + + web3VM () { + return (this.providers.vm as VMProvider).web3 + } + + web3 () { + // @todo(https://github.com/ethereum/remix-project/issues/431) + const isVM = this.executionContext.isVM() + if (isVM) { + return (this.providers.vm as VMProvider).web3 + } + return this.executionContext.web3() + } + + getTxListener (opts) { + opts.event = { + // udapp: this.udapp.event + udapp: this.event + } + const txlistener = new Txlistener(opts, this.executionContext) + return txlistener + } + + runOrCallContractMethod (contractName, contractAbi, funABI, contract, value, address, callType, lookupOnly, logMsg, logCallback, outputCb, confirmationCb, continueCb, promptCb) { + // contractsDetails is used to resolve libraries + txFormat.buildData(contractName, contractAbi, {}, false, funABI, callType, (error, data) => { + if (error) { + return logCallback(`${logMsg} errored: ${error.message ? error.message : error}`) + } + if (!lookupOnly) { + logCallback(`${logMsg} pending ... `) + } else { + logCallback(`${logMsg}`) + } + if (funABI.type === 'fallback') data.dataHex = value + + if (data) { + data.contractName = contractName + data.contractABI = contractAbi + data.contract = contract + } + const useCall = funABI.stateMutability === 'view' || funABI.stateMutability === 'pure' + this.runTx({ to: address, data, useCall }, confirmationCb, continueCb, promptCb, (error, txResult, _address, returnValue) => { + if (error) { + return logCallback(`${logMsg} errored: ${error.message ? error.message : error}`) } - return this.executionContext.web3() - } - - getTxListener (opts) { - opts.event = { - // udapp: this.udapp.event - udapp: this.event + if (lookupOnly) { + outputCb(returnValue) } - const txlistener = new Txlistener(opts, this.executionContext) - return txlistener - } - - runOrCallContractMethod (contractName, contractAbi, funABI, contract, value, address, callType, lookupOnly, logMsg, logCallback, outputCb, confirmationCb, continueCb, promptCb) { - // contractsDetails is used to resolve libraries - txFormat.buildData(contractName, contractAbi, {}, false, funABI, callType, (error, data) => { - if (error) { - return logCallback(`${logMsg} errored: ${error.message ? error.message : error}`) - } - if (!lookupOnly) { - logCallback(`${logMsg} pending ... `) - } else { - logCallback(`${logMsg}`) - } - if (funABI.type === 'fallback') data.dataHex = value - - if (data) { - data.contractName = contractName - data.contractABI = contractAbi - data.contract = contract - } - const useCall = funABI.stateMutability === 'view' || funABI.stateMutability === 'pure' - this.runTx({ to: address, data, useCall }, confirmationCb, continueCb, promptCb, (error, txResult, _address, returnValue) => { - if (error) { - return logCallback(`${logMsg} errored: ${error.message ? error.message : error}`) - } - if (lookupOnly) { - outputCb(returnValue) - } - }) - }, - (msg) => { - logCallback(msg) - }, - (data, runTxCallback) => { - // called for libraries deployment - this.runTx(data, confirmationCb, runTxCallback, promptCb, () => { /* Do nothing. */ }) - }) - } - - context () { - return (this.executionContext.isVM() ? 'memory' : 'blockchain') - } - - // NOTE: the config is only needed because exectuionContext.init does - async resetAndInit (config: Config, transactionContextAPI: TransactionContextAPI) { - this.transactionContextAPI = transactionContextAPI - this.executionContext.init(config) - this.executionContext.stopListenOnLastBlock() - this.executionContext.listenOnLastBlock() - } - - addProvider (provider) { - this.executionContext.addProvider(provider) - } - - removeProvider (name) { - this.executionContext.removeProvider(name) - } - - // TODO : event should be triggered by Udapp instead of TxListener - /** Listen on New Transaction. (Cannot be done inside constructor because txlistener doesn't exist yet) */ - startListening (txlistener) { - txlistener.event.register('newTransaction', (tx, receipt) => { - this.events.emit('newTransaction', tx, receipt) - }) - } - - async resetEnvironment () { - await this.getCurrentProvider().resetEnvironment() - // TODO: most params here can be refactored away in txRunner - const web3Runner = new TxRunnerWeb3({ - config: this.config, - detectNetwork: (cb) => { - this.executionContext.detectNetwork(cb) - }, - isVM: () => { return this.executionContext.isVM() }, - personalMode: () => { - return this.getProvider() === 'web3' ? this.config.get('settings/personal-mode') : false - } - }, _ => this.executionContext.web3(), _ => this.executionContext.currentblockGasLimit()) + }) + }, + (msg) => { + logCallback(msg) + }, + (data, runTxCallback) => { + // called for libraries deployment + this.runTx(data, confirmationCb, runTxCallback, promptCb, () => { /* Do nothing. */ }) + }) + } + + context () { + return (this.executionContext.isVM() ? 'memory' : 'blockchain') + } + + // NOTE: the config is only needed because exectuionContext.init does + async resetAndInit (config: Config, transactionContextAPI: TransactionContextAPI) { + this.transactionContextAPI = transactionContextAPI + this.executionContext.init(config) + this.executionContext.stopListenOnLastBlock() + this.executionContext.listenOnLastBlock() + } + + addProvider (provider) { + this.executionContext.addProvider(provider) + } + + removeProvider (name) { + this.executionContext.removeProvider(name) + } + + // TODO : event should be triggered by Udapp instead of TxListener + /** Listen on New Transaction. (Cannot be done inside constructor because txlistener doesn't exist yet) */ + startListening (txlistener) { + txlistener.event.register('newTransaction', (tx, receipt) => { + this.events.emit('newTransaction', tx, receipt) + }) + } + + async resetEnvironment () { + await this.getCurrentProvider().resetEnvironment() + // TODO: most params here can be refactored away in txRunner + const web3Runner = new TxRunnerWeb3({ + config: this.config, + detectNetwork: (cb) => { + this.executionContext.detectNetwork(cb) + }, + isVM: () => { return this.executionContext.isVM() }, + personalMode: () => { + return this.getProvider() === 'web3' ? this.config.get('settings/personal-mode') : false + } + }, _ => this.executionContext.web3(), _ => this.executionContext.currentblockGasLimit()) - web3Runner.event.register('transactionBroadcasted', (txhash) => { - this.executionContext.detectNetwork((error, network) => { - if (error || !network) return - if (network.name === 'VM') return - const viewEtherScanLink = etherScanLink(network.name, txhash) - - if (viewEtherScanLink) { - this.call('terminal', 'logHtml', - ( + web3Runner.event.register('transactionBroadcasted', (txhash) => { + this.executionContext.detectNetwork((error, network) => { + if (error || !network) return + if (network.name === 'VM') return + const viewEtherScanLink = etherScanLink(network.name, txhash) + + if (viewEtherScanLink) { + this.call('terminal', 'logHtml', + ( view on etherscan - )) - } - }) - }) - this.txRunner = new TxRunner(web3Runner, {}) - } + )) + } + }) + }) + this.txRunner = new TxRunner(web3Runner, {}) + } - /** + /** * Create a VM Account * @param {{privateKey: string, balance: string}} newAccount The new account to create */ - createVMAccount (newAccount) { - if (!this.executionContext.isVM()) { - throw new Error('plugin API does not allow creating a new account through web3 connection. Only vm mode is allowed') - } - return (this.providers.vm as VMProvider).createVMAccount(newAccount) + createVMAccount (newAccount) { + if (!this.executionContext.isVM()) { + throw new Error('plugin API does not allow creating a new account through web3 connection. Only vm mode is allowed') } + return (this.providers.vm as VMProvider).createVMAccount(newAccount) + } - newAccount (_password, passwordPromptCb, cb) { - return this.getCurrentProvider().newAccount(passwordPromptCb, cb) - } + newAccount (_password, passwordPromptCb, cb) { + return this.getCurrentProvider().newAccount(passwordPromptCb, cb) + } - /** Get the balance of an address, and convert wei to ether */ - getBalanceInEther (address) { - return this.getCurrentProvider().getBalanceInEther(address) - } + /** Get the balance of an address, and convert wei to ether */ + getBalanceInEther (address) { + return this.getCurrentProvider().getBalanceInEther(address) + } - pendingTransactionsCount () { - return Object.keys(this.txRunner.pendingTxs).length - } + pendingTransactionsCount () { + return Object.keys(this.txRunner.pendingTxs).length + } - async getCode(address) { - return await this.web3().eth.getCode(address) - } + async getCode(address) { + return await this.web3().eth.getCode(address) + } - async getTransactionReceipt (hash) { - return await this.web3().eth.getTransactionReceipt(hash) - } + async getTransactionReceipt (hash) { + return await this.web3().eth.getTransactionReceipt(hash) + } - /** + /** * This function send a tx only to Remix VM or testnet, will return an error for the mainnet * SHOULD BE TAKEN CAREFULLY! * * @param {Object} tx - transaction. */ - sendTransaction (tx: Transaction) { - return new Promise((resolve, reject) => { - this.executionContext.detectNetwork((error, network) => { - if (error) return reject(error) - if (network.name === 'Main' && network.id === '1') { - return reject(new Error('It is not allowed to make this action against mainnet')) - } + sendTransaction (tx: Transaction) { + return new Promise((resolve, reject) => { + this.executionContext.detectNetwork((error, network) => { + if (error) return reject(error) + if (network.name === 'Main' && network.id === '1') { + return reject(new Error('It is not allowed to make this action against mainnet')) + } - this.txRunner.rawRun( - tx, - (network, tx, gasEstimation, continueTxExecution, cancelCb) => { continueTxExecution() }, - (error, continueTxExecution, cancelCb) => { if (error) { reject(error) } else { continueTxExecution() } }, - (okCb, cancelCb) => { okCb() }, - async (error, result) => { - if (error) return reject(error) - try { - if (this.executionContext.isVM()) { - const execResult = await this.web3().eth.getExecutionResultFromSimulator(result.transactionHash) - resolve(resultToRemixTx(result, execResult)) - } else - resolve(resultToRemixTx(result)) - } catch (e) { - reject(e) - } - } - ) - }) + this.txRunner.rawRun( + tx, + (network, tx, gasEstimation, continueTxExecution, cancelCb) => { continueTxExecution() }, + (error, continueTxExecution, cancelCb) => { if (error) { reject(error) } else { continueTxExecution() } }, + (okCb, cancelCb) => { okCb() }, + async (error, result) => { + if (error) return reject(error) + try { + if (this.executionContext.isVM()) { + const execResult = await this.web3().eth.getExecutionResultFromSimulator(result.transactionHash) + resolve(resultToRemixTx(result, execResult)) + } else + resolve(resultToRemixTx(result)) + } catch (e) { + reject(e) + } + } + ) + }) + }) + } + + async runTx (args, confirmationCb, continueCb, promptCb, cb) { + const getGasLimit = () => { + return new Promise((resolve, reject) => { + if (this.transactionContextAPI.getGasLimit) { + return this.transactionContextAPI.getGasLimit((err, value) => { + if (err) return reject(err) + return resolve(value) + }) + } + return resolve(3000000) + }) + } + const queryValue = () => { + return new Promise((resolve, reject) => { + if (args.value) { + return resolve(args.value) + } + if (args.useCall || !this.transactionContextAPI.getValue) { + return resolve(0) + } + this.transactionContextAPI.getValue((err, value) => { + if (err) return reject(err) + return resolve(value) }) + }) } - - async runTx (args, confirmationCb, continueCb, promptCb, cb) { - const getGasLimit = () => { - return new Promise((resolve, reject) => { - if (this.transactionContextAPI.getGasLimit) { - return this.transactionContextAPI.getGasLimit((err, value) => { - if (err) return reject(err) - return resolve(value) - }) - } - return resolve(3000000) - }) + const getAccount = () => { + return new Promise((resolve, reject) => { + if (args.from) { + return resolve(args.from) } - const queryValue = () => { - return new Promise((resolve, reject) => { - if (args.value) { - return resolve(args.value) - } - if (args.useCall || !this.transactionContextAPI.getValue) { - return resolve(0) - } - this.transactionContextAPI.getValue((err, value) => { - if (err) return reject(err) - return resolve(value) - }) - }) + if (this.transactionContextAPI.getAddress) { + return this.transactionContextAPI.getAddress(function (err, address) { + if (err) return reject(err) + if (!address) return reject('"from" is not defined. Please make sure an account is selected. If you are using a public node, it is likely that no account will be provided. In that case, add the public node to your injected provider (type Metamask) and use injected provider in Remix.') + return resolve(address) + }) } - const getAccount = () => { - return new Promise((resolve, reject) => { - if (args.from) { - return resolve(args.from) - } - if (this.transactionContextAPI.getAddress) { - return this.transactionContextAPI.getAddress(function (err, address) { - if (err) return reject(err) - if (!address) return reject('"from" is not defined. Please make sure an account is selected. If you are using a public node, it is likely that no account will be provided. In that case, add the public node to your injected provider (type Metamask) and use injected provider in Remix.') - return resolve(address) - }) - } - this.getAccounts(function (err, accounts) { - if (err) return reject(err) - const address = accounts[0] - - if (!address) return reject('No accounts available') - if (this.executionContext.isVM() && !this.providers.vm.RemixSimulatorProvider.Accounts.accounts[address]) { - return reject('Invalid account selected') - } - return resolve(address) - }) - }) + this.getAccounts(function (err, accounts) { + if (err) return reject(err) + const address = accounts[0] + + if (!address) return reject('No accounts available') + if (this.executionContext.isVM() && !this.providers.vm.RemixSimulatorProvider.Accounts.accounts[address]) { + return reject('Invalid account selected') + } + return resolve(address) + }) + }) + } + const runTransaction = async () => { + // eslint-disable-next-line no-async-promise-executor + return new Promise(async (resolve, reject) => { + let fromAddress + let value + let gasLimit + try { + fromAddress = await getAccount() + value = await queryValue() + gasLimit = await getGasLimit() + } catch (e) { + reject(e) + return } - const runTransaction = async () => { - // eslint-disable-next-line no-async-promise-executor - return new Promise(async (resolve, reject) => { - let fromAddress - let value - let gasLimit - try { - fromAddress = await getAccount() - value = await queryValue() - gasLimit = await getGasLimit() - } catch (e) { - reject(e) - return - } - const tx = { to: args.to, data: args.data.dataHex, useCall: args.useCall, from: fromAddress, value: value, gasLimit: gasLimit, timestamp: args.data.timestamp } - const payLoad = { funAbi: args.data.funAbi, funArgs: args.data.funArgs, contractBytecode: args.data.contractBytecode, contractName: args.data.contractName, contractABI: args.data.contractABI, linkReferences: args.data.linkReferences } + const tx = { to: args.to, data: args.data.dataHex, useCall: args.useCall, from: fromAddress, value: value, gasLimit: gasLimit, timestamp: args.data.timestamp } + const payLoad = { funAbi: args.data.funAbi, funArgs: args.data.funArgs, contractBytecode: args.data.contractBytecode, contractName: args.data.contractName, contractABI: args.data.contractABI, linkReferences: args.data.linkReferences } - if (!tx.timestamp) tx.timestamp = Date.now() - const timestamp = tx.timestamp + if (!tx.timestamp) tx.timestamp = Date.now() + const timestamp = tx.timestamp - this._triggerEvent('initiatingTransaction', [timestamp, tx, payLoad]) - try { - this.txRunner.rawRun(tx, confirmationCb, continueCb, promptCb, - async (error, result) => { - if (error) { - if (typeof (error) !== 'string') { - if (error.message) error = error.message - else { - try { error = 'error: ' + JSON.stringify(error) } catch (e) { console.log(e) } - } - } - return reject(error) - } + this._triggerEvent('initiatingTransaction', [timestamp, tx, payLoad]) + try { + this.txRunner.rawRun(tx, confirmationCb, continueCb, promptCb, + async (error, result) => { + if (error) { + if (typeof (error) !== 'string') { + if (error.message) error = error.message + else { + try { error = 'error: ' + JSON.stringify(error) } catch (e) { console.log(e) } + } + } + return reject(error) + } - const isVM = this.executionContext.isVM() - if (isVM && tx.useCall) { - try { - result.transactionHash = await this.web3().eth.getHashFromTagBySimulator(timestamp) - } catch (e) { - console.log('unable to retrieve back the "call" hash', e) - } - } - const eventName = (tx.useCall ? 'callExecuted' : 'transactionExecuted') - - this._triggerEvent(eventName, [error, tx.from, tx.to, tx.data, tx.useCall, result, timestamp, payLoad]) - return resolve({ result, tx }) - } - ) - } catch (err) { - let error = err - if (error && (typeof (error) !== 'string')) { - if (error.message) error = error.message - else { - try { error = 'error: ' + JSON.stringify(error) } catch (e) { console.log(e) } - } - } - return reject(error) + const isVM = this.executionContext.isVM() + if (isVM && tx.useCall) { + try { + result.transactionHash = await this.web3().eth.getHashFromTagBySimulator(timestamp) + } catch (e) { + console.log('unable to retrieve back the "call" hash', e) } - }) + } + const eventName = (tx.useCall ? 'callExecuted' : 'transactionExecuted') + + this._triggerEvent(eventName, [error, tx.from, tx.to, tx.data, tx.useCall, result, timestamp, payLoad]) + return resolve({ result, tx }) + } + ) + } catch (err) { + let error = err + if (error && (typeof (error) !== 'string')) { + if (error.message) error = error.message + else { + try { error = 'error: ' + JSON.stringify(error) } catch (e) { console.log(e) } + } + } + return reject(error) } - try { - const transaction = await runTransaction() - const txResult = (transaction as any).result - const tx = (transaction as any).tx - /* + }) + } + try { + const transaction = await runTransaction() + const txResult = (transaction as any).result + const tx = (transaction as any).tx + /* value of txResult is inconsistent: - transact to contract: {"receipt": { ... }, "tx":{ ... }, "transactionHash":"0x7ba4c05075210fdbcf4e6660258379db5cc559e15703f9ac6f970a320c2dee09"} - call to contract: {"result":"0x0000000000000000000000000000000000000000000000000000000000000000","transactionHash":"0x5236a76152054a8aad0c7135bcc151f03bccb773be88fbf4823184e47fc76247"} */ - const isVM = this.executionContext.isVM() - let execResult - let returnValue = null - if (isVM) { - const hhlogs = await this.web3().eth.getHHLogsForTx(txResult.transactionHash) - - if (hhlogs && hhlogs.length) { - const finalLogs =
console.log:
- { - hhlogs.map((log) => { - let formattedLog - // Hardhat implements the same formatting options that can be found in Node.js' console.log, - // which in turn uses util.format: https://nodejs.org/dist/latest-v12.x/docs/api/util.html#util_util_format_format_args - // For example: console.log("Name: %s, Age: %d", remix, 6) will log 'Name: remix, Age: 6' - // We check first arg to determine if 'util.format' is needed - if (typeof log[0] === 'string' && (log[0].includes('%s') || log[0].includes('%d'))) { - formattedLog = format(log[0], ...log.slice(1)) - } else { - formattedLog = log.join(' ') - } - return
{formattedLog}
- })} -
- _paq.push(['trackEvent', 'udapp', 'hardhat', 'console.log']) - this.call('terminal', 'logHtml', finalLogs) - } - execResult = await this.web3().eth.getExecutionResultFromSimulator(txResult.transactionHash) - 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 ? toBuffer(execResult.returnValue) : toBuffer(addHexPrefix(txResult.result) || '0x0000000000000000000000000000000000000000000000000000000000000000') - const compiledContracts = await this.call('compilerArtefacts', 'getAllContractDatas') - const vmError = txExecution.checkVMError(execResult, compiledContracts) - if (vmError.error) { - return cb(vmError.message) - } + const isVM = this.executionContext.isVM() + let execResult + let returnValue = null + if (isVM) { + const hhlogs = await this.web3().eth.getHHLogsForTx(txResult.transactionHash) + + if (hhlogs && hhlogs.length) { + const finalLogs =
console.log:
+ { + hhlogs.map((log) => { + let formattedLog + // Hardhat implements the same formatting options that can be found in Node.js' console.log, + // which in turn uses util.format: https://nodejs.org/dist/latest-v12.x/docs/api/util.html#util_util_format_format_args + // For example: console.log("Name: %s, Age: %d", remix, 6) will log 'Name: remix, Age: 6' + // We check first arg to determine if 'util.format' is needed + if (typeof log[0] === 'string' && (log[0].includes('%s') || log[0].includes('%d'))) { + formattedLog = format(log[0], ...log.slice(1)) + } else { + formattedLog = log.join(' ') } - } + return
{formattedLog}
+ })} +
+ _paq.push(['trackEvent', 'udapp', 'hardhat', 'console.log']) + this.call('terminal', 'logHtml', finalLogs) + } + execResult = await this.web3().eth.getExecutionResultFromSimulator(txResult.transactionHash) + 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 ? toBuffer(execResult.returnValue) : toBuffer(addHexPrefix(txResult.result) || '0x0000000000000000000000000000000000000000000000000000000000000000') + const compiledContracts = await this.call('compilerArtefacts', 'getAllContractDatas') + const vmError = txExecution.checkVMError(execResult, compiledContracts) + if (vmError.error) { + return cb(vmError.message) + } + } + } - if (!isVM && tx && tx.useCall) { - returnValue = toBuffer(addHexPrefix(txResult.result)) - } + if (!isVM && tx && tx.useCall) { + returnValue = toBuffer(addHexPrefix(txResult.result)) + } - let address = null - if (txResult && txResult.receipt) { - address = txResult.receipt.contractAddress - } + let address = null + if (txResult && txResult.receipt) { + address = txResult.receipt.contractAddress + } - cb(null, txResult, address, returnValue) - } catch (error) { - cb(error) - } + cb(null, txResult, address, returnValue) + } catch (error) { + cb(error) } + } } diff --git a/apps/remix-ide/src/blockchain/execution-context.js b/apps/remix-ide/src/blockchain/execution-context.js index d730a130e3..29d00f872a 100644 --- a/apps/remix-ide/src/blockchain/execution-context.js +++ b/apps/remix-ide/src/blockchain/execution-context.js @@ -8,188 +8,188 @@ const _paq = window._paq = window._paq || [] let web3 if (typeof window !== 'undefined' && typeof window.ethereum !== 'undefined') { - var injectedProvider = window.ethereum - web3 = new Web3(injectedProvider) + var injectedProvider = window.ethereum + web3 = new Web3(injectedProvider) } else { - web3 = new Web3(new Web3.providers.HttpProvider('http://localhost:8545')) + web3 = new Web3(new Web3.providers.HttpProvider('http://localhost:8545')) } /* trigger contextChanged, web3EndpointChanged */ export class ExecutionContext { - constructor () { - this.event = new EventManager() - this.executionContext = 'vm-shanghai' - this.lastBlock = null - this.blockGasLimitDefault = 4300000 - this.blockGasLimit = this.blockGasLimitDefault - this.currentFork = 'shanghai' - this.mainNetGenesisHash = '0xd4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3' - this.customNetWorks = {} - this.blocks = {} - this.latestBlockNumber = 0 - this.txs = {} - this.customWeb3 = {} // mapping between a context name and a web3.js instance - } - - init (config) { - this.executionContext = 'vm-shanghai' - this.event.trigger('contextChanged', [this.executionContext]) - } - - getProvider () { - return this.executionContext - } - - getProviderObject () { - return this.customNetWorks[this.executionContext] - } - - getSelectedAddress () { - return injectedProvider ? injectedProvider.selectedAddress : null - } - - getCurrentFork () { - return this.currentFork - } - - isVM () { - return this.executionContext.startsWith('vm') - } - - setWeb3 (context, web3) { - this.customWeb3[context] = web3 - } - - web3 () { - if (this.customWeb3[this.executionContext]) return this.customWeb3[this.executionContext] - return web3 - } - - detectNetwork (callback) { - if (this.isVM()) { - callback(null, { id: '-', name: 'VM' }) + constructor () { + this.event = new EventManager() + this.executionContext = 'vm-shanghai' + this.lastBlock = null + this.blockGasLimitDefault = 4300000 + this.blockGasLimit = this.blockGasLimitDefault + this.currentFork = 'shanghai' + this.mainNetGenesisHash = '0xd4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3' + this.customNetWorks = {} + this.blocks = {} + this.latestBlockNumber = 0 + this.txs = {} + this.customWeb3 = {} // mapping between a context name and a web3.js instance + } + + init (config) { + this.executionContext = 'vm-shanghai' + this.event.trigger('contextChanged', [this.executionContext]) + } + + getProvider () { + return this.executionContext + } + + getProviderObject () { + return this.customNetWorks[this.executionContext] + } + + getSelectedAddress () { + return injectedProvider ? injectedProvider.selectedAddress : null + } + + getCurrentFork () { + return this.currentFork + } + + isVM () { + return this.executionContext.startsWith('vm') + } + + setWeb3 (context, web3) { + this.customWeb3[context] = web3 + } + + web3 () { + if (this.customWeb3[this.executionContext]) return this.customWeb3[this.executionContext] + return web3 + } + + detectNetwork (callback) { + if (this.isVM()) { + callback(null, { id: '-', name: 'VM' }) + } else { + if (!web3.currentProvider) { + return callback('No provider set') + } + web3.eth.net.getId((err, id) => { + let name = null + if (err) name = 'Unknown' + // https://github.com/ethereum/EIPs/blob/master/EIPS/eip-155.md + else if (id === 1) name = 'Main' + else if (id === 3) name = 'Ropsten' + else if (id === 4) name = 'Rinkeby' + else if (id === 5) name = 'Goerli' + else if (id === 42) name = 'Kovan' + else if (id === 11155111) name = 'Sepolia' + else name = 'Custom' + + if (id === '1') { + web3.eth.getBlock(0, (error, block) => { + if (error) console.log('cant query first block') + if (block && block.hash !== this.mainNetGenesisHash) name = 'Custom' + callback(err, { id, name, lastBlock: this.lastBlock, currentFork: this.currentFork }) + }) } else { - if (!web3.currentProvider) { - return callback('No provider set') - } - web3.eth.net.getId((err, id) => { - let name = null - if (err) name = 'Unknown' - // https://github.com/ethereum/EIPs/blob/master/EIPS/eip-155.md - else if (id === 1) name = 'Main' - else if (id === 3) name = 'Ropsten' - else if (id === 4) name = 'Rinkeby' - else if (id === 5) name = 'Goerli' - else if (id === 42) name = 'Kovan' - else if (id === 11155111) name = 'Sepolia' - else name = 'Custom' - - if (id === '1') { - web3.eth.getBlock(0, (error, block) => { - if (error) console.log('cant query first block') - if (block && block.hash !== this.mainNetGenesisHash) name = 'Custom' - callback(err, { id, name, lastBlock: this.lastBlock, currentFork: this.currentFork }) - }) - } else { - callback(err, { id, name, lastBlock: this.lastBlock, currentFork: this.currentFork }) - } - }) + callback(err, { id, name, lastBlock: this.lastBlock, currentFork: this.currentFork }) } + }) } + } - removeProvider (name) { - if (name && this.customNetWorks[name]) { - if (this.executionContext === name) this.setContext('vm-merge', null, null, null) - delete this.customNetWorks[name] - this.event.trigger('removeProvider', [name]) - } + removeProvider (name) { + if (name && this.customNetWorks[name]) { + if (this.executionContext === name) this.setContext('vm-merge', null, null, null) + delete this.customNetWorks[name] + this.event.trigger('removeProvider', [name]) } + } - addProvider (network) { - if (network && network.name && !this.customNetWorks[network.name]) { - this.customNetWorks[network.name] = network - this.event.trigger('addProvider', [network]) - } + addProvider (network) { + if (network && network.name && !this.customNetWorks[network.name]) { + this.customNetWorks[network.name] = network + this.event.trigger('addProvider', [network]) } + } - internalWeb3 () { - return web3 - } + internalWeb3 () { + return web3 + } - setContext (context, endPointUrl, confirmCb, infoCb) { - this.executionContext = context - this.executionContextChange(context, endPointUrl, confirmCb, infoCb, null) - } - - async executionContextChange (value, endPointUrl, confirmCb, infoCb, cb) { - _paq.push(['trackEvent', 'udapp', 'providerChanged', value.context]) - const context = value.context - if (!cb) cb = () => { /* Do nothing. */ } - if (!confirmCb) confirmCb = () => { /* Do nothing. */ } - if (!infoCb) infoCb = () => { /* Do nothing. */ } - if (this.customNetWorks[context]) { - var network = this.customNetWorks[context] - await network.init() - this.currentFork = network.fork - this.executionContext = context - // injected - web3.setProvider(network.provider) - await this._updateChainContext() - this.event.trigger('contextChanged', [context]) - cb() + setContext (context, endPointUrl, confirmCb, infoCb) { + this.executionContext = context + this.executionContextChange(context, endPointUrl, confirmCb, infoCb, null) + } + + async executionContextChange (value, endPointUrl, confirmCb, infoCb, cb) { + _paq.push(['trackEvent', 'udapp', 'providerChanged', value.context]) + const context = value.context + if (!cb) cb = () => { /* Do nothing. */ } + if (!confirmCb) confirmCb = () => { /* Do nothing. */ } + if (!infoCb) infoCb = () => { /* Do nothing. */ } + if (this.customNetWorks[context]) { + var network = this.customNetWorks[context] + await network.init() + this.currentFork = network.fork + this.executionContext = context + // injected + web3.setProvider(network.provider) + await this._updateChainContext() + this.event.trigger('contextChanged', [context]) + cb() + } + } + + currentblockGasLimit () { + return this.blockGasLimit + } + + stopListenOnLastBlock () { + if (this.listenOnLastBlockId) clearInterval(this.listenOnLastBlockId) + this.listenOnLastBlockId = null + } + + async _updateChainContext () { + if (!this.isVM()) { + try { + const block = await web3.eth.getBlock('latest') + // we can't use the blockGasLimit cause the next blocks could have a lower limit : https://github.com/ethereum/remix/issues/506 + this.blockGasLimit = (block && block.gasLimit) ? Math.floor(block.gasLimit - (5 * block.gasLimit) / 1024) : this.blockGasLimitDefault + this.lastBlock = block + try { + this.currentFork = execution.forkAt(await web3.eth.net.getId(), block.number) + } catch (e) { + this.currentFork = 'merge' + console.log(`unable to detect fork, defaulting to ${this.currentFork}..`) + console.error(e) } + } catch (e) { + console.error(e) + this.blockGasLimit = this.blockGasLimitDefault + } } + } - currentblockGasLimit () { - return this.blockGasLimit - } - - stopListenOnLastBlock () { - if (this.listenOnLastBlockId) clearInterval(this.listenOnLastBlockId) - this.listenOnLastBlockId = null - } - - async _updateChainContext () { - if (!this.isVM()) { - try { - const block = await web3.eth.getBlock('latest') - // we can't use the blockGasLimit cause the next blocks could have a lower limit : https://github.com/ethereum/remix/issues/506 - this.blockGasLimit = (block && block.gasLimit) ? Math.floor(block.gasLimit - (5 * block.gasLimit) / 1024) : this.blockGasLimitDefault - this.lastBlock = block - try { - this.currentFork = execution.forkAt(await web3.eth.net.getId(), block.number) - } catch (e) { - this.currentFork = 'merge' - console.log(`unable to detect fork, defaulting to ${this.currentFork}..`) - console.error(e) - } - } catch (e) { - console.error(e) - this.blockGasLimit = this.blockGasLimitDefault - } - } - } + listenOnLastBlock () { + this.listenOnLastBlockId = setInterval(() => { + this._updateChainContext() + }, 15000) + } - listenOnLastBlock () { - this.listenOnLastBlockId = setInterval(() => { - this._updateChainContext() - }, 15000) + txDetailsLink (network, hash) { + const transactionDetailsLinks = { + Main: 'https://www.etherscan.io/tx/', + Rinkeby: 'https://rinkeby.etherscan.io/tx/', + Ropsten: 'https://ropsten.etherscan.io/tx/', + Sepolia: 'https://sepolia.etherscan.io/tx/', + Kovan: 'https://kovan.etherscan.io/tx/', + Goerli: 'https://goerli.etherscan.io/tx/' } - txDetailsLink (network, hash) { - const transactionDetailsLinks = { - Main: 'https://www.etherscan.io/tx/', - Rinkeby: 'https://rinkeby.etherscan.io/tx/', - Ropsten: 'https://ropsten.etherscan.io/tx/', - Sepolia: 'https://sepolia.etherscan.io/tx/', - Kovan: 'https://kovan.etherscan.io/tx/', - Goerli: 'https://goerli.etherscan.io/tx/' - } - - if (transactionDetailsLinks[network]) { - return transactionDetailsLinks[network] + hash - } + if (transactionDetailsLinks[network]) { + return transactionDetailsLinks[network] + hash } + } } diff --git a/apps/remix-ide/src/blockchain/helper.ts b/apps/remix-ide/src/blockchain/helper.ts index 7df7546d20..9760194e4f 100644 --- a/apps/remix-ide/src/blockchain/helper.ts +++ b/apps/remix-ide/src/blockchain/helper.ts @@ -1,14 +1,14 @@ const transactionDetailsLinks = { - Main: 'https://www.etherscan.io/tx/', - Rinkeby: 'https://rinkeby.etherscan.io/tx/', - Ropsten: 'https://ropsten.etherscan.io/tx/', - Kovan: 'https://kovan.etherscan.io/tx/', - Goerli: 'https://goerli.etherscan.io/tx/', - Sepolia: 'https://sepolia.etherscan.io/tx/' + Main: 'https://www.etherscan.io/tx/', + Rinkeby: 'https://rinkeby.etherscan.io/tx/', + Ropsten: 'https://ropsten.etherscan.io/tx/', + Kovan: 'https://kovan.etherscan.io/tx/', + Goerli: 'https://goerli.etherscan.io/tx/', + Sepolia: 'https://sepolia.etherscan.io/tx/' } export function etherScanLink (network: string, hash: string): string { - if (transactionDetailsLinks[network]) { - return transactionDetailsLinks[network] + hash - } + if (transactionDetailsLinks[network]) { + return transactionDetailsLinks[network] + hash + } } \ No newline at end of file diff --git a/apps/remix-ide/src/blockchain/providers/injected.ts b/apps/remix-ide/src/blockchain/providers/injected.ts index bc6ab49e7f..dce2341b99 100644 --- a/apps/remix-ide/src/blockchain/providers/injected.ts +++ b/apps/remix-ide/src/blockchain/providers/injected.ts @@ -3,47 +3,47 @@ import { hashPersonalMessage } from '@ethereumjs/util' import { ExecutionContext } from '../execution-context' export class InjectedProvider { - executionContext: ExecutionContext - - constructor (executionContext) { - this.executionContext = executionContext - } - - getAccounts (cb) { - return this.executionContext.web3().eth.getAccounts(cb) - } - - newAccount (passwordPromptCb, cb) { - passwordPromptCb((passphrase) => { - this.executionContext.web3().eth.personal.newAccount(passphrase, cb) - }) - } - - async resetEnvironment () { - /* Do nothing. */ - } - - async getBalanceInEther (address) { - const balance = await this.executionContext.web3().eth.getBalance(address) - return Web3.utils.fromWei(balance.toString(10), 'ether') - } - - getGasPrice (cb) { - this.executionContext.web3().eth.getGasPrice(cb) - } - - signMessage (message, account, _passphrase, cb) { - const messageHash = hashPersonalMessage(Buffer.from(message)) - try { - this.executionContext.web3().eth.personal.sign(message, account, (error, signedData) => { - cb(error, '0x' + messageHash.toString('hex'), signedData) - }) - } catch (e) { - cb(e.message) - } - } - - getProvider () { - return 'injected' - } + executionContext: ExecutionContext + + constructor (executionContext) { + this.executionContext = executionContext + } + + getAccounts (cb) { + return this.executionContext.web3().eth.getAccounts(cb) + } + + newAccount (passwordPromptCb, cb) { + passwordPromptCb((passphrase) => { + this.executionContext.web3().eth.personal.newAccount(passphrase, cb) + }) + } + + async resetEnvironment () { + /* Do nothing. */ + } + + async getBalanceInEther (address) { + const balance = await this.executionContext.web3().eth.getBalance(address) + return Web3.utils.fromWei(balance.toString(10), 'ether') + } + + getGasPrice (cb) { + this.executionContext.web3().eth.getGasPrice(cb) + } + + signMessage (message, account, _passphrase, cb) { + const messageHash = hashPersonalMessage(Buffer.from(message)) + try { + this.executionContext.web3().eth.personal.sign(message, account, (error, signedData) => { + cb(error, '0x' + messageHash.toString('hex'), signedData) + }) + } catch (e) { + cb(e.message) + } + } + + getProvider () { + return 'injected' + } } diff --git a/apps/remix-ide/src/blockchain/providers/node.ts b/apps/remix-ide/src/blockchain/providers/node.ts index 65116b4923..e08aa1e66b 100644 --- a/apps/remix-ide/src/blockchain/providers/node.ts +++ b/apps/remix-ide/src/blockchain/providers/node.ts @@ -5,56 +5,56 @@ import { ExecutionContext } from '../execution-context' import Config from '../../config' export class NodeProvider { - executionContext: ExecutionContext - config: Config - - constructor (executionContext: ExecutionContext, config: Config) { - this.executionContext = executionContext - this.config = config - } - - getAccounts (cb) { - if (this.config.get('settings/personal-mode')) { - return this.executionContext.web3().eth.personal.getAccounts(cb) - } - return this.executionContext.web3().eth.getAccounts(cb) - } - - newAccount (passwordPromptCb, cb) { - if (!this.config.get('settings/personal-mode')) { - return cb('Not running in personal mode') - } - passwordPromptCb((passphrase) => { - this.executionContext.web3().eth.personal.newAccount(passphrase, cb) - }) - } - - async resetEnvironment () { - /* Do nothing. */ - } - - async getBalanceInEther (address) { - const balance = await this.executionContext.web3().eth.getBalance(address) - return Web3.utils.fromWei(balance.toString(10), 'ether') - } - - getGasPrice (cb) { - this.executionContext.web3().eth.getGasPrice(cb) - } - - signMessage (message, account, passphrase, cb) { - const messageHash = hashPersonalMessage(Buffer.from(message)) - try { - const personal = new Personal(this.executionContext.web3().currentProvider) - personal.sign(message, account, passphrase, (error, signedData) => { - cb(error, '0x' + messageHash.toString('hex'), signedData) - }) - } catch (e) { - cb(e.message) - } - } - - getProvider () { - return this.executionContext.getProvider() - } + executionContext: ExecutionContext + config: Config + + constructor (executionContext: ExecutionContext, config: Config) { + this.executionContext = executionContext + this.config = config + } + + getAccounts (cb) { + if (this.config.get('settings/personal-mode')) { + return this.executionContext.web3().eth.personal.getAccounts(cb) + } + return this.executionContext.web3().eth.getAccounts(cb) + } + + newAccount (passwordPromptCb, cb) { + if (!this.config.get('settings/personal-mode')) { + return cb('Not running in personal mode') + } + passwordPromptCb((passphrase) => { + this.executionContext.web3().eth.personal.newAccount(passphrase, cb) + }) + } + + async resetEnvironment () { + /* Do nothing. */ + } + + async getBalanceInEther (address) { + const balance = await this.executionContext.web3().eth.getBalance(address) + return Web3.utils.fromWei(balance.toString(10), 'ether') + } + + getGasPrice (cb) { + this.executionContext.web3().eth.getGasPrice(cb) + } + + signMessage (message, account, passphrase, cb) { + const messageHash = hashPersonalMessage(Buffer.from(message)) + try { + const personal = new Personal(this.executionContext.web3().currentProvider) + personal.sign(message, account, passphrase, (error, signedData) => { + cb(error, '0x' + messageHash.toString('hex'), signedData) + }) + } catch (e) { + cb(e.message) + } + } + + getProvider () { + return this.executionContext.getProvider() + } } diff --git a/apps/remix-ide/src/blockchain/providers/vm.ts b/apps/remix-ide/src/blockchain/providers/vm.ts index 4234e42616..29cbf4edde 100644 --- a/apps/remix-ide/src/blockchain/providers/vm.ts +++ b/apps/remix-ide/src/blockchain/providers/vm.ts @@ -5,105 +5,105 @@ import { extend, JSONRPCRequestPayload, JSONRPCResponseCallback } from '@remix-p import { ExecutionContext } from '../execution-context' export class VMProvider { - executionContext: ExecutionContext - web3: Web3 - worker: Worker - provider: { + executionContext: ExecutionContext + web3: Web3 + worker: Worker + provider: { sendAsync: (query: JSONRPCRequestPayload, callback: JSONRPCResponseCallback) => void } - newAccountCallback: {[stamp: number]: (error: Error, address: string) => void} + newAccountCallback: {[stamp: number]: (error: Error, address: string) => void} - constructor (executionContext: ExecutionContext) { - this.executionContext = executionContext - this.worker = null - this.provider = null - this.newAccountCallback = {} - } + constructor (executionContext: ExecutionContext) { + this.executionContext = executionContext + this.worker = null + this.provider = null + this.newAccountCallback = {} + } - getAccounts (cb) { - this.web3.eth.getAccounts((err, accounts) => { - if (err) { - return cb('No accounts?') - } - return cb(null, accounts) - }) - } + getAccounts (cb) { + this.web3.eth.getAccounts((err, accounts) => { + if (err) { + return cb('No accounts?') + } + return cb(null, accounts) + }) + } - async resetEnvironment () { - if (this.worker) this.worker.terminate() - this.worker = new Worker(new URL('./worker-vm', import.meta.url)) - const provider = this.executionContext.getProviderObject() + async resetEnvironment () { + if (this.worker) this.worker.terminate() + this.worker = new Worker(new URL('./worker-vm', import.meta.url)) + const provider = this.executionContext.getProviderObject() - let incr = 0 - const stamps = {} + let incr = 0 + const stamps = {} - return new Promise((resolve, reject) => { - this.worker.addEventListener('message', (msg) => { - if (msg.data.cmd === 'sendAsyncResult' && stamps[msg.data.stamp]) { - stamps[msg.data.stamp](msg.data.error, msg.data.result) - } else if (msg.data.cmd === 'initiateResult') { - if (!msg.data.error) { - this.provider = { - sendAsync: (query, callback) => { - const stamp = Date.now() + incr - incr++ - stamps[stamp] = callback - this.worker.postMessage({ cmd: 'sendAsync', query, stamp }) - } - } - this.web3 = new Web3(this.provider) - extend(this.web3) - this.executionContext.setWeb3(this.executionContext.getProvider(), this.web3) - resolve({}) - } else { - reject(new Error(msg.data.error)) - } - } else if (msg.data.cmd === 'newAccountResult') { - if (this.newAccountCallback[msg.data.stamp]) { - this.newAccountCallback[msg.data.stamp](msg.data.error, msg.data.result) - delete this.newAccountCallback[msg.data.stamp] - } - } - }) - this.worker.postMessage({ cmd: 'init', fork: this.executionContext.getCurrentFork(), nodeUrl: provider?.options['nodeUrl'], blockNumber: provider?.options['blockNumber']}) - }) - } + return new Promise((resolve, reject) => { + this.worker.addEventListener('message', (msg) => { + if (msg.data.cmd === 'sendAsyncResult' && stamps[msg.data.stamp]) { + stamps[msg.data.stamp](msg.data.error, msg.data.result) + } else if (msg.data.cmd === 'initiateResult') { + if (!msg.data.error) { + this.provider = { + sendAsync: (query, callback) => { + const stamp = Date.now() + incr + incr++ + stamps[stamp] = callback + this.worker.postMessage({ cmd: 'sendAsync', query, stamp }) + } + } + this.web3 = new Web3(this.provider) + extend(this.web3) + this.executionContext.setWeb3(this.executionContext.getProvider(), this.web3) + resolve({}) + } else { + reject(new Error(msg.data.error)) + } + } else if (msg.data.cmd === 'newAccountResult') { + if (this.newAccountCallback[msg.data.stamp]) { + this.newAccountCallback[msg.data.stamp](msg.data.error, msg.data.result) + delete this.newAccountCallback[msg.data.stamp] + } + } + }) + this.worker.postMessage({ cmd: 'init', fork: this.executionContext.getCurrentFork(), nodeUrl: provider?.options['nodeUrl'], blockNumber: provider?.options['blockNumber']}) + }) + } - // TODO: is still here because of the plugin API - // can be removed later when we update the API - createVMAccount (newAccount) { - const { privateKey, balance } = newAccount - this.worker.postMessage({ cmd: 'addAccount', privateKey: privateKey, balance }) - const privKey = Buffer.from(privateKey, 'hex') - return '0x' + privateToAddress(privKey).toString('hex') - } + // TODO: is still here because of the plugin API + // can be removed later when we update the API + createVMAccount (newAccount) { + const { privateKey, balance } = newAccount + this.worker.postMessage({ cmd: 'addAccount', privateKey: privateKey, balance }) + const privKey = Buffer.from(privateKey, 'hex') + return '0x' + privateToAddress(privKey).toString('hex') + } - newAccount (_passwordPromptCb, cb) { - const stamp = Date.now() - this.newAccountCallback[stamp] = cb - this.worker.postMessage({ cmd: 'newAccount', stamp }) - } + newAccount (_passwordPromptCb, cb) { + const stamp = Date.now() + this.newAccountCallback[stamp] = cb + this.worker.postMessage({ cmd: 'newAccount', stamp }) + } - async getBalanceInEther (address) { - const balance = await this.web3.eth.getBalance(address) - return Web3.utils.fromWei(new BN(balance).toString(10), 'ether') - } + async getBalanceInEther (address) { + const balance = await this.web3.eth.getBalance(address) + return Web3.utils.fromWei(new BN(balance).toString(10), 'ether') + } - getGasPrice (cb) { - this.web3.eth.getGasPrice(cb) - } + getGasPrice (cb) { + this.web3.eth.getGasPrice(cb) + } - signMessage (message, account, _passphrase, cb) { - const messageHash = hashPersonalMessage(Buffer.from(message)) - this.web3.eth.sign(message, account, (error, signedData) => { - if (error) { - return cb(error) - } - cb(null, '0x' + messageHash.toString('hex'), signedData) - }) - } + signMessage (message, account, _passphrase, cb) { + const messageHash = hashPersonalMessage(Buffer.from(message)) + this.web3.eth.sign(message, account, (error, signedData) => { + if (error) { + return cb(error) + } + cb(null, '0x' + messageHash.toString('hex'), signedData) + }) + } - getProvider () { - return this.executionContext.getProvider() - } + getProvider () { + return this.executionContext.getProvider() + } } diff --git a/apps/remix-ide/src/blockchain/providers/worker-vm.ts b/apps/remix-ide/src/blockchain/providers/worker-vm.ts index 64a8d0255b..165eed809c 100644 --- a/apps/remix-ide/src/blockchain/providers/worker-vm.ts +++ b/apps/remix-ide/src/blockchain/providers/worker-vm.ts @@ -2,76 +2,76 @@ import { Provider } from '@remix-project/remix-simulator' let provider: Provider = null self.onmessage = (e: MessageEvent) => { - const data = e.data - switch (data.cmd) { - case 'init': - { - provider = new Provider({ fork: data.fork, nodeUrl: data.nodeUrl, blockNumber: data.blockNumber }) - provider.init().then(() => { - self.postMessage({ - cmd: 'initiateResult', - stamp: data.stamp - }) - }).catch((error) => { - self.postMessage({ - cmd: 'initiateResult', - error, - stamp: data.stamp - }) + const data = e.data + switch (data.cmd) { + case 'init': + { + provider = new Provider({ fork: data.fork, nodeUrl: data.nodeUrl, blockNumber: data.blockNumber }) + provider.init().then(() => { + self.postMessage({ + cmd: 'initiateResult', + stamp: data.stamp + }) + }).catch((error) => { + self.postMessage({ + cmd: 'initiateResult', + error, + stamp: data.stamp + }) + }) + break + } + case 'sendAsync': + { + if (provider) { + provider.sendAsync(data.query, (error, result) => { + self.postMessage({ + cmd: 'sendAsyncResult', + error, + result, + stamp: data.stamp }) - break + }) + } else { + self.postMessage({ + cmd: 'sendAsyncResult', + error: 'Provider not instantiated', + result: null, + stamp: data.stamp + }) } - case 'sendAsync': - { - if (provider) { - provider.sendAsync(data.query, (error, result) => { - self.postMessage({ - cmd: 'sendAsyncResult', - error, - result, - stamp: data.stamp - }) - }) - } else { - self.postMessage({ - cmd: 'sendAsyncResult', - error: 'Provider not instantiated', - result: null, - stamp: data.stamp - }) - } - break + break + } + case 'addAccount': + { + if (provider) { + provider.Accounts._addAccount(data.privateKey, data.balance) } - case 'addAccount': - { - if (provider) { - provider.Accounts._addAccount(data.privateKey, data.balance) - } - break + break + } + case 'newAccount': + { + if (provider) { + provider.Accounts.newAccount((error, address: string) => { + if (error) { + self.postMessage({ + cmd: 'newAccountResult', + error, + stamp: data.stamp + }) + } else { + self.postMessage({ + cmd: 'newAccountResult', + result: address, + stamp: data.stamp + }) + } + }) } - case 'newAccount': - { - if (provider) { - provider.Accounts.newAccount((error, address: string) => { - if (error) { - self.postMessage({ - cmd: 'newAccountResult', - error, - stamp: data.stamp - }) - } else { - self.postMessage({ - cmd: 'newAccountResult', - result: address, - stamp: data.stamp - }) - } - }) - } - break - } - } + break + } + } } diff --git a/apps/remix-ide/src/config.js b/apps/remix-ide/src/config.js index 349e394d07..5d5f3630cd 100644 --- a/apps/remix-ide/src/config.js +++ b/apps/remix-ide/src/config.js @@ -4,53 +4,53 @@ var CONFIG_FILE = '.remix.config' const EventEmitter = require('events') function Config(storage) { - this.items = {} - this.unpersistedItems = {} - this.events = new EventEmitter() - - // load on instantiation - try { - var config = storage.get(CONFIG_FILE) - if (config) { - this.items = JSON.parse(config) - } - } catch (exception) { - /* Do nothing. */ - } - - this.exists = function (key) { - return this.items[key] !== undefined - } - - this.get = function (key) { - return this.items[key] + this.items = {} + this.unpersistedItems = {} + this.events = new EventEmitter() + + // load on instantiation + try { + var config = storage.get(CONFIG_FILE) + if (config) { + this.items = JSON.parse(config) } + } catch (exception) { + /* Do nothing. */ + } - this.set = function (key, content) { - this.items[key] = content - try { - storage.set(CONFIG_FILE, JSON.stringify(this.items)) - this.events.emit('configChanged', { key, content }) - this.events.emit(key + '_changed', content) - } catch (exception) { - /* Do nothing. */ - } - } + this.exists = function (key) { + return this.items[key] !== undefined + } - this.clear = function () { - this.items = {} - storage.remove(CONFIG_FILE) - } + this.get = function (key) { + return this.items[key] + } - this.getUnpersistedProperty = function (key) { - return this.unpersistedItems[key] + this.set = function (key, content) { + this.items[key] = content + try { + storage.set(CONFIG_FILE, JSON.stringify(this.items)) + this.events.emit('configChanged', { key, content }) + this.events.emit(key + '_changed', content) + } catch (exception) { + /* Do nothing. */ } + } - // TODO: this only used for *one* property "doNotShowTransactionConfirmationAgain" - // and can be removed once it's refactored away in txRunner - this.setUnpersistedProperty = function (key, value) { - this.unpersistedItems[key] = value - } + this.clear = function () { + this.items = {} + storage.remove(CONFIG_FILE) + } + + this.getUnpersistedProperty = function (key) { + return this.unpersistedItems[key] + } + + // TODO: this only used for *one* property "doNotShowTransactionConfirmationAgain" + // and can be removed once it's refactored away in txRunner + this.setUnpersistedProperty = function (key, value) { + this.unpersistedItems[key] = value + } } module.exports = Config diff --git a/apps/remix-ide/src/index.tsx b/apps/remix-ide/src/index.tsx index 92a0206003..4b12675e7a 100644 --- a/apps/remix-ide/src/index.tsx +++ b/apps/remix-ide/src/index.tsx @@ -9,20 +9,20 @@ import Registry from './app/state/registry' import { Storage } from '@remix-project/remix-lib' (async function () { - try { - const configStorage = new Storage('config-v0.8:') - const config = new Config(configStorage); - Registry.getInstance().put({ api: config, name: 'config' }) - } catch (e) { } - const theme = new ThemeModule() - theme.initTheme() + try { + const configStorage = new Storage('config-v0.8:') + const config = new Config(configStorage); + Registry.getInstance().put({ api: config, name: 'config' }) + } catch (e) { } + const theme = new ThemeModule() + theme.initTheme() - render( - - - , - document.getElementById('root') - ) + render( + + + , + document.getElementById('root') + ) })() diff --git a/apps/remix-ide/src/lib/helper.js b/apps/remix-ide/src/lib/helper.js index d332c7e937..4a04fccdd2 100644 --- a/apps/remix-ide/src/lib/helper.js +++ b/apps/remix-ide/src/lib/helper.js @@ -2,153 +2,153 @@ var async = require('async') import { toChecksumAddress } from '@ethereumjs/util' export default { - shortenAddress: function (address, etherBalance) { - var len = address.length - return address.slice(0, 5) + '...' + address.slice(len - 5, len) + (etherBalance ? ' (' + etherBalance.toString() + ' ether)' : '') - }, - addressToString: function (address) { - if (!address) return null - if (typeof address !== 'string') { - address = address.toString('hex') - } - if (address.indexOf('0x') === -1) { - address = '0x' + address - } - return toChecksumAddress(address) - }, - shortenHexData: function (data) { - if (!data) return '' - if (data.length < 5) return data - var len = data.length - return data.slice(0, 5) + '...' + data.slice(len - 5, len) - }, - createNonClashingNameWithPrefix (name, fileProvider, prefix, cb) { - if (!name) name = 'Undefined' - var counter = '' - var ext = 'sol' - var reg = /(.*)\.([^.]+)/g - var split = reg.exec(name) - if (split) { - name = split[1] - ext = split[2] - } - var exist = true - async.whilst( - () => { return exist }, - (callback) => { - fileProvider.exists(name + counter + prefix + '.' + ext).then(currentExist => { - exist = currentExist - if (exist) counter = (counter | 0) + 1 - callback() - }).catch(error => { - if (error) console.log(error) - }) - }, - (error) => { cb(error, name + counter + prefix + '.' + ext) } - ) - }, - createNonClashingName (name, fileProvider, cb) { - this.createNonClashingNameWithPrefix(name, fileProvider, '', cb) - }, - async createNonClashingNameAsync (name, fileManager, prefix = '') { - if (!name) name = 'Undefined' - let counter = '' - let ext = 'sol' - const reg = /(.*)\.([^.]+)/g - const split = reg.exec(name) - if (split) { - name = split[1] - ext = split[2] - } - let exist = true + shortenAddress: function (address, etherBalance) { + var len = address.length + return address.slice(0, 5) + '...' + address.slice(len - 5, len) + (etherBalance ? ' (' + etherBalance.toString() + ' ether)' : '') + }, + addressToString: function (address) { + if (!address) return null + if (typeof address !== 'string') { + address = address.toString('hex') + } + if (address.indexOf('0x') === -1) { + address = '0x' + address + } + return toChecksumAddress(address) + }, + shortenHexData: function (data) { + if (!data) return '' + if (data.length < 5) return data + var len = data.length + return data.slice(0, 5) + '...' + data.slice(len - 5, len) + }, + createNonClashingNameWithPrefix (name, fileProvider, prefix, cb) { + if (!name) name = 'Undefined' + var counter = '' + var ext = 'sol' + var reg = /(.*)\.([^.]+)/g + var split = reg.exec(name) + if (split) { + name = split[1] + ext = split[2] + } + var exist = true + async.whilst( + () => { return exist }, + (callback) => { + fileProvider.exists(name + counter + prefix + '.' + ext).then(currentExist => { + exist = currentExist + if (exist) counter = (counter | 0) + 1 + callback() + }).catch(error => { + if (error) console.log(error) + }) + }, + (error) => { cb(error, name + counter + prefix + '.' + ext) } + ) + }, + createNonClashingName (name, fileProvider, cb) { + this.createNonClashingNameWithPrefix(name, fileProvider, '', cb) + }, + async createNonClashingNameAsync (name, fileManager, prefix = '') { + if (!name) name = 'Undefined' + let counter = '' + let ext = 'sol' + const reg = /(.*)\.([^.]+)/g + const split = reg.exec(name) + if (split) { + name = split[1] + ext = split[2] + } + let exist = true - do { - const isDuplicate = await fileManager.exists(name + counter + prefix + '.' + ext) - if (isDuplicate) counter = (counter | 0) + 1 - else exist = false - } while (exist) + do { + const isDuplicate = await fileManager.exists(name + counter + prefix + '.' + ext) + if (isDuplicate) counter = (counter | 0) + 1 + else exist = false + } while (exist) - return name + counter + prefix + '.' + ext - }, - async createNonClashingDirNameAsync (name, fileManager) { - if (!name) name = 'Undefined' - let counter = '' - let exist = true + return name + counter + prefix + '.' + ext + }, + async createNonClashingDirNameAsync (name, fileManager) { + if (!name) name = 'Undefined' + let counter = '' + let exist = true - do { - const isDuplicate = await fileManager.exists(name + counter) + do { + const isDuplicate = await fileManager.exists(name + counter) - if (isDuplicate) counter = (counter | 0) + 1 - else exist = false - } while (exist) + if (isDuplicate) counter = (counter | 0) + 1 + else exist = false + } while (exist) - return name + counter - }, - checkSpecialChars (name) { - return name.match(/[:*?"<>\\'|]/) != null - }, - checkSlash (name) { - return name.match(/\//) != null - }, - isHexadecimal (value) { - return /^[0-9a-fA-F]+$/.test(value) && (value.length % 2 === 0) - }, - is0XPrefixed (value) { - return value.substr(0, 2) === '0x' - }, - isNumeric (value) { - return /^\+?(0|[1-9]\d*)$/.test(value) - }, - isValidHash (hash) { // 0x prefixed, hexadecimal, 64digit - const hexValue = hash.slice(2, hash.length) - return this.is0XPrefixed(hash) && /^[0-9a-fA-F]{64}$/.test(hexValue) - }, - removeTrailingSlashes (text) { - // Remove single or consecutive trailing slashes - return text.replace(/\/+$/g, '') - }, - removeMultipleSlashes (text) { - // Replace consecutive slashes with '/' - return text.replace(/\/+/g, '/') - }, - find: find, - joinPath (...paths) { - paths = paths.filter((value) => value !== '').map((path) => path.replace(/^\/|\/$/g, '')) // remove first and last slash) - if (paths.length === 1) return paths[0] - return paths.join('/') - }, - extractNameFromKey (key) { - const keyPath = key.split('/') + return name + counter + }, + checkSpecialChars (name) { + return name.match(/[:*?"<>\\'|]/) != null + }, + checkSlash (name) { + return name.match(/\//) != null + }, + isHexadecimal (value) { + return /^[0-9a-fA-F]+$/.test(value) && (value.length % 2 === 0) + }, + is0XPrefixed (value) { + return value.substr(0, 2) === '0x' + }, + isNumeric (value) { + return /^\+?(0|[1-9]\d*)$/.test(value) + }, + isValidHash (hash) { // 0x prefixed, hexadecimal, 64digit + const hexValue = hash.slice(2, hash.length) + return this.is0XPrefixed(hash) && /^[0-9a-fA-F]{64}$/.test(hexValue) + }, + removeTrailingSlashes (text) { + // Remove single or consecutive trailing slashes + return text.replace(/\/+$/g, '') + }, + removeMultipleSlashes (text) { + // Replace consecutive slashes with '/' + return text.replace(/\/+/g, '/') + }, + find: find, + joinPath (...paths) { + paths = paths.filter((value) => value !== '').map((path) => path.replace(/^\/|\/$/g, '')) // remove first and last slash) + if (paths.length === 1) return paths[0] + return paths.join('/') + }, + extractNameFromKey (key) { + const keyPath = key.split('/') - return keyPath[keyPath.length - 1] - } + return keyPath[keyPath.length - 1] + } } function findDeep (object, fn, found = { break: false, value: undefined }) { - if (typeof object !== 'object' || object === null) return - for (var i in object) { - if (found.break) break - var el = object[i] - if (el && el.innerText !== undefined && el.innerText !== null) el = el.innerText - if (fn(el, i, object)) { - found.value = el - found.break = true - break - } else { - findDeep(el, fn, found) - } + if (typeof object !== 'object' || object === null) return + for (var i in object) { + if (found.break) break + var el = object[i] + if (el && el.innerText !== undefined && el.innerText !== null) el = el.innerText + if (fn(el, i, object)) { + found.value = el + found.break = true + break + } else { + findDeep(el, fn, found) } - return found.value + } + return found.value } function find (args, query) { - query = query.trim() - var isMatch = !!findDeep(args, function check (value, key) { - if (value === undefined || value === null) return false - if (typeof value === 'function') return false - if (typeof value === 'object') return false - var contains = String(value).indexOf(query.trim()) !== -1 - return contains - }) - return isMatch + query = query.trim() + var isMatch = !!findDeep(args, function check (value, key) { + if (value === undefined || value === null) return false + if (typeof value === 'function') return false + if (typeof value === 'object') return false + var contains = String(value).indexOf(query.trim()) !== -1 + return contains + }) + return isMatch } diff --git a/apps/remix-ide/src/registry.js b/apps/remix-ide/src/registry.js index 71ae9db635..3716c0fc45 100644 --- a/apps/remix-ide/src/registry.js +++ b/apps/remix-ide/src/registry.js @@ -1,26 +1,26 @@ // const moduleID = require('./module-id.js') module.exports = class registry { - constructor () { - this.state = {} - } + constructor () { + this.state = {} + } - put ({ api, name }) { - // const serveruid = moduleID() + '.' + (name || '') - if (this.state[name]) return this.state[name] - const server = { - // uid: serveruid, - api - } - this.state[name] = { server } - return server + put ({ api, name }) { + // const serveruid = moduleID() + '.' + (name || '') + if (this.state[name]) return this.state[name] + const server = { + // uid: serveruid, + api } + this.state[name] = { server } + return server + } - get (name) { - // const clientuid = moduleID() - const state = this.state[name] - if (!state) return - const server = state.server - return server - } + get (name) { + // const clientuid = moduleID() + const state = this.state[name] + if (!state) return + const server = state.server + return server + } } diff --git a/apps/remix-ide/src/remixAppManager.js b/apps/remix-ide/src/remixAppManager.js index 9c9f365f23..53d727eee9 100644 --- a/apps/remix-ide/src/remixAppManager.js +++ b/apps/remix-ide/src/remixAppManager.js @@ -6,13 +6,13 @@ const _paq = window._paq = window._paq || [] // requiredModule removes the plugin from the plugin manager list on UI const requiredModules = [ // services + layout views + system views - 'manager', 'config', 'compilerArtefacts', 'compilerMetadata', 'contextualListener', 'editor', 'offsetToLineColumnConverter', 'network', 'theme', 'locale', - 'fileManager', 'contentImport', 'blockchain', 'web3Provider', 'scriptRunner', 'fetchAndCompile', 'mainPanel', 'hiddenPanel', 'sidePanel', 'menuicons', - 'filePanel', 'terminal', 'settings', 'pluginManager', 'tabs', 'udapp', 'dGitProvider', 'solidity', 'solidity-logic', 'gistHandler', 'layout', - 'notification', 'permissionhandler', 'walkthrough', 'storage', 'restorebackupzip', 'link-libraries', 'deploy-libraries', 'openzeppelin-proxy', - 'hardhat-provider', 'ganache-provider', 'foundry-provider', 'basic-http-provider', 'injected', 'injected-trustwallet', 'injected-optimism-provider', 'injected-arbitrum-one-provider', 'vm-custom-fork', 'vm-goerli-fork', 'vm-mainnet-fork', 'vm-sepolia-fork', 'vm-merge', 'vm-london', 'vm-berlin', - 'vm-shanghai', - 'compileAndRun', 'search', 'recorder', 'fileDecorator', 'codeParser', 'codeFormatter', 'solidityumlgen', 'contractflattener', 'solidity-script'] + 'manager', 'config', 'compilerArtefacts', 'compilerMetadata', 'contextualListener', 'editor', 'offsetToLineColumnConverter', 'network', 'theme', 'locale', + 'fileManager', 'contentImport', 'blockchain', 'web3Provider', 'scriptRunner', 'fetchAndCompile', 'mainPanel', 'hiddenPanel', 'sidePanel', 'menuicons', + 'filePanel', 'terminal', 'settings', 'pluginManager', 'tabs', 'udapp', 'dGitProvider', 'solidity', 'solidity-logic', 'gistHandler', 'layout', + 'notification', 'permissionhandler', 'walkthrough', 'storage', 'restorebackupzip', 'link-libraries', 'deploy-libraries', 'openzeppelin-proxy', + 'hardhat-provider', 'ganache-provider', 'foundry-provider', 'basic-http-provider', 'injected', 'injected-trustwallet', 'injected-optimism-provider', 'injected-arbitrum-one-provider', 'vm-custom-fork', 'vm-goerli-fork', 'vm-mainnet-fork', 'vm-sepolia-fork', 'vm-merge', 'vm-london', 'vm-berlin', + 'vm-shanghai', + 'compileAndRun', 'search', 'recorder', 'fileDecorator', 'codeParser', 'codeFormatter', 'solidityumlgen', 'contractflattener', 'solidity-script'] // dependentModules shouldn't be manually activated (e.g hardhat is activated by remixd) const dependentModules = ['foundry', 'hardhat', 'truffle', 'slither'] @@ -20,17 +20,17 @@ const dependentModules = ['foundry', 'hardhat', 'truffle', 'slither'] const loadLocalPlugins = ["doc-gen", "doc-viewer", "etherscan", "vyper", 'solhint', 'walletconnect'] const sensitiveCalls = { - 'fileManager': ['writeFile', 'copyFile', 'rename', 'copyDir'], - 'contentImport': ['resolveAndSave'], - 'web3Provider': ['sendAsync'], + 'fileManager': ['writeFile', 'copyFile', 'rename', 'copyDir'], + 'contentImport': ['resolveAndSave'], + 'web3Provider': ['sendAsync'], } export function isNative(name) { - // nativePlugin allows to bypass the permission request - const nativePlugins = ['vyper', 'workshops', 'debugger', 'remixd', 'menuicons', 'solidity', 'solidity-logic', 'solidityStaticAnalysis', 'solidityUnitTesting', - 'layout', 'notification', 'hardhat-provider', 'ganache-provider', 'foundry-provider', 'basic-http-provider', 'injected-optimism-provider', - 'tabs', 'injected-arbitrum-one-provider', 'injected', 'doc-gen', 'doc-viewer'] - return nativePlugins.includes(name) || requiredModules.includes(name) + // nativePlugin allows to bypass the permission request + const nativePlugins = ['vyper', 'workshops', 'debugger', 'remixd', 'menuicons', 'solidity', 'solidity-logic', 'solidityStaticAnalysis', 'solidityUnitTesting', + 'layout', 'notification', 'hardhat-provider', 'ganache-provider', 'foundry-provider', 'basic-http-provider', 'injected-optimism-provider', + 'tabs', 'injected-arbitrum-one-provider', 'injected', 'doc-gen', 'doc-viewer'] + return nativePlugins.includes(name) || requiredModules.includes(name) } /** @@ -44,191 +44,191 @@ export function isNative(name) { * @returns {boolean} */ export function canActivate(from, to) { - return ['ethdoc'].includes(from.name) || + return ['ethdoc'].includes(from.name) || isNative(from.name) || (to && from && from.canActivate && from.canActivate.includes(to.name)) } export class RemixAppManager extends PluginManager { - constructor() { - super() - this.event = new EventEmitter() - this.pluginsDirectory = 'https://raw.githubusercontent.com/ethereum/remix-plugins-directory/master/build/metadata.json' - this.pluginLoader = new PluginLoader() - } + constructor() { + super() + this.event = new EventEmitter() + this.pluginsDirectory = 'https://raw.githubusercontent.com/ethereum/remix-plugins-directory/master/build/metadata.json' + this.pluginLoader = new PluginLoader() + } - async canActivatePlugin(from, to) { - return canActivate(from, to) - } + async canActivatePlugin(from, to) { + return canActivate(from, to) + } - async canDeactivatePlugin(from, to) { - if (requiredModules.includes(to.name)) return false - return isNative(from.name) - } + async canDeactivatePlugin(from, to) { + if (requiredModules.includes(to.name)) return false + return isNative(from.name) + } - async canDeactivate(from, to) { - return this.canDeactivatePlugin(from, to) - } + async canDeactivate(from, to) { + return this.canDeactivatePlugin(from, to) + } - async deactivatePlugin(name) { - const profile = await this.getProfile(name) - const [to, from] = [ - profile, - await this.getProfile(this.requestFrom) - ] - if (this.canDeactivatePlugin(from, to)) { - if (profile.methods.includes('deactivate')) { - try { - await this.call(name, 'deactivate') - } catch (e) { - console.log(e) - } - } - await this.toggleActive(name) + async deactivatePlugin(name) { + const profile = await this.getProfile(name) + const [to, from] = [ + profile, + await this.getProfile(this.requestFrom) + ] + if (this.canDeactivatePlugin(from, to)) { + if (profile.methods.includes('deactivate')) { + try { + await this.call(name, 'deactivate') + } catch (e) { + console.log(e) } + } + await this.toggleActive(name) } + } - async canCall(from, to, method, message) { - const isSensitiveCall = sensitiveCalls[to] && sensitiveCalls[to].includes(method) - // Make sure the caller of this methods is the target plugin - if (to !== this.currentRequest.from) { - return false - } - // skipping native plugins' requests - if (isNative(from)) { - return true - } - - // ask the user for permission - return await this.call('permissionhandler', 'askPermission', this.profiles[from], this.profiles[to], method, message, isSensitiveCall) + async canCall(from, to, method, message) { + const isSensitiveCall = sensitiveCalls[to] && sensitiveCalls[to].includes(method) + // Make sure the caller of this methods is the target plugin + if (to !== this.currentRequest.from) { + return false } - - onPluginActivated(plugin) { - this.pluginLoader.set(plugin, this.actives.filter((plugin) => !this.isDependent(plugin))) - this.event.emit('activate', plugin) - this.emit('activate', plugin) - if (!requiredModules.includes(plugin.name)) _paq.push(['trackEvent', 'pluginManager', 'activate', plugin.name]) + // skipping native plugins' requests + if (isNative(from)) { + return true } - getAll() { - return Object.keys(this.profiles).map((p) => { - return this.profiles[p] - }) - } + // ask the user for permission + return await this.call('permissionhandler', 'askPermission', this.profiles[from], this.profiles[to], method, message, isSensitiveCall) + } - getIds() { - return Object.keys(this.profiles) - } + onPluginActivated(plugin) { + this.pluginLoader.set(plugin, this.actives.filter((plugin) => !this.isDependent(plugin))) + this.event.emit('activate', plugin) + this.emit('activate', plugin) + if (!requiredModules.includes(plugin.name)) _paq.push(['trackEvent', 'pluginManager', 'activate', plugin.name]) + } - onPluginDeactivated(plugin) { - this.pluginLoader.set(plugin, this.actives.filter((plugin) => !this.isDependent(plugin))) - this.event.emit('deactivate', plugin) - _paq.push(['trackEvent', 'pluginManager', 'deactivate', plugin.name]) - } + getAll() { + return Object.keys(this.profiles).map((p) => { + return this.profiles[p] + }) + } - isDependent(name) { - return dependentModules.includes(name) - } + getIds() { + return Object.keys(this.profiles) + } - isRequired(name) { - // excluding internal use plugins - return requiredModules.includes(name) - } + onPluginDeactivated(plugin) { + this.pluginLoader.set(plugin, this.actives.filter((plugin) => !this.isDependent(plugin))) + this.event.emit('deactivate', plugin) + _paq.push(['trackEvent', 'pluginManager', 'deactivate', plugin.name]) + } + + isDependent(name) { + return dependentModules.includes(name) + } - async registeredPlugins() { - let plugins + isRequired(name) { + // excluding internal use plugins + return requiredModules.includes(name) + } + + async registeredPlugins() { + let plugins + try { + const res = await fetch(this.pluginsDirectory) + plugins = await res.json() + plugins = plugins.filter((plugin) => { + if (plugin.targets && Array.isArray(plugin.targets) && plugin.targets.length > 0) { + return (plugin.targets.includes('remix')) + } + return true + }) + localStorage.setItem('plugins-directory', JSON.stringify(plugins)) + } catch (e) { + console.log('getting plugins list from localstorage...') + const savedPlugins = localStorage.getItem('plugins-directory') + if (savedPlugins) { try { - const res = await fetch(this.pluginsDirectory) - plugins = await res.json() - plugins = plugins.filter((plugin) => { - if (plugin.targets && Array.isArray(plugin.targets) && plugin.targets.length > 0) { - return (plugin.targets.includes('remix')) - } - return true - }) - localStorage.setItem('plugins-directory', JSON.stringify(plugins)) + plugins = JSON.parse(savedPlugins) } catch (e) { - console.log('getting plugins list from localstorage...') - const savedPlugins = localStorage.getItem('plugins-directory') - if (savedPlugins) { - try { - plugins = JSON.parse(savedPlugins) - } catch (e) { - console.error(e) - } - } - } - const testPluginName = localStorage.getItem('test-plugin-name') - const testPluginUrl = localStorage.getItem('test-plugin-url') - - for (let plugin of loadLocalPlugins) { - // fetch the profile from the local plugin - try { - const profile = await fetch(`plugins/${plugin}/profile.json`) - const profileJson = await profile.json() - // remove duplicates - plugins = plugins.filter((p) => p.name !== profileJson.name && p.displayName !== profileJson.displayName) - // change url - profileJson.url = `plugins/${plugin}/index.html` - // add the local plugin - plugins.push(profileJson) - } catch (e) { - console.log(e) - } + console.error(e) } - - return plugins.map(plugin => { - if (plugin.name === testPluginName) plugin.url = testPluginUrl - return new IframePlugin(plugin) - }) + } } + const testPluginName = localStorage.getItem('test-plugin-name') + const testPluginUrl = localStorage.getItem('test-plugin-url') - async registerContextMenuItems() { - await this.call('filePanel', 'registerContextMenuItem', { - id: 'contractflattener', - name: 'flattenAContract', - label: 'Flatten', - type: [], - extension: ['.sol'], - path: [], - pattern: [], - sticky: true, - group: 5 - }) - await this.call('filePanel', 'registerContextMenuItem', { - id: 'nahmii-compiler', - name: 'compileCustomAction', - label: 'Compile for Nahmii', - type: [], - extension: ['.sol'], - path: [], - pattern: [], - sticky: true, - group: 6 - }) - await this.call('filePanel', 'registerContextMenuItem', { - id: 'solidityumlgen', - name: 'generateCustomAction', - label: 'Generate UML', - type: [], - extension: ['.sol'], - path: [], - pattern: [], - sticky: true, - group: 7 - }) - await this.call('filePanel', 'registerContextMenuItem', { - id: 'doc-gen', - name: 'generateDocsCustomAction', - label: 'Generate Docs', - type: [], - extension: ['.sol'], - path: [], - pattern: [], - sticky: true, - group: 7 - }) + for (let plugin of loadLocalPlugins) { + // fetch the profile from the local plugin + try { + const profile = await fetch(`plugins/${plugin}/profile.json`) + const profileJson = await profile.json() + // remove duplicates + plugins = plugins.filter((p) => p.name !== profileJson.name && p.displayName !== profileJson.displayName) + // change url + profileJson.url = `plugins/${plugin}/index.html` + // add the local plugin + plugins.push(profileJson) + } catch (e) { + console.log(e) + } } + + return plugins.map(plugin => { + if (plugin.name === testPluginName) plugin.url = testPluginUrl + return new IframePlugin(plugin) + }) + } + + async registerContextMenuItems() { + await this.call('filePanel', 'registerContextMenuItem', { + id: 'contractflattener', + name: 'flattenAContract', + label: 'Flatten', + type: [], + extension: ['.sol'], + path: [], + pattern: [], + sticky: true, + group: 5 + }) + await this.call('filePanel', 'registerContextMenuItem', { + id: 'nahmii-compiler', + name: 'compileCustomAction', + label: 'Compile for Nahmii', + type: [], + extension: ['.sol'], + path: [], + pattern: [], + sticky: true, + group: 6 + }) + await this.call('filePanel', 'registerContextMenuItem', { + id: 'solidityumlgen', + name: 'generateCustomAction', + label: 'Generate UML', + type: [], + extension: ['.sol'], + path: [], + pattern: [], + sticky: true, + group: 7 + }) + await this.call('filePanel', 'registerContextMenuItem', { + id: 'doc-gen', + name: 'generateDocsCustomAction', + label: 'Generate Docs', + type: [], + extension: ['.sol'], + path: [], + pattern: [], + sticky: true, + group: 7 + }) + } } /** @class Reference loaders. @@ -236,39 +236,39 @@ export class RemixAppManager extends PluginManager { * (localStorage, queryParams) **/ class PluginLoader { - get currentLoader() { - return this.loaders[this.current] - } + get currentLoader() { + return this.loaders[this.current] + } - constructor() { - const queryParams = new QueryParams() - this.donotAutoReload = ['remixd'] // that would be a bad practice to force loading some plugins at page load. - this.loaders = {} - this.loaders.localStorage = { - set: (plugin, actives) => { - const saved = actives.filter((name) => !this.donotAutoReload.includes(name)) - localStorage.setItem('workspace', JSON.stringify(saved)) - }, - get: () => { return JSON.parse(localStorage.getItem('workspace')) } - } - - this.loaders.queryParams = { - set: () => { /* Do nothing. */ }, - get: () => { - const { activate } = queryParams.get() - if (!activate) return [] - return activate.split(',') - } - } - - this.current = queryParams.get().activate ? 'queryParams' : 'localStorage' + constructor() { + const queryParams = new QueryParams() + this.donotAutoReload = ['remixd'] // that would be a bad practice to force loading some plugins at page load. + this.loaders = {} + this.loaders.localStorage = { + set: (plugin, actives) => { + const saved = actives.filter((name) => !this.donotAutoReload.includes(name)) + localStorage.setItem('workspace', JSON.stringify(saved)) + }, + get: () => { return JSON.parse(localStorage.getItem('workspace')) } } - set(plugin, actives) { - this.currentLoader.set(plugin, actives) + this.loaders.queryParams = { + set: () => { /* Do nothing. */ }, + get: () => { + const { activate } = queryParams.get() + if (!activate) return [] + return activate.split(',') + } } - get() { - return this.currentLoader.get() - } + this.current = queryParams.get().activate ? 'queryParams' : 'localStorage' + } + + set(plugin, actives) { + this.currentLoader.set(plugin, actives) + } + + get() { + return this.currentLoader.get() + } } diff --git a/apps/remix-ide/src/remixEngine.js b/apps/remix-ide/src/remixEngine.js index 3a0c905acd..64982d6338 100644 --- a/apps/remix-ide/src/remixEngine.js +++ b/apps/remix-ide/src/remixEngine.js @@ -2,28 +2,28 @@ import { Engine } from '@remixproject/engine' import { EventEmitter } from 'events' export class RemixEngine extends Engine { - constructor () { - super() - this.event = new EventEmitter() - } + constructor () { + super() + this.event = new EventEmitter() + } - setPluginOption ({ name, kind }) { - if (kind === 'provider') return { queueTimeout: 60000 * 2 } - if (name === 'LearnEth') return { queueTimeout: 60000 } - if (name === 'dGitProvider') return { queueTimeout: 60000 * 4 } - if (name === 'slither') return { queueTimeout: 60000 * 4 } // Requires when a solc version is installed - if (name === 'hardhat') return { queueTimeout: 60000 * 4 } - if (name === 'truffle') return { queueTimeout: 60000 * 4 } - if (name === 'localPlugin') return { queueTimeout: 60000 * 4 } - if (name === 'notification') return { queueTimeout: 60000 * 10 } - if (name === 'sourcify') return { queueTimeout: 60000 * 4 } - if (name === 'fetchAndCompile') return { queueTimeout: 60000 * 4 } - if (name === 'walletconnect') return { queueTimeout: 60000 * 4 } - if (name === 'udapp') return { queueTimeout: 60000 * 4 } - return { queueTimeout: 10000 } - } + setPluginOption ({ name, kind }) { + if (kind === 'provider') return { queueTimeout: 60000 * 2 } + if (name === 'LearnEth') return { queueTimeout: 60000 } + if (name === 'dGitProvider') return { queueTimeout: 60000 * 4 } + if (name === 'slither') return { queueTimeout: 60000 * 4 } // Requires when a solc version is installed + if (name === 'hardhat') return { queueTimeout: 60000 * 4 } + if (name === 'truffle') return { queueTimeout: 60000 * 4 } + if (name === 'localPlugin') return { queueTimeout: 60000 * 4 } + if (name === 'notification') return { queueTimeout: 60000 * 10 } + if (name === 'sourcify') return { queueTimeout: 60000 * 4 } + if (name === 'fetchAndCompile') return { queueTimeout: 60000 * 4 } + if (name === 'walletconnect') return { queueTimeout: 60000 * 4 } + if (name === 'udapp') return { queueTimeout: 60000 * 4 } + return { queueTimeout: 10000 } + } - onRegistration (plugin) { - this.event.emit('onRegistration', plugin) - } + onRegistration (plugin) { + this.event.emit('onRegistration', plugin) + } } diff --git a/apps/remix-ide/src/walkthroughService.js b/apps/remix-ide/src/walkthroughService.js index d0d466555f..4c81b0cde9 100644 --- a/apps/remix-ide/src/walkthroughService.js +++ b/apps/remix-ide/src/walkthroughService.js @@ -3,64 +3,64 @@ import * as packageJson from '../../../package.json' const introJs = require('intro.js') const profile = { - name: 'walkthrough', - displayName: 'Walkthrough', - description: 'Remix walkthrough for beginner', - version: packageJson.version, - methods: ['start'] + name: 'walkthrough', + displayName: 'Walkthrough', + description: 'Remix walkthrough for beginner', + version: packageJson.version, + methods: ['start'] } export class WalkthroughService extends Plugin { - constructor (appManager, showMatamo) { - super(profile) - appManager.event.on('activate', (plugin) => { - if (plugin.name === 'udapp' && !showMatamo) { - this.start() - } - }) - } + constructor (appManager, showMatamo) { + super(profile) + appManager.event.on('activate', (plugin) => { + if (plugin.name === 'udapp' && !showMatamo) { + this.start() + } + }) + } - start () { - if (!localStorage.getItem('hadTour_initial')) { - introJs().setOptions({ - steps: [{ - title: 'Welcome to Remix IDE', - intro: 'Click to launch the Home tab that contains links, tips, and shortcuts..', - element: document.querySelector('#verticalIconsHomeIcon'), - tooltipClass: 'bg-light text-dark', - position: 'right' - }, - { - element: document.querySelector('#verticalIconsKindsolidity'), - title: 'Solidity Compiler', - intro: 'Having selected a .sol file in the File Explorer (the icon above), compile it with the Solidity Compiler.', - tooltipClass: 'bg-light text-dark', - position: 'right' - }, - { - title: 'Deploy your contract', - element: document.querySelector('#verticalIconsKindudapp'), - intro: 'Choose a chain, deploy a contract and play with your functions.', - tooltipClass: 'bg-light text-dark', - position: 'right' - } - ] - }).onafterchange((targetElement) => { - const header = document.getElementsByClassName('introjs-tooltip-header')[0] - if (header) { - header.classList.add('d-flex') - header.classList.add('justify-content-between') - header.classList.add('text-nowrap') - header.classList.add('pr-0') - } - const skipbutton = document.getElementsByClassName('introjs-skipbutton')[0] - if (skipbutton) { - skipbutton.classList.add('ml-3') - skipbutton.classList.add('text-decoration-none') - skipbutton.id = 'remixTourSkipbtn' - } - }).start() - localStorage.setItem('hadTour_initial', true) + start () { + if (!localStorage.getItem('hadTour_initial')) { + introJs().setOptions({ + steps: [{ + title: 'Welcome to Remix IDE', + intro: 'Click to launch the Home tab that contains links, tips, and shortcuts..', + element: document.querySelector('#verticalIconsHomeIcon'), + tooltipClass: 'bg-light text-dark', + position: 'right' + }, + { + element: document.querySelector('#verticalIconsKindsolidity'), + title: 'Solidity Compiler', + intro: 'Having selected a .sol file in the File Explorer (the icon above), compile it with the Solidity Compiler.', + tooltipClass: 'bg-light text-dark', + position: 'right' + }, + { + title: 'Deploy your contract', + element: document.querySelector('#verticalIconsKindudapp'), + intro: 'Choose a chain, deploy a contract and play with your functions.', + tooltipClass: 'bg-light text-dark', + position: 'right' + } + ] + }).onafterchange((targetElement) => { + const header = document.getElementsByClassName('introjs-tooltip-header')[0] + if (header) { + header.classList.add('d-flex') + header.classList.add('justify-content-between') + header.classList.add('text-nowrap') + header.classList.add('pr-0') + } + const skipbutton = document.getElementsByClassName('introjs-skipbutton')[0] + if (skipbutton) { + skipbutton.classList.add('ml-3') + skipbutton.classList.add('text-decoration-none') + skipbutton.id = 'remixTourSkipbtn' } + }).start() + localStorage.setItem('hadTour_initial', true) } + } } diff --git a/apps/remix-ide/webpack.config.js b/apps/remix-ide/webpack.config.js index b34b7dcb11..fd53dc8b2d 100644 --- a/apps/remix-ide/webpack.config.js +++ b/apps/remix-ide/webpack.config.js @@ -9,16 +9,16 @@ const CssMinimizerPlugin = require("css-minimizer-webpack-plugin") const axios = require('axios') const versionData = { - version: version, - timestamp: Date.now(), - mode: process.env.NODE_ENV === 'production' ? 'production' : 'development' + version: version, + timestamp: Date.now(), + mode: process.env.NODE_ENV === 'production' ? 'production' : 'development' } const loadLocalSolJson = async () => { - // execute apps/remix-ide/ci/downloadsoljson.sh - const child = require('child_process').execSync('bash ./apps/remix-ide/ci/downloadsoljson.sh', { encoding: 'utf8', cwd: process.cwd(), shell: true }) - // show output - console.log(child) + // execute apps/remix-ide/ci/downloadsoljson.sh + const child = require('child_process').execSync('bash ./apps/remix-ide/ci/downloadsoljson.sh', { encoding: 'utf8', cwd: process.cwd(), shell: true }) + // show output + console.log(child) } fs.writeFileSync('./apps/remix-ide/src/assets/version.json', JSON.stringify(versionData)) @@ -30,129 +30,129 @@ const project = fs.readFileSync('./apps/remix-ide/project.json', 'utf8') const implicitDependencies = JSON.parse(project).implicitDependencies const copyPatterns = implicitDependencies.map((dep) => { - try { - fs.statSync(__dirname + `/../../dist/apps/${dep}`).isDirectory() - return { from: `../../dist/apps/${dep}`, to: `plugins/${dep}` } - } - catch (e) { - console.log('error', e) - return false - } + try { + fs.statSync(__dirname + `/../../dist/apps/${dep}`).isDirectory() + return { from: `../../dist/apps/${dep}`, to: `plugins/${dep}` } + } + catch (e) { + console.log('error', e) + return false + } }) console.log('Copying plugins... ', copyPatterns) // Nx plugins for webpack. module.exports = composePlugins(withNx(), withReact(), (config) => { - // Update the webpack config as needed here. - // e.g. `config.plugins.push(new MyPlugin())` - - // add fallback for node modules - config.resolve.fallback = { - ...config.resolve.fallback, - "crypto": require.resolve("crypto-browserify"), - "stream": require.resolve("stream-browserify"), - "path": require.resolve("path-browserify"), - "http": require.resolve("stream-http"), - "https": require.resolve("https-browserify"), - "constants": require.resolve("constants-browserify"), - "os": false, //require.resolve("os-browserify/browser"), - "timers": false, // require.resolve("timers-browserify"), - "zlib": require.resolve("browserify-zlib"), - "fs": false, - "module": false, - "tls": false, - "net": false, - "readline": false, - "child_process": false, - "buffer": require.resolve("buffer/"), - "vm": require.resolve('vm-browserify'), - } - - - // add externals - config.externals = { - ...config.externals, - solc: 'solc', - } - - // add public path - config.output.publicPath = '/' - - // set filename - config.output.filename = `[name].${versionData.version}.${versionData.timestamp}.js` - config.output.chunkFilename = `[name].${versionData.version}.${versionData.timestamp}.js` - - - // add copy & provide plugin - config.plugins.push( - new CopyPlugin({ - patterns: [ - { from: '../../node_modules/monaco-editor/min/vs', to: 'assets/js/monaco-editor/min/vs' }, - ...copyPatterns - ].filter(Boolean) - }), - new CopyFileAfterBuild(), - new webpack.ProvidePlugin({ - Buffer: ['buffer', 'Buffer'], - url: ['url', 'URL'], - process: 'process/browser', - }) - ) - - // souce-map loader - config.module.rules.push({ - test: /\.js$/, - use: ["source-map-loader"], - enforce: "pre" + // Update the webpack config as needed here. + // e.g. `config.plugins.push(new MyPlugin())` + + // add fallback for node modules + config.resolve.fallback = { + ...config.resolve.fallback, + "crypto": require.resolve("crypto-browserify"), + "stream": require.resolve("stream-browserify"), + "path": require.resolve("path-browserify"), + "http": require.resolve("stream-http"), + "https": require.resolve("https-browserify"), + "constants": require.resolve("constants-browserify"), + "os": false, //require.resolve("os-browserify/browser"), + "timers": false, // require.resolve("timers-browserify"), + "zlib": require.resolve("browserify-zlib"), + "fs": false, + "module": false, + "tls": false, + "net": false, + "readline": false, + "child_process": false, + "buffer": require.resolve("buffer/"), + "vm": require.resolve('vm-browserify'), + } + + + // add externals + config.externals = { + ...config.externals, + solc: 'solc', + } + + // add public path + config.output.publicPath = '/' + + // set filename + config.output.filename = `[name].${versionData.version}.${versionData.timestamp}.js` + config.output.chunkFilename = `[name].${versionData.version}.${versionData.timestamp}.js` + + + // add copy & provide plugin + config.plugins.push( + new CopyPlugin({ + patterns: [ + { from: '../../node_modules/monaco-editor/min/vs', to: 'assets/js/monaco-editor/min/vs' }, + ...copyPatterns + ].filter(Boolean) + }), + new CopyFileAfterBuild(), + new webpack.ProvidePlugin({ + Buffer: ['buffer', 'Buffer'], + url: ['url', 'URL'], + process: 'process/browser', }) - - config.ignoreWarnings = [/Failed to parse source map/, /require function/] // ignore source-map-loader warnings & AST warnings - - // set minimizer - config.optimization.minimizer = [ - new TerserPlugin({ - parallel: true, - terserOptions: { - ecma: 2015, - compress: false, - mangle: false, - format: { - comments: false, - }, - }, - extractComments: false, - }), - new CssMinimizerPlugin(), - ]; - - config.watchOptions = { - ignored: /node_modules/ - } - - return config; + ) + + // souce-map loader + config.module.rules.push({ + test: /\.js$/, + use: ["source-map-loader"], + enforce: "pre" + }) + + config.ignoreWarnings = [/Failed to parse source map/, /require function/] // ignore source-map-loader warnings & AST warnings + + // set minimizer + config.optimization.minimizer = [ + new TerserPlugin({ + parallel: true, + terserOptions: { + ecma: 2015, + compress: false, + mangle: false, + format: { + comments: false, + }, + }, + extractComments: false, + }), + new CssMinimizerPlugin(), + ]; + + config.watchOptions = { + ignored: /node_modules/ + } + + return config; }); class CopyFileAfterBuild { - apply(compiler) { - const onEnd = async () => { - try { - console.log('runnning CopyFileAfterBuild') - // This copy the raw-loader files used by the etherscan plugin to the remix-ide root folder. - // This is needed because by default the etherscan resources are served from the /plugins/etherscan/ folder, - // but the raw-loader try to access the resources from the root folder. - const files = fs.readdirSync('./dist/apps/etherscan') - files.forEach(file => { - if (file.includes('plugin-etherscan')) { - fs.copyFileSync('./dist/apps/etherscan/' + file, './dist/apps/remix-ide/' + file) - } - }) - } catch (e) { - console.error('running CopyFileAfterBuild failed with error: ' + e.message) - } - } - compiler.hooks.afterEmit.tapPromise('FileManagerPlugin', onEnd); + apply(compiler) { + const onEnd = async () => { + try { + console.log('runnning CopyFileAfterBuild') + // This copy the raw-loader files used by the etherscan plugin to the remix-ide root folder. + // This is needed because by default the etherscan resources are served from the /plugins/etherscan/ folder, + // but the raw-loader try to access the resources from the root folder. + const files = fs.readdirSync('./dist/apps/etherscan') + files.forEach(file => { + if (file.includes('plugin-etherscan')) { + fs.copyFileSync('./dist/apps/etherscan/' + file, './dist/apps/remix-ide/' + file) + } + }) + } catch (e) { + console.error('running CopyFileAfterBuild failed with error: ' + e.message) + } } + compiler.hooks.afterEmit.tapPromise('FileManagerPlugin', onEnd); + } } diff --git a/libs/ghaction-helper/src/artifacts-helper.ts b/libs/ghaction-helper/src/artifacts-helper.ts index 3cc02ff9b5..35d15085f3 100644 --- a/libs/ghaction-helper/src/artifacts-helper.ts +++ b/libs/ghaction-helper/src/artifacts-helper.ts @@ -4,18 +4,18 @@ import * as fs from 'fs/promises' import * as path from 'path' export async function getArtifactsByContractName (contractIdentifier: string) { - //@ts-ignore - const contractArtifacts = await fs.readdir(global.remixContractArtifactsPath) - let contract + //@ts-ignore + const contractArtifacts = await fs.readdir(global.remixContractArtifactsPath) + let contract - for (const artifactFile of contractArtifacts) { - //@ts-ignore - const artifact = await fs.readFile(path.join(global.remixContractArtifactsPath, artifactFile), 'utf-8') - const artifactJSON: CompilationResult = JSON.parse(artifact) - const contractFullPath = (Object.keys(artifactJSON.contracts!)).find((contractName) => artifactJSON.contracts![contractName] && artifactJSON.contracts![contractName][contractIdentifier]) + for (const artifactFile of contractArtifacts) { + //@ts-ignore + const artifact = await fs.readFile(path.join(global.remixContractArtifactsPath, artifactFile), 'utf-8') + const artifactJSON: CompilationResult = JSON.parse(artifact) + const contractFullPath = (Object.keys(artifactJSON.contracts!)).find((contractName) => artifactJSON.contracts![contractName] && artifactJSON.contracts![contractName][contractIdentifier]) - contract = contractFullPath ? artifactJSON.contracts![contractFullPath!][contractIdentifier] : undefined - if (contract) break - } - return contract + contract = contractFullPath ? artifactJSON.contracts![contractFullPath!][contractIdentifier] : undefined + if (contract) break + } + return contract } diff --git a/libs/ghaction-helper/src/methods.ts b/libs/ghaction-helper/src/methods.ts index b6558f4eb0..6d64a9cb7a 100644 --- a/libs/ghaction-helper/src/methods.ts +++ b/libs/ghaction-helper/src/methods.ts @@ -6,9 +6,9 @@ import { SignerWithAddress } from './signer' import Web3 from "web3" const providerConfig = { - fork: global.fork || null, - nodeUrl: global.nodeUrl || null, - blockNumber: global.blockNumber || null + fork: global.fork || null, + nodeUrl: global.nodeUrl || null, + blockNumber: global.blockNumber || null } global.remixProvider = new Provider(providerConfig) @@ -19,245 +19,245 @@ global.ethereum = global.web3Provider global.web3 = new Web3(global.web3Provider) const isFactoryOptions = (signerOrOptions: any) => { - if (!signerOrOptions || signerOrOptions === undefined || signerOrOptions instanceof ethers.Signer) return false - return true + if (!signerOrOptions || signerOrOptions === undefined || signerOrOptions instanceof ethers.Signer) return false + return true } const isArtifact = (artifact: any) => { - const { - contractName, - sourceName, - abi, - bytecode, - deployedBytecode, - linkReferences, - deployedLinkReferences, - } = artifact - - return ( - typeof contractName === "string" && + const { + contractName, + sourceName, + abi, + bytecode, + deployedBytecode, + linkReferences, + deployedLinkReferences, + } = artifact + + return ( + typeof contractName === "string" && typeof sourceName === "string" && Array.isArray(abi) && typeof bytecode === "string" && typeof deployedBytecode === "string" && linkReferences !== undefined && deployedLinkReferences !== undefined - ) + ) } function linkBytecode(artifact: any, libraries: any) { - let bytecode = artifact.bytecode + let bytecode = artifact.bytecode - for (const { sourceName, libraryName, address } of libraries) { - const linkReferences = artifact.linkReferences[sourceName][libraryName] + for (const { sourceName, libraryName, address } of libraries) { + const linkReferences = artifact.linkReferences[sourceName][libraryName] - for (const { start, length } of linkReferences) { - bytecode = + for (const { start, length } of linkReferences) { + bytecode = bytecode.substr(0, 2 + start * 2) + address.substr(2) + bytecode.substr(2 + (start + length) * 2) - } } + } - return bytecode + return bytecode } const collectLibrariesAndLink = async (artifact: any, libraries: any) => { - const neededLibraries = [] - for (const [sourceName, sourceLibraries] of Object.entries(artifact.linkReferences)) { - // @ts-ignore - for (const libName of Object.keys(sourceLibraries)) { - neededLibraries.push({ sourceName, libName }) - } + const neededLibraries = [] + for (const [sourceName, sourceLibraries] of Object.entries(artifact.linkReferences)) { + // @ts-ignore + for (const libName of Object.keys(sourceLibraries)) { + neededLibraries.push({ sourceName, libName }) + } + } + + const linksToApply = new Map() + for (const [linkedLibraryName, linkedLibraryAddress] of Object.entries(libraries)) { + // @ts-ignore + if (!ethers.utils.isAddress(linkedLibraryAddress)) { + throw new Error( + `You tried to link the contract ${artifact.contractName} with the library ${linkedLibraryName}, but provided this invalid address: ${linkedLibraryAddress}` + ) } - const linksToApply = new Map() - for (const [linkedLibraryName, linkedLibraryAddress] of Object.entries(libraries)) { - // @ts-ignore - if (!ethers.utils.isAddress(linkedLibraryAddress)) { - throw new Error( - `You tried to link the contract ${artifact.contractName} with the library ${linkedLibraryName}, but provided this invalid address: ${linkedLibraryAddress}` - ) - } - - const matchingNeededLibraries = neededLibraries.filter((lib) => { - return ( - lib.libName === linkedLibraryName || + const matchingNeededLibraries = neededLibraries.filter((lib) => { + return ( + lib.libName === linkedLibraryName || `${lib.sourceName}:${lib.libName}` === linkedLibraryName - ) - }) - - if (matchingNeededLibraries.length === 0) { - let detailedMessage - if (neededLibraries.length > 0) { - const libraryFQNames = neededLibraries - .map((lib) => `${lib.sourceName}:${lib.libName}`) - .map((x) => `* ${x}`) - .join("\n") - detailedMessage = `The libraries needed are: + ) + }) + + if (matchingNeededLibraries.length === 0) { + let detailedMessage + if (neededLibraries.length > 0) { + const libraryFQNames = neededLibraries + .map((lib) => `${lib.sourceName}:${lib.libName}`) + .map((x) => `* ${x}`) + .join("\n") + detailedMessage = `The libraries needed are: ${libraryFQNames}` - } else { - detailedMessage = "This contract doesn't need linking any libraries." - } - throw new Error( - `You tried to link the contract ${artifact.contractName} with ${linkedLibraryName}, which is not one of its libraries. + } else { + detailedMessage = "This contract doesn't need linking any libraries." + } + throw new Error( + `You tried to link the contract ${artifact.contractName} with ${linkedLibraryName}, which is not one of its libraries. ${detailedMessage}` - ) - } - - if (matchingNeededLibraries.length > 1) { - const matchingNeededLibrariesFQNs = matchingNeededLibraries - .map(({ sourceName, libName }) => `${sourceName}:${libName}`) - .map((x) => `* ${x}`) - .join("\n") - throw new Error( - `The library name ${linkedLibraryName} is ambiguous for the contract ${artifact.contractName}. + ) + } + + if (matchingNeededLibraries.length > 1) { + const matchingNeededLibrariesFQNs = matchingNeededLibraries + .map(({ sourceName, libName }) => `${sourceName}:${libName}`) + .map((x) => `* ${x}`) + .join("\n") + throw new Error( + `The library name ${linkedLibraryName} is ambiguous for the contract ${artifact.contractName}. It may resolve to one of the following libraries: ${matchingNeededLibrariesFQNs} To fix this, choose one of these fully qualified library names and replace where appropriate.` - ) - } + ) + } - const [neededLibrary] = matchingNeededLibraries + const [neededLibrary] = matchingNeededLibraries - const neededLibraryFQN = `${neededLibrary.sourceName}:${neededLibrary.libName}` + const neededLibraryFQN = `${neededLibrary.sourceName}:${neededLibrary.libName}` - // The only way for this library to be already mapped is - // for it to be given twice in the libraries user input: - // once as a library name and another as a fully qualified library name. - if (linksToApply.has(neededLibraryFQN)) { - throw new Error( - `The library names ${neededLibrary.libName} and ${neededLibraryFQN} refer to the same library and were given as two separate library links. + // The only way for this library to be already mapped is + // for it to be given twice in the libraries user input: + // once as a library name and another as a fully qualified library name. + if (linksToApply.has(neededLibraryFQN)) { + throw new Error( + `The library names ${neededLibrary.libName} and ${neededLibraryFQN} refer to the same library and were given as two separate library links. Remove one of them and review your library links before proceeding.` - ) - } - - linksToApply.set(neededLibraryFQN, { - sourceName: neededLibrary.sourceName, - libraryName: neededLibrary.libName, - address: linkedLibraryAddress, - }) + ) } - if (linksToApply.size < neededLibraries.length) { - const missingLibraries = neededLibraries - .map((lib) => `${lib.sourceName}:${lib.libName}`) - .filter((libFQName) => !linksToApply.has(libFQName)) - .map((x) => `* ${x}`) - .join("\n") - - throw new Error( - `The contract ${artifact.contractName} is missing links for the following libraries: + linksToApply.set(neededLibraryFQN, { + sourceName: neededLibrary.sourceName, + libraryName: neededLibrary.libName, + address: linkedLibraryAddress, + }) + } + + if (linksToApply.size < neededLibraries.length) { + const missingLibraries = neededLibraries + .map((lib) => `${lib.sourceName}:${lib.libName}`) + .filter((libFQName) => !linksToApply.has(libFQName)) + .map((x) => `* ${x}`) + .join("\n") + + throw new Error( + `The contract ${artifact.contractName} is missing links for the following libraries: ${missingLibraries}` - ) - } + ) + } - // @ts-ignore - return linkBytecode(artifact, [...linksToApply.values()]) + // @ts-ignore + return linkBytecode(artifact, [...linksToApply.values()]) } // Convert output.contracts.. in Artifact object compatible form const resultToArtifact = (result: any) => { - const { fullyQualifiedName, artefact } = result - return { - contractName: fullyQualifiedName.split(':')[1], - sourceName: fullyQualifiedName.split(':')[0], - abi: artefact.abi, - bytecode: artefact.evm.bytecode.object, - deployedBytecode: artefact.evm.deployedBytecode.object, - linkReferences: artefact.evm.bytecode.linkReferences, - deployedLinkReferences: artefact.evm.deployedBytecode.linkReferences - } + const { fullyQualifiedName, artefact } = result + return { + contractName: fullyQualifiedName.split(':')[1], + sourceName: fullyQualifiedName.split(':')[0], + abi: artefact.abi, + bytecode: artefact.evm.bytecode.object, + deployedBytecode: artefact.evm.deployedBytecode.object, + linkReferences: artefact.evm.bytecode.linkReferences, + deployedLinkReferences: artefact.evm.deployedBytecode.linkReferences + } } const getContractFactory = async (contractNameOrABI: ethers.ContractInterface, bytecode?: string, signerOrOptions = null) => { - if (bytecode && contractNameOrABI) { - //@ts-ignore - return new ethers.ContractFactory(contractNameOrABI, bytecode, signerOrOptions || web3Provider.getSigner()) - } else if (typeof contractNameOrABI === 'string') { - const contract = await getArtifactsByContractName(contractNameOrABI) - - if (contract) { - //@ts-ignore - return new ethers.ContractFactory(contract.abi, contract.evm.bytecode.object, signerOrOptions || web3Provider.getSigner()) - } else { - throw new Error('Contract artifacts not found') - } + if (bytecode && contractNameOrABI) { + //@ts-ignore + return new ethers.ContractFactory(contractNameOrABI, bytecode, signerOrOptions || web3Provider.getSigner()) + } else if (typeof contractNameOrABI === 'string') { + const contract = await getArtifactsByContractName(contractNameOrABI) + + if (contract) { + //@ts-ignore + return new ethers.ContractFactory(contract.abi, contract.evm.bytecode.object, signerOrOptions || web3Provider.getSigner()) } else { - throw new Error('Invalid contract name or ABI provided') + throw new Error('Contract artifacts not found') } + } else { + throw new Error('Invalid contract name or ABI provided') + } } const getContractAt = async (contractNameOrABI: ethers.ContractInterface, address: string, signer = null) => { - //@ts-ignore - const provider = web3Provider + //@ts-ignore + const provider = web3Provider - if(typeof contractNameOrABI === 'string') { - const result = await getArtifactsByContractName(contractNameOrABI) + if(typeof contractNameOrABI === 'string') { + const result = await getArtifactsByContractName(contractNameOrABI) - if (result) { - return new ethers.Contract(address, result.abi, signer || provider.getSigner()) - } else { - throw new Error('Contract artifacts not found') - } + if (result) { + return new ethers.Contract(address, result.abi, signer || provider.getSigner()) } else { - return new ethers.Contract(address, contractNameOrABI, signer || provider.getSigner()) + throw new Error('Contract artifacts not found') } + } else { + return new ethers.Contract(address, contractNameOrABI, signer || provider.getSigner()) + } } const getSigner = async (address: string) => { - //@ts-ignore - const provider = web3Provider - const signer = provider.getSigner(address) + //@ts-ignore + const provider = web3Provider + const signer = provider.getSigner(address) - return SignerWithAddress.create(signer) + return SignerWithAddress.create(signer) } const getSigners = async () => { - //@ts-ignore - const provider = web3Provider - const accounts = await provider.listAccounts() + //@ts-ignore + const provider = web3Provider + const accounts = await provider.listAccounts() - return await Promise.all( accounts.map((account: any) => getSigner(account))) + return await Promise.all( accounts.map((account: any) => getSigner(account))) } const getContractFactoryFromArtifact = async (artifact: any, signerOrOptions: { signer: any, libraries: any }) => { - let libraries = {} - let signer - - if (!isArtifact(artifact)) { - throw new Error( - `You are trying to create a contract factory from an artifact, but you have not passed a valid artifact parameter.` - ) - } - - if (isFactoryOptions(signerOrOptions)) { - signer = signerOrOptions.signer; - libraries = signerOrOptions.libraries ?? {}; - } else { - signer = signerOrOptions; - } + let libraries = {} + let signer - if (artifact.bytecode === "0x") { - throw new Error( - `You are trying to create a contract factory for the contract ${artifact.contractName}, which is abstract and can't be deployed. + if (!isArtifact(artifact)) { + throw new Error( + `You are trying to create a contract factory from an artifact, but you have not passed a valid artifact parameter.` + ) + } + + if (isFactoryOptions(signerOrOptions)) { + signer = signerOrOptions.signer; + libraries = signerOrOptions.libraries ?? {}; + } else { + signer = signerOrOptions; + } + + if (artifact.bytecode === "0x") { + throw new Error( + `You are trying to create a contract factory for the contract ${artifact.contractName}, which is abstract and can't be deployed. If you want to call a contract using ${artifact.contractName} as its interface use the "getContractAt" function instead.` - ) - } + ) + } - const linkedBytecode = await collectLibrariesAndLink(artifact, libraries) - //@ts-ignore - return new ethers.ContractFactory(artifact.abi, linkedBytecode || artifact.bytecode, signer || web3Provider.getSigner()) + const linkedBytecode = await collectLibrariesAndLink(artifact, libraries) + //@ts-ignore + return new ethers.ContractFactory(artifact.abi, linkedBytecode || artifact.bytecode, signer || web3Provider.getSigner()) } const getContractAtFromArtifact = async (artifact: any, address: string, signerOrOptions = null) => { - if (!isArtifact(artifact)) { - throw new Error( - `You are trying to create a contract factory from an artifact, but you have not passed a valid artifact parameter.` - ) - } + if (!isArtifact(artifact)) { + throw new Error( + `You are trying to create a contract factory from an artifact, but you have not passed a valid artifact parameter.` + ) + } - return await getContractAt(artifact.abi, address, signerOrOptions) + return await getContractAt(artifact.abi, address, signerOrOptions) } export { getContractAtFromArtifact, getContractFactoryFromArtifact, getSigners, getSigner, getContractAt, getContractFactory } diff --git a/libs/ghaction-helper/src/signer.ts b/libs/ghaction-helper/src/signer.ts index ff5626b566..7a74ead6e9 100644 --- a/libs/ghaction-helper/src/signer.ts +++ b/libs/ghaction-helper/src/signer.ts @@ -2,8 +2,8 @@ import { ethers } from "ethers" export class SignerWithAddress extends ethers.Signer { - address: string - _signer: { + address: string + _signer: { provider: any signTransaction: (transaction: any) => any, signMessage: (message: string) => any, @@ -11,43 +11,43 @@ export class SignerWithAddress extends ethers.Signer { connect: (provider: any) => any, _signTypedData: (...params: any) => any } - provider: any - static async create(signer: any) { - return new SignerWithAddress(await signer.getAddress(), signer) - } - - constructor(address: string, _signer: any) { - super() - this.address = address - this._signer = _signer - this.provider = _signer.provider - } - - async getAddress() { - return this.address - } - - signMessage(message: string){ - return this._signer.signMessage(message) - } - - signTransaction(transaction: any) { - return this._signer.signTransaction(transaction) - } - - sendTransaction(transaction: any) { - return this._signer.sendTransaction(transaction) - } - - connect(provider: any) { - return new SignerWithAddress(this.address, this._signer.connect(provider)) - } - - _signTypedData(...params: any) { - return this._signer._signTypedData(...params) - } - - toJSON() { - return `` - } + provider: any + static async create(signer: any) { + return new SignerWithAddress(await signer.getAddress(), signer) + } + + constructor(address: string, _signer: any) { + super() + this.address = address + this._signer = _signer + this.provider = _signer.provider + } + + async getAddress() { + return this.address + } + + signMessage(message: string){ + return this._signer.signMessage(message) + } + + signTransaction(transaction: any) { + return this._signer.signTransaction(transaction) + } + + sendTransaction(transaction: any) { + return this._signer.sendTransaction(transaction) + } + + connect(provider: any) { + return new SignerWithAddress(this.address, this._signer.connect(provider)) + } + + _signTypedData(...params: any) { + return this._signer._signTypedData(...params) + } + + toJSON() { + return `` + } } \ No newline at end of file diff --git a/libs/remix-analyzer/src/solidity-analyzer/index.ts b/libs/remix-analyzer/src/solidity-analyzer/index.ts index d2c45ff635..defba294cc 100644 --- a/libs/remix-analyzer/src/solidity-analyzer/index.ts +++ b/libs/remix-analyzer/src/solidity-analyzer/index.ts @@ -9,68 +9,68 @@ type ModuleObj = { } export default class staticAnalysisRunner { - /** + /** * Run analysis (Used by IDE) * @param compilationResult contract compilation result * @param toRun module indexes (compiled from remix IDE) * @param callback callback */ - run (compilationResult: CompilationResult, toRun: number[]): AnalysisReport[] { - const modules: ModuleObj[] = toRun.map((i) => { - const Module = this.modules()[i] - const m = new Module() - return { name: m.name, mod: m } - }) - return this.runWithModuleList(compilationResult, modules) - } + run (compilationResult: CompilationResult, toRun: number[]): AnalysisReport[] { + const modules: ModuleObj[] = toRun.map((i) => { + const Module = this.modules()[i] + const m = new Module() + return { name: m.name, mod: m } + }) + return this.runWithModuleList(compilationResult, modules) + } - /** + /** * Run analysis passing list of modules to run * @param compilationResult contract compilation result * @param modules analysis module * @param callback callback */ - runWithModuleList (compilationResult: CompilationResult, modules: ModuleObj[]): AnalysisReport[] { - let reports: AnalysisReport[] = [] - // Also provide convenience analysis via the AST walker. - const walker = new AstWalker() - for (const k in compilationResult.sources) { - walker.walkFull(compilationResult.sources[k].ast, - (node: any) => { - modules.map((item: ModuleObj) => { - if (item.mod.visit !== undefined) { - try { - item.mod.visit(node) - } catch (e) { - reports.push({ - name: item.name, report: [{ warning: 'INTERNAL ERROR in module ' + item.name + ' ' + e.message, error: e.stack }] - }) - } - } - }) - return true - } - ) - } - - // Here, modules can just collect the results from the AST walk, - // but also perform new analysis. - reports = reports.concat(modules.map((item: ModuleObj) => { - let report: AnalysisReportObj[] | null = null - try { - report = item.mod.report(compilationResult) - } catch (e) { - report = [{ warning: 'INTERNAL ERROR in module ' + item.name + ' ' + e.message, error: e.stack }] + runWithModuleList (compilationResult: CompilationResult, modules: ModuleObj[]): AnalysisReport[] { + let reports: AnalysisReport[] = [] + // Also provide convenience analysis via the AST walker. + const walker = new AstWalker() + for (const k in compilationResult.sources) { + walker.walkFull(compilationResult.sources[k].ast, + (node: any) => { + modules.map((item: ModuleObj) => { + if (item.mod.visit !== undefined) { + try { + item.mod.visit(node) + } catch (e) { + reports.push({ + name: item.name, report: [{ warning: 'INTERNAL ERROR in module ' + item.name + ' ' + e.message, error: e.stack }] + }) + } } - return { name: item.name, report: report } - })) - return reports + }) + return true + } + ) } - /** + // Here, modules can just collect the results from the AST walk, + // but also perform new analysis. + reports = reports.concat(modules.map((item: ModuleObj) => { + let report: AnalysisReportObj[] | null = null + try { + report = item.mod.report(compilationResult) + } catch (e) { + report = [{ warning: 'INTERNAL ERROR in module ' + item.name + ' ' + e.message, error: e.stack }] + } + return { name: item.name, report: report } + })) + return reports + } + + /** * Get list of all analysis modules */ - modules (): any[] { - return list - } + modules (): any[] { + return list + } } diff --git a/libs/remix-analyzer/src/solidity-analyzer/modules/abstractAstView.ts b/libs/remix-analyzer/src/solidity-analyzer/modules/abstractAstView.ts index 0febc41dc1..36d3f479d1 100644 --- a/libs/remix-analyzer/src/solidity-analyzer/modules/abstractAstView.ts +++ b/libs/remix-analyzer/src/solidity-analyzer/modules/abstractAstView.ts @@ -1,23 +1,23 @@ import { - getStateVariableDeclarationsFromContractNode, getInheritsFromName, getContractName, - getFunctionOrModifierDefinitionParameterPart, getType, getDeclaredVariableName, - getFunctionDefinitionReturnParameterPart, getCompilerVersion + getStateVariableDeclarationsFromContractNode, getInheritsFromName, getContractName, + getFunctionOrModifierDefinitionParameterPart, getType, getDeclaredVariableName, + getFunctionDefinitionReturnParameterPart, getCompilerVersion } from './staticAnalysisCommon' import { AstWalker } from '@remix-project/remix-astwalker' import { - FunctionDefinitionAstNode, ParameterListAstNode, ModifierDefinitionAstNode, ContractHLAst, VariableDeclarationAstNode, - FunctionHLAst, ReportObj, ReportFunction, VisitFunction, ModifierHLAst, CompilationResult + FunctionDefinitionAstNode, ParameterListAstNode, ModifierDefinitionAstNode, ContractHLAst, VariableDeclarationAstNode, + FunctionHLAst, ReportObj, ReportFunction, VisitFunction, ModifierHLAst, CompilationResult } from '../../types' type WrapFunction = ((contracts: ContractHLAst[], isSameName: boolean, version: string) => ReportObj[]) export default class abstractAstView { - contracts: ContractHLAst[] = [] - currentContractIndex = -1 - currentFunctionIndex = -1 - currentModifierIndex = -1 - isFunctionNotModifier = false - /* + contracts: ContractHLAst[] = [] + currentContractIndex = -1 + currentFunctionIndex = -1 + currentModifierIndex = -1 + isFunctionNotModifier = false + /* file1: contract c{} file2: import "file1" as x; contract c{} therefore we have two contracts with the same name c. At the moment this is not handled because alias name "x" is not @@ -25,9 +25,9 @@ export default class abstractAstView { Additionally the fullQuallified function names e.g. [contractName].[functionName](param1Type, param2Type, ... ) must be prefixed to fully support this and when inheritance is resolved it must include alias resolving e.g x.c = file1.c */ - multipleContractsWithSameName = false + multipleContractsWithSameName = false - /** + /** * Builds a higher level AST view. I creates a list with each contract as an object in it. * Example contractsOut: * @@ -52,139 +52,139 @@ export default class abstractAstView { * @contractsOut {list} return list for high level AST view * @return {ASTNode -> void} returns a function that can be used as visit function for static analysis modules, to build up a higher level AST view for further analysis. */ - // eslint-disable-next-line camelcase - build_visit (relevantNodeFilter: ((node:any) => boolean)): VisitFunction { - return (node: any) => { - if (node.nodeType === 'ContractDefinition') { - this.setCurrentContract({ - node: node, - functions: [], - relevantNodes: [], - modifiers: [], - inheritsFrom: [], - stateVariables: getStateVariableDeclarationsFromContractNode(node) - }) - } else if (node.nodeType === 'InheritanceSpecifier') { - const currentContract: ContractHLAst = this.getCurrentContract() - const inheritsFromName: string = getInheritsFromName(node) - currentContract.inheritsFrom.push(inheritsFromName) - } else if (node.nodeType === 'FunctionDefinition') { - this.setCurrentFunction({ - node: node, - relevantNodes: [], - modifierInvocations: [], - localVariables: this.getLocalVariables(node), - parameters: this.getLocalParameters(node), - returns: this.getReturnParameters(node) - }) - // push back relevant nodes to their the current fn if any - this.getCurrentContract().relevantNodes.map((item) => { - if (item.referencedDeclaration === node.id) { - this.getCurrentFunction().relevantNodes.push(item.node) - } - }) - } else if (node.nodeType === 'ModifierDefinition') { - this.setCurrentModifier({ - node: node, - relevantNodes: [], - localVariables: this.getLocalVariables(node), - parameters: this.getLocalParameters(node) - }) - } else if (node.nodeType === 'ModifierInvocation') { - if (!this.isFunctionNotModifier) throw new Error('abstractAstView.js: Found modifier invocation outside of function scope.') - this.getCurrentFunction().modifierInvocations.push(node) - } else if (relevantNodeFilter(node)) { - let scope: FunctionHLAst | ModifierHLAst | ContractHLAst = (this.isFunctionNotModifier) ? this.getCurrentFunction() : this.getCurrentModifier() - if (scope) { - scope.relevantNodes.push(node) - } else { - scope = this.getCurrentContract() // if we are not in a function scope, add the node to the contract scope - if (scope && node.referencedDeclaration) { - scope.relevantNodes.push({ referencedDeclaration: node.referencedDeclaration, node: node }) - } - } - } + // eslint-disable-next-line camelcase + build_visit (relevantNodeFilter: ((node:any) => boolean)): VisitFunction { + return (node: any) => { + if (node.nodeType === 'ContractDefinition') { + this.setCurrentContract({ + node: node, + functions: [], + relevantNodes: [], + modifiers: [], + inheritsFrom: [], + stateVariables: getStateVariableDeclarationsFromContractNode(node) + }) + } else if (node.nodeType === 'InheritanceSpecifier') { + const currentContract: ContractHLAst = this.getCurrentContract() + const inheritsFromName: string = getInheritsFromName(node) + currentContract.inheritsFrom.push(inheritsFromName) + } else if (node.nodeType === 'FunctionDefinition') { + this.setCurrentFunction({ + node: node, + relevantNodes: [], + modifierInvocations: [], + localVariables: this.getLocalVariables(node), + parameters: this.getLocalParameters(node), + returns: this.getReturnParameters(node) + }) + // push back relevant nodes to their the current fn if any + this.getCurrentContract().relevantNodes.map((item) => { + if (item.referencedDeclaration === node.id) { + this.getCurrentFunction().relevantNodes.push(item.node) + } + }) + } else if (node.nodeType === 'ModifierDefinition') { + this.setCurrentModifier({ + node: node, + relevantNodes: [], + localVariables: this.getLocalVariables(node), + parameters: this.getLocalParameters(node) + }) + } else if (node.nodeType === 'ModifierInvocation') { + if (!this.isFunctionNotModifier) throw new Error('abstractAstView.js: Found modifier invocation outside of function scope.') + this.getCurrentFunction().modifierInvocations.push(node) + } else if (relevantNodeFilter(node)) { + let scope: FunctionHLAst | ModifierHLAst | ContractHLAst = (this.isFunctionNotModifier) ? this.getCurrentFunction() : this.getCurrentModifier() + if (scope) { + scope.relevantNodes.push(node) + } else { + scope = this.getCurrentContract() // if we are not in a function scope, add the node to the contract scope + if (scope && node.referencedDeclaration) { + scope.relevantNodes.push({ referencedDeclaration: node.referencedDeclaration, node: node }) + } } + } } + } - // eslint-disable-next-line camelcase - build_report (wrap: WrapFunction): ReportFunction { - // eslint-disable-next-line @typescript-eslint/no-unused-vars - return (compilationResult: CompilationResult) => { - const solVersion = getCompilerVersion(compilationResult.contracts) - this.resolveStateVariablesInHierarchy(this.contracts) - return wrap(this.contracts, this.multipleContractsWithSameName, solVersion) - } + // eslint-disable-next-line camelcase + build_report (wrap: WrapFunction): ReportFunction { + // eslint-disable-next-line @typescript-eslint/no-unused-vars + return (compilationResult: CompilationResult) => { + const solVersion = getCompilerVersion(compilationResult.contracts) + this.resolveStateVariablesInHierarchy(this.contracts) + return wrap(this.contracts, this.multipleContractsWithSameName, solVersion) } + } - private resolveStateVariablesInHierarchy (contracts: ContractHLAst[]): void { - contracts.map((c: ContractHLAst) => { - this.resolveStateVariablesInHierarchyForContract(c, contracts) - }) - } + private resolveStateVariablesInHierarchy (contracts: ContractHLAst[]): void { + contracts.map((c: ContractHLAst) => { + this.resolveStateVariablesInHierarchyForContract(c, contracts) + }) + } - private resolveStateVariablesInHierarchyForContract (currentContract: ContractHLAst, contracts: ContractHLAst[]): void { - currentContract.inheritsFrom.map((inheritsFromName: string) => { - // add variables from inherited contracts - const inheritsFrom: ContractHLAst | undefined = contracts.find((contract: ContractHLAst) => getContractName(contract.node) === inheritsFromName) - if (inheritsFrom) { - currentContract.stateVariables = currentContract.stateVariables.concat(inheritsFrom.stateVariables) - } else { - console.log('abstractAstView.js: could not find contract defintion inherited from ' + inheritsFromName) - } - }) - } + private resolveStateVariablesInHierarchyForContract (currentContract: ContractHLAst, contracts: ContractHLAst[]): void { + currentContract.inheritsFrom.map((inheritsFromName: string) => { + // add variables from inherited contracts + const inheritsFrom: ContractHLAst | undefined = contracts.find((contract: ContractHLAst) => getContractName(contract.node) === inheritsFromName) + if (inheritsFrom) { + currentContract.stateVariables = currentContract.stateVariables.concat(inheritsFrom.stateVariables) + } else { + console.log('abstractAstView.js: could not find contract defintion inherited from ' + inheritsFromName) + } + }) + } - private setCurrentContract (contract: ContractHLAst): void { - const name: string = getContractName(contract.node) - if (this.contracts.map((c: ContractHLAst) => getContractName(c.node)).filter((n) => n === name).length > 0) { - console.log('abstractAstView.js: two or more contracts with the same name dectected, import aliases not supported at the moment') - this.multipleContractsWithSameName = true - } - this.currentContractIndex = (this.contracts.push(contract) - 1) + private setCurrentContract (contract: ContractHLAst): void { + const name: string = getContractName(contract.node) + if (this.contracts.map((c: ContractHLAst) => getContractName(c.node)).filter((n) => n === name).length > 0) { + console.log('abstractAstView.js: two or more contracts with the same name dectected, import aliases not supported at the moment') + this.multipleContractsWithSameName = true } + this.currentContractIndex = (this.contracts.push(contract) - 1) + } - private setCurrentFunction (func: FunctionHLAst): void { - this.isFunctionNotModifier = true - this.currentFunctionIndex = (this.getCurrentContract().functions.push(func) - 1) - } + private setCurrentFunction (func: FunctionHLAst): void { + this.isFunctionNotModifier = true + this.currentFunctionIndex = (this.getCurrentContract().functions.push(func) - 1) + } - private setCurrentModifier (modi): void { - this.isFunctionNotModifier = false - this.currentModifierIndex = (this.getCurrentContract().modifiers.push(modi) - 1) - } + private setCurrentModifier (modi): void { + this.isFunctionNotModifier = false + this.currentModifierIndex = (this.getCurrentContract().modifiers.push(modi) - 1) + } - private getCurrentContract (): ContractHLAst { - return this.contracts[this.currentContractIndex] - } + private getCurrentContract (): ContractHLAst { + return this.contracts[this.currentContractIndex] + } - private getCurrentFunction (): FunctionHLAst { - return this.getCurrentContract().functions[this.currentFunctionIndex] - } + private getCurrentFunction (): FunctionHLAst { + return this.getCurrentContract().functions[this.currentFunctionIndex] + } - private getCurrentModifier (): ModifierHLAst { - return this.getCurrentContract().modifiers[this.currentModifierIndex] - } + private getCurrentModifier (): ModifierHLAst { + return this.getCurrentContract().modifiers[this.currentModifierIndex] + } - private getLocalParameters (funcNode: FunctionDefinitionAstNode | ModifierDefinitionAstNode): string[] { - return getFunctionOrModifierDefinitionParameterPart(funcNode).parameters.map(getType) - } + private getLocalParameters (funcNode: FunctionDefinitionAstNode | ModifierDefinitionAstNode): string[] { + return getFunctionOrModifierDefinitionParameterPart(funcNode).parameters.map(getType) + } - private getReturnParameters (funcNode: FunctionDefinitionAstNode): Record[] { - return this.getLocalVariables(getFunctionDefinitionReturnParameterPart(funcNode)).map((n: VariableDeclarationAstNode) => { - return { - type: getType(n), - name: getDeclaredVariableName(n) - } - }) - } + private getReturnParameters (funcNode: FunctionDefinitionAstNode): Record[] { + return this.getLocalVariables(getFunctionDefinitionReturnParameterPart(funcNode)).map((n: VariableDeclarationAstNode) => { + return { + type: getType(n), + name: getDeclaredVariableName(n) + } + }) + } - private getLocalVariables (funcNode: ParameterListAstNode): VariableDeclarationAstNode[] { - const locals: VariableDeclarationAstNode[] = [] - new AstWalker().walkFull(funcNode, (node: any) => { - if (node.nodeType === 'VariableDeclaration') locals.push(node) - return true - }) - return locals - } + private getLocalVariables (funcNode: ParameterListAstNode): VariableDeclarationAstNode[] { + const locals: VariableDeclarationAstNode[] = [] + new AstWalker().walkFull(funcNode, (node: any) => { + if (node.nodeType === 'VariableDeclaration') locals.push(node) + return true + }) + return locals + } } diff --git a/libs/remix-analyzer/src/solidity-analyzer/modules/algorithmCategories.ts b/libs/remix-analyzer/src/solidity-analyzer/modules/algorithmCategories.ts index 59a998aaa6..ba649b9729 100644 --- a/libs/remix-analyzer/src/solidity-analyzer/modules/algorithmCategories.ts +++ b/libs/remix-analyzer/src/solidity-analyzer/modules/algorithmCategories.ts @@ -4,6 +4,6 @@ * A further category could be approximate if some form of approximation is used */ export default { - EXACT: { hasFalsePositives: false, hasFalseNegatives: false, id: 'EXACT' }, - HEURISTIC: { hasFalsePositives: true, hasFalseNegatives: true, id: 'HEURI' } + EXACT: { hasFalsePositives: false, hasFalseNegatives: false, id: 'EXACT' }, + HEURISTIC: { hasFalsePositives: true, hasFalseNegatives: true, id: 'HEURI' } } diff --git a/libs/remix-analyzer/src/solidity-analyzer/modules/assignAndCompare.ts b/libs/remix-analyzer/src/solidity-analyzer/modules/assignAndCompare.ts index 1abca17307..c902214550 100644 --- a/libs/remix-analyzer/src/solidity-analyzer/modules/assignAndCompare.ts +++ b/libs/remix-analyzer/src/solidity-analyzer/modules/assignAndCompare.ts @@ -2,31 +2,31 @@ import category from './categories' import { isSubScopeWithTopLevelUnAssignedBinOp, getUnAssignedTopLevelBinOps } from './staticAnalysisCommon' import algorithm from './algorithmCategories' import { - AnalyzerModule, ModuleAlgorithm, ModuleCategory, ReportObj, BlockAstNode, IfStatementAstNode, - WhileStatementAstNode, ForStatementAstNode, CompilationResult, ExpressionStatementAstNode, SupportedVersion + AnalyzerModule, ModuleAlgorithm, ModuleCategory, ReportObj, BlockAstNode, IfStatementAstNode, + WhileStatementAstNode, ForStatementAstNode, CompilationResult, ExpressionStatementAstNode, SupportedVersion } from './../../types' export default class assignAndCompare implements AnalyzerModule { - warningNodes: ExpressionStatementAstNode[] = [] - name = 'Result not used: ' - description = 'The result of an operation not used' - category: ModuleCategory = category.MISC - algorithm: ModuleAlgorithm = algorithm.EXACT - version: SupportedVersion = { - start: '0.4.12' - } + warningNodes: ExpressionStatementAstNode[] = [] + name = 'Result not used: ' + description = 'The result of an operation not used' + category: ModuleCategory = category.MISC + algorithm: ModuleAlgorithm = algorithm.EXACT + version: SupportedVersion = { + start: '0.4.12' + } - visit (node: BlockAstNode | IfStatementAstNode | WhileStatementAstNode | ForStatementAstNode): void { - if (node?.nodeType && isSubScopeWithTopLevelUnAssignedBinOp(node)) getUnAssignedTopLevelBinOps(node).forEach((n) => this.warningNodes.push(n)) - } + visit (node: BlockAstNode | IfStatementAstNode | WhileStatementAstNode | ForStatementAstNode): void { + if (node?.nodeType && isSubScopeWithTopLevelUnAssignedBinOp(node)) getUnAssignedTopLevelBinOps(node).forEach((n) => this.warningNodes.push(n)) + } - // eslint-disable-next-line @typescript-eslint/no-unused-vars - report (compilationResults: CompilationResult): ReportObj[] { - return this.warningNodes.map((item) => { - return { - warning: 'A binary operation yields a value that is not used further. This is often caused by confusing assignment (=) and comparison (==).', - location: item.src - } - }) - } + // eslint-disable-next-line @typescript-eslint/no-unused-vars + report (compilationResults: CompilationResult): ReportObj[] { + return this.warningNodes.map((item) => { + return { + warning: 'A binary operation yields a value that is not used further. This is often caused by confusing assignment (=) and comparison (==).', + location: item.src + } + }) + } } diff --git a/libs/remix-analyzer/src/solidity-analyzer/modules/blockBlockhash.ts b/libs/remix-analyzer/src/solidity-analyzer/modules/blockBlockhash.ts index 714e7f62dd..70db8092e8 100644 --- a/libs/remix-analyzer/src/solidity-analyzer/modules/blockBlockhash.ts +++ b/libs/remix-analyzer/src/solidity-analyzer/modules/blockBlockhash.ts @@ -4,29 +4,29 @@ import algorithm from './algorithmCategories' import { AnalyzerModule, ModuleAlgorithm, ModuleCategory, ReportObj, CompilationResult, FunctionCallAstNode, SupportedVersion } from './../../types' export default class blockBlockhash implements AnalyzerModule { - warningNodes: FunctionCallAstNode[] = [] - name = 'Block hash: ' - description = 'Can be influenced by miners' - category: ModuleCategory = category.SECURITY - algorithm: ModuleAlgorithm = algorithm.EXACT - version: SupportedVersion = { - start: '0.4.12' - } + warningNodes: FunctionCallAstNode[] = [] + name = 'Block hash: ' + description = 'Can be influenced by miners' + category: ModuleCategory = category.SECURITY + algorithm: ModuleAlgorithm = algorithm.EXACT + version: SupportedVersion = { + start: '0.4.12' + } - visit (node: FunctionCallAstNode): void { - if (node.nodeType === 'FunctionCall' && isBlockBlockHashAccess(node)) this.warningNodes.push(node) - } + visit (node: FunctionCallAstNode): void { + if (node.nodeType === 'FunctionCall' && isBlockBlockHashAccess(node)) this.warningNodes.push(node) + } - // eslint-disable-next-line @typescript-eslint/no-unused-vars - report (compilationResults: CompilationResult): ReportObj[] { - return this.warningNodes.map((item) => { - return { - warning: `Use of "blockhash": "blockhash(uint blockNumber)" is used to access the last 256 block hashes. + // eslint-disable-next-line @typescript-eslint/no-unused-vars + report (compilationResults: CompilationResult): ReportObj[] { + return this.warningNodes.map((item) => { + return { + warning: `Use of "blockhash": "blockhash(uint blockNumber)" is used to access the last 256 block hashes. A miner computes the block hash by "summing up" the information in the current block mined. By "summing up" the information cleverly, a miner can try to influence the outcome of a transaction in the current block. This is especially easy if there are only a small number of equally likely outcomes.`, - location: item.src - } - }) - } + location: item.src + } + }) + } } diff --git a/libs/remix-analyzer/src/solidity-analyzer/modules/blockTimestamp.ts b/libs/remix-analyzer/src/solidity-analyzer/modules/blockTimestamp.ts index be78b7e90b..92fa67498f 100644 --- a/libs/remix-analyzer/src/solidity-analyzer/modules/blockTimestamp.ts +++ b/libs/remix-analyzer/src/solidity-analyzer/modules/blockTimestamp.ts @@ -2,43 +2,43 @@ import category from './categories' import { isNowAccess, isBlockTimestampAccess, getCompilerVersion } from './staticAnalysisCommon' import algorithm from './algorithmCategories' import { - AnalyzerModule, ModuleAlgorithm, ModuleCategory, ReportObj, CompilationResult, IdentifierAstNode, - MemberAccessAstNode, SupportedVersion + AnalyzerModule, ModuleAlgorithm, ModuleCategory, ReportObj, CompilationResult, IdentifierAstNode, + MemberAccessAstNode, SupportedVersion } from './../../types' export default class blockTimestamp implements AnalyzerModule { - warningNowNodes: IdentifierAstNode[] = [] - warningblockTimestampNodes: MemberAccessAstNode[] = [] - name = 'Block timestamp: ' - description = 'Can be influenced by miners' - category: ModuleCategory = category.SECURITY - algorithm: ModuleAlgorithm = algorithm.EXACT - version: SupportedVersion = { - start: '0.4.12' - } + warningNowNodes: IdentifierAstNode[] = [] + warningblockTimestampNodes: MemberAccessAstNode[] = [] + name = 'Block timestamp: ' + description = 'Can be influenced by miners' + category: ModuleCategory = category.SECURITY + algorithm: ModuleAlgorithm = algorithm.EXACT + version: SupportedVersion = { + start: '0.4.12' + } - visit (node: IdentifierAstNode | MemberAccessAstNode): void { - if (node.nodeType === 'Identifier' && isNowAccess(node)) this.warningNowNodes.push(node) - else if (node.nodeType === 'MemberAccess' && isBlockTimestampAccess(node)) this.warningblockTimestampNodes.push(node) - } + visit (node: IdentifierAstNode | MemberAccessAstNode): void { + if (node.nodeType === 'Identifier' && isNowAccess(node)) this.warningNowNodes.push(node) + else if (node.nodeType === 'MemberAccess' && isBlockTimestampAccess(node)) this.warningblockTimestampNodes.push(node) + } - // eslint-disable-next-line @typescript-eslint/no-unused-vars - report (compilationResults: CompilationResult): ReportObj[] { - const version = getCompilerVersion(compilationResults.contracts) - return this.warningNowNodes.map((item, i) => { - return { - warning: `Use of "now": "now" does not mean current time. "now" is an alias for "block.timestamp". + // eslint-disable-next-line @typescript-eslint/no-unused-vars + report (compilationResults: CompilationResult): ReportObj[] { + const version = getCompilerVersion(compilationResults.contracts) + return this.warningNowNodes.map((item, i) => { + return { + warning: `Use of "now": "now" does not mean current time. "now" is an alias for "block.timestamp". "block.timestamp" can be influenced by miners to a certain degree, be careful.`, - location: item.src, - more: `https://solidity.readthedocs.io/en/${version}/units-and-global-variables.html?highlight=block.timestamp#block-and-transaction-properties` - } - }).concat(this.warningblockTimestampNodes.map((item) => { - return { - warning: `Use of "block.timestamp": "block.timestamp" can be influenced by miners to a certain degree. + location: item.src, + more: `https://solidity.readthedocs.io/en/${version}/units-and-global-variables.html?highlight=block.timestamp#block-and-transaction-properties` + } + }).concat(this.warningblockTimestampNodes.map((item) => { + return { + warning: `Use of "block.timestamp": "block.timestamp" can be influenced by miners to a certain degree. That means that a miner can "choose" the block.timestamp, to a certain degree, to change the outcome of a transaction in the mined block.`, - location: item.src, - more: `https://solidity.readthedocs.io/en/${version}/units-and-global-variables.html?highlight=block.timestamp#block-and-transaction-properties` - } - })) - } + location: item.src, + more: `https://solidity.readthedocs.io/en/${version}/units-and-global-variables.html?highlight=block.timestamp#block-and-transaction-properties` + } + })) + } } diff --git a/libs/remix-analyzer/src/solidity-analyzer/modules/categories.ts b/libs/remix-analyzer/src/solidity-analyzer/modules/categories.ts index 855d691b8c..9eb9c6872a 100644 --- a/libs/remix-analyzer/src/solidity-analyzer/modules/categories.ts +++ b/libs/remix-analyzer/src/solidity-analyzer/modules/categories.ts @@ -1,6 +1,6 @@ export default { - SECURITY: { displayName: 'Security', id: 'SEC' }, - GAS: { displayName: 'Gas & Economy', id: 'GAS' }, - MISC: { displayName: 'Miscellaneous', id: 'MISC' }, - ERC: { displayName: 'ERC', id: 'ERC' } + SECURITY: { displayName: 'Security', id: 'SEC' }, + GAS: { displayName: 'Gas & Economy', id: 'GAS' }, + MISC: { displayName: 'Miscellaneous', id: 'MISC' }, + ERC: { displayName: 'ERC', id: 'ERC' } } diff --git a/libs/remix-analyzer/src/solidity-analyzer/modules/checksEffectsInteraction.ts b/libs/remix-analyzer/src/solidity-analyzer/modules/checksEffectsInteraction.ts index e0cf9adac6..16e62f3795 100644 --- a/libs/remix-analyzer/src/solidity-analyzer/modules/checksEffectsInteraction.ts +++ b/libs/remix-analyzer/src/solidity-analyzer/modules/checksEffectsInteraction.ts @@ -1,98 +1,98 @@ import category from './categories' import { - isInteraction, isEffect, isLocalCallGraphRelevantNode, getFullQuallyfiedFuncDefinitionIdent, - isWriteOnStateVariable, isStorageVariableDeclaration, getFullQualifiedFunctionCallIdent, getCompilerVersion + isInteraction, isEffect, isLocalCallGraphRelevantNode, getFullQuallyfiedFuncDefinitionIdent, + isWriteOnStateVariable, isStorageVariableDeclaration, getFullQualifiedFunctionCallIdent, getCompilerVersion } from './staticAnalysisCommon' import algorithm from './algorithmCategories' import { buildGlobalFuncCallGraph, resolveCallGraphSymbol, analyseCallGraph } from './functionCallGraph' import AbstractAst from './abstractAstView' import { - AnalyzerModule, ModuleAlgorithm, ModuleCategory, ReportObj, ContractHLAst, VariableDeclarationAstNode, - FunctionHLAst, ContractCallGraph, Context, FunctionCallAstNode, AssignmentAstNode, UnaryOperationAstNode, - InlineAssemblyAstNode, ReportFunction, VisitFunction, FunctionCallGraph, SupportedVersion + AnalyzerModule, ModuleAlgorithm, ModuleCategory, ReportObj, ContractHLAst, VariableDeclarationAstNode, + FunctionHLAst, ContractCallGraph, Context, FunctionCallAstNode, AssignmentAstNode, UnaryOperationAstNode, + InlineAssemblyAstNode, ReportFunction, VisitFunction, FunctionCallGraph, SupportedVersion } from './../../types' export default class checksEffectsInteraction implements AnalyzerModule { - name = 'Check-effects-interaction: ' - description = 'Potential reentrancy bugs' - category: ModuleCategory = category.SECURITY - algorithm: ModuleAlgorithm = algorithm.HEURISTIC - version: SupportedVersion = { - start: '0.4.12' - } + name = 'Check-effects-interaction: ' + description = 'Potential reentrancy bugs' + category: ModuleCategory = category.SECURITY + algorithm: ModuleAlgorithm = algorithm.HEURISTIC + version: SupportedVersion = { + start: '0.4.12' + } - abstractAst: AbstractAst = new AbstractAst() + abstractAst: AbstractAst = new AbstractAst() - visit: VisitFunction = this.abstractAst.build_visit((node: FunctionCallAstNode | AssignmentAstNode | UnaryOperationAstNode | InlineAssemblyAstNode) => ( - node.nodeType === 'FunctionCall' && (isInteraction(node) || isLocalCallGraphRelevantNode(node))) || + visit: VisitFunction = this.abstractAst.build_visit((node: FunctionCallAstNode | AssignmentAstNode | UnaryOperationAstNode | InlineAssemblyAstNode) => ( + node.nodeType === 'FunctionCall' && (isInteraction(node) || isLocalCallGraphRelevantNode(node))) || ((node.nodeType === 'Assignment' || node.nodeType === 'UnaryOperation' || node.nodeType === 'InlineAssembly') && isEffect(node))) - report: ReportFunction = this.abstractAst.build_report(this._report.bind(this)) + report: ReportFunction = this.abstractAst.build_report(this._report.bind(this)) - private _report (contracts: ContractHLAst[], multipleContractsWithSameName: boolean, version: string): ReportObj[] { - const warnings: ReportObj[] = [] - const hasModifiers: boolean = contracts.some((item) => item.modifiers.length > 0) - const callGraph: Record = buildGlobalFuncCallGraph(contracts) - contracts.forEach((contract) => { - contract.functions.forEach((func) => { - func['changesState'] = this.checkIfChangesState( - getFullQuallyfiedFuncDefinitionIdent( - contract.node, - func.node, - func.parameters - ), - this.getContext( - callGraph, - contract, - func) - ) - }) - contract.functions.forEach((func: FunctionHLAst) => { - if (this.isPotentialVulnerableFunction(func, this.getContext(callGraph, contract, func))) { - const funcName: string = getFullQuallyfiedFuncDefinitionIdent(contract.node, func.node, func.parameters) - let comments: string = (hasModifiers) ? 'Note: Modifiers are currently not considered by this static analysis.' : '' - comments += (multipleContractsWithSameName) ? 'Note: Import aliases are currently not supported by this static analysis.' : '' - warnings.push({ - warning: `Potential violation of Checks-Effects-Interaction pattern in ${funcName}: Could potentially lead to re-entrancy vulnerability. ${comments}`, - location: func.node.src, - more: `https://solidity.readthedocs.io/en/${version}/security-considerations.html#re-entrancy` - }) - } - }) - }) - return warnings - } + private _report (contracts: ContractHLAst[], multipleContractsWithSameName: boolean, version: string): ReportObj[] { + const warnings: ReportObj[] = [] + const hasModifiers: boolean = contracts.some((item) => item.modifiers.length > 0) + const callGraph: Record = buildGlobalFuncCallGraph(contracts) + contracts.forEach((contract) => { + contract.functions.forEach((func) => { + func['changesState'] = this.checkIfChangesState( + getFullQuallyfiedFuncDefinitionIdent( + contract.node, + func.node, + func.parameters + ), + this.getContext( + callGraph, + contract, + func) + ) + }) + contract.functions.forEach((func: FunctionHLAst) => { + if (this.isPotentialVulnerableFunction(func, this.getContext(callGraph, contract, func))) { + const funcName: string = getFullQuallyfiedFuncDefinitionIdent(contract.node, func.node, func.parameters) + let comments: string = (hasModifiers) ? 'Note: Modifiers are currently not considered by this static analysis.' : '' + comments += (multipleContractsWithSameName) ? 'Note: Import aliases are currently not supported by this static analysis.' : '' + warnings.push({ + warning: `Potential violation of Checks-Effects-Interaction pattern in ${funcName}: Could potentially lead to re-entrancy vulnerability. ${comments}`, + location: func.node.src, + more: `https://solidity.readthedocs.io/en/${version}/security-considerations.html#re-entrancy` + }) + } + }) + }) + return warnings + } - private getContext (callGraph: Record, currentContract: ContractHLAst, func: FunctionHLAst): Context { - return { callGraph: callGraph, currentContract: currentContract, stateVariables: this.getStateVariables(currentContract, func) } - } + private getContext (callGraph: Record, currentContract: ContractHLAst, func: FunctionHLAst): Context { + return { callGraph: callGraph, currentContract: currentContract, stateVariables: this.getStateVariables(currentContract, func) } + } - private getStateVariables (contract: ContractHLAst, func: FunctionHLAst): VariableDeclarationAstNode[] { - return contract.stateVariables.concat(func.localVariables.filter(isStorageVariableDeclaration)) - } + private getStateVariables (contract: ContractHLAst, func: FunctionHLAst): VariableDeclarationAstNode[] { + return contract.stateVariables.concat(func.localVariables.filter(isStorageVariableDeclaration)) + } - private isPotentialVulnerableFunction (func: FunctionHLAst, context: Context): boolean { - let isPotentialVulnerable = false - let interaction = false - func.relevantNodes.forEach((node) => { - if (isInteraction(node)) { - interaction = true - } else if (interaction && (isWriteOnStateVariable(node, context.stateVariables) || this.isLocalCallWithStateChange(node, context))) { - isPotentialVulnerable = true - } - }) - return isPotentialVulnerable - } + private isPotentialVulnerableFunction (func: FunctionHLAst, context: Context): boolean { + let isPotentialVulnerable = false + let interaction = false + func.relevantNodes.forEach((node) => { + if (isInteraction(node)) { + interaction = true + } else if (interaction && (isWriteOnStateVariable(node, context.stateVariables) || this.isLocalCallWithStateChange(node, context))) { + isPotentialVulnerable = true + } + }) + return isPotentialVulnerable + } - private isLocalCallWithStateChange (node: FunctionCallAstNode, context: Context): boolean { - if (isLocalCallGraphRelevantNode(node)) { - const func: FunctionCallGraph | undefined = resolveCallGraphSymbol(context.callGraph, getFullQualifiedFunctionCallIdent(context.currentContract.node, node)) - return !func || (func && func.node['changesState']) - } - return false + private isLocalCallWithStateChange (node: FunctionCallAstNode, context: Context): boolean { + if (isLocalCallGraphRelevantNode(node)) { + const func: FunctionCallGraph | undefined = resolveCallGraphSymbol(context.callGraph, getFullQualifiedFunctionCallIdent(context.currentContract.node, node)) + return !func || (func && func.node['changesState']) } + return false + } - private checkIfChangesState (startFuncName: string, context: Context): boolean { - return analyseCallGraph(context.callGraph, startFuncName, context, (node: any, context: Context) => isWriteOnStateVariable(node, context.stateVariables)) - } + private checkIfChangesState (startFuncName: string, context: Context): boolean { + return analyseCallGraph(context.callGraph, startFuncName, context, (node: any, context: Context) => isWriteOnStateVariable(node, context.stateVariables)) + } } diff --git a/libs/remix-analyzer/src/solidity-analyzer/modules/constantFunctions.ts b/libs/remix-analyzer/src/solidity-analyzer/modules/constantFunctions.ts index abbdb91807..f27b83c222 100644 --- a/libs/remix-analyzer/src/solidity-analyzer/modules/constantFunctions.ts +++ b/libs/remix-analyzer/src/solidity-analyzer/modules/constantFunctions.ts @@ -1,31 +1,31 @@ import category from './categories' import { - isLowLevelCall, isTransfer, isExternalDirectCall, isEffect, isLocalCallGraphRelevantNode, isSelfdestructCall, - isDeleteUnaryOperation, isPayableFunction, isConstructor, getFullQuallyfiedFuncDefinitionIdent, hasFunctionBody, - isConstantFunction, isWriteOnStateVariable, isStorageVariableDeclaration, isCallToNonConstLocalFunction, - getFullQualifiedFunctionCallIdent + isLowLevelCall, isTransfer, isExternalDirectCall, isEffect, isLocalCallGraphRelevantNode, isSelfdestructCall, + isDeleteUnaryOperation, isPayableFunction, isConstructor, getFullQuallyfiedFuncDefinitionIdent, hasFunctionBody, + isConstantFunction, isWriteOnStateVariable, isStorageVariableDeclaration, isCallToNonConstLocalFunction, + getFullQualifiedFunctionCallIdent } from './staticAnalysisCommon' import algorithm from './algorithmCategories' import { buildGlobalFuncCallGraph, resolveCallGraphSymbol, analyseCallGraph } from './functionCallGraph' import AbstractAst from './abstractAstView' import { - AnalyzerModule, ModuleAlgorithm, ModuleCategory, ReportObj, ContractCallGraph, Context, ContractHLAst, - FunctionHLAst, VariableDeclarationAstNode, FunctionCallGraph, FunctionCallAstNode, VisitFunction, ReportFunction, SupportedVersion + AnalyzerModule, ModuleAlgorithm, ModuleCategory, ReportObj, ContractCallGraph, Context, ContractHLAst, + FunctionHLAst, VariableDeclarationAstNode, FunctionCallGraph, FunctionCallAstNode, VisitFunction, ReportFunction, SupportedVersion } from './../../types' export default class constantFunctions implements AnalyzerModule { - name = 'Constant/View/Pure functions: ' - description = 'Potentially constant/view/pure functions' - category: ModuleCategory = category.MISC - algorithm: ModuleAlgorithm = algorithm.HEURISTIC - version: SupportedVersion = { - start: '0.4.12' - } + name = 'Constant/View/Pure functions: ' + description = 'Potentially constant/view/pure functions' + category: ModuleCategory = category.MISC + algorithm: ModuleAlgorithm = algorithm.HEURISTIC + version: SupportedVersion = { + start: '0.4.12' + } - abstractAst: AbstractAst = new AbstractAst() + abstractAst: AbstractAst = new AbstractAst() - visit: VisitFunction = this.abstractAst.build_visit( - (node: any) => isLowLevelCall(node) || + visit: VisitFunction = this.abstractAst.build_visit( + (node: any) => isLowLevelCall(node) || isTransfer(node) || isExternalDirectCall(node) || isEffect(node) || @@ -34,73 +34,73 @@ export default class constantFunctions implements AnalyzerModule { node.nodeType === 'NewExpression' || isSelfdestructCall(node) || isDeleteUnaryOperation(node) - ) + ) - report: ReportFunction = this.abstractAst.build_report(this._report.bind(this)) + report: ReportFunction = this.abstractAst.build_report(this._report.bind(this)) - private _report (contracts: ContractHLAst[], multipleContractsWithSameName: boolean, version: string): ReportObj[] { - const warnings: ReportObj[] = [] - const hasModifiers: boolean = contracts.some((item) => item.modifiers.length > 0) + private _report (contracts: ContractHLAst[], multipleContractsWithSameName: boolean, version: string): ReportObj[] { + const warnings: ReportObj[] = [] + const hasModifiers: boolean = contracts.some((item) => item.modifiers.length > 0) - const callGraph: Record = buildGlobalFuncCallGraph(contracts) + const callGraph: Record = buildGlobalFuncCallGraph(contracts) - contracts.forEach((contract: ContractHLAst) => { - contract.functions.forEach((func: FunctionHLAst) => { - if (isPayableFunction(func.node) || isConstructor(func.node)) { - func['potentiallyshouldBeConst'] = false - } else { - func['potentiallyshouldBeConst'] = this.checkIfShouldBeConstant( - getFullQuallyfiedFuncDefinitionIdent( - contract.node, - func.node, - func.parameters - ), - this.getContext( - callGraph, - contract, - func - ) - ) - } + contracts.forEach((contract: ContractHLAst) => { + contract.functions.forEach((func: FunctionHLAst) => { + if (isPayableFunction(func.node) || isConstructor(func.node)) { + func['potentiallyshouldBeConst'] = false + } else { + func['potentiallyshouldBeConst'] = this.checkIfShouldBeConstant( + getFullQuallyfiedFuncDefinitionIdent( + contract.node, + func.node, + func.parameters + ), + this.getContext( + callGraph, + contract, + func + ) + ) + } + }) + contract.functions.filter((func: FunctionHLAst) => hasFunctionBody(func.node)).forEach((func: FunctionHLAst) => { + if (isConstantFunction(func.node) !== func['potentiallyshouldBeConst']) { + const funcName: string = getFullQuallyfiedFuncDefinitionIdent(contract.node, func.node, func.parameters) + let comments: string = (hasModifiers) ? 'Note: Modifiers are currently not considered by this static analysis.' : '' + comments += (multipleContractsWithSameName) ? 'Note: Import aliases are currently not supported by this static analysis.' : '' + if (func['potentiallyshouldBeConst']) { + warnings.push({ + warning: `${funcName} : Potentially should be constant/view/pure but is not. ${comments}`, + location: func.node.src, + more: `https://solidity.readthedocs.io/en/${version}/contracts.html#view-functions` }) - contract.functions.filter((func: FunctionHLAst) => hasFunctionBody(func.node)).forEach((func: FunctionHLAst) => { - if (isConstantFunction(func.node) !== func['potentiallyshouldBeConst']) { - const funcName: string = getFullQuallyfiedFuncDefinitionIdent(contract.node, func.node, func.parameters) - let comments: string = (hasModifiers) ? 'Note: Modifiers are currently not considered by this static analysis.' : '' - comments += (multipleContractsWithSameName) ? 'Note: Import aliases are currently not supported by this static analysis.' : '' - if (func['potentiallyshouldBeConst']) { - warnings.push({ - warning: `${funcName} : Potentially should be constant/view/pure but is not. ${comments}`, - location: func.node.src, - more: `https://solidity.readthedocs.io/en/${version}/contracts.html#view-functions` - }) - } else { - warnings.push({ - warning: `${funcName} : Is constant but potentially should not be. ${comments}`, - location: func.node.src, - more: `https://solidity.readthedocs.io/en/${version}/contracts.html#view-functions` - }) - } - } + } else { + warnings.push({ + warning: `${funcName} : Is constant but potentially should not be. ${comments}`, + location: func.node.src, + more: `https://solidity.readthedocs.io/en/${version}/contracts.html#view-functions` }) - }) - return warnings - } + } + } + }) + }) + return warnings + } - private getContext (callGraph: Record, currentContract: ContractHLAst, func: FunctionHLAst): Context { - return { callGraph: callGraph, currentContract: currentContract, stateVariables: this.getStateVariables(currentContract, func) } - } + private getContext (callGraph: Record, currentContract: ContractHLAst, func: FunctionHLAst): Context { + return { callGraph: callGraph, currentContract: currentContract, stateVariables: this.getStateVariables(currentContract, func) } + } - private getStateVariables (contract: ContractHLAst, func: FunctionHLAst): VariableDeclarationAstNode[] { - return contract.stateVariables.concat(func.localVariables.filter(isStorageVariableDeclaration)) - } + private getStateVariables (contract: ContractHLAst, func: FunctionHLAst): VariableDeclarationAstNode[] { + return contract.stateVariables.concat(func.localVariables.filter(isStorageVariableDeclaration)) + } - private checkIfShouldBeConstant (startFuncName: string, context: Context): boolean { - return !analyseCallGraph(context.callGraph, startFuncName, context, this.isConstBreaker.bind(this)) - } + private checkIfShouldBeConstant (startFuncName: string, context: Context): boolean { + return !analyseCallGraph(context.callGraph, startFuncName, context, this.isConstBreaker.bind(this)) + } - private isConstBreaker (node: any, context: Context): boolean { - return isWriteOnStateVariable(node, context.stateVariables) || + private isConstBreaker (node: any, context: Context): boolean { + return isWriteOnStateVariable(node, context.stateVariables) || isLowLevelCall(node) || isTransfer(node) || this.isCallOnNonConstExternalInterfaceFunction(node, context) || @@ -109,13 +109,13 @@ export default class constantFunctions implements AnalyzerModule { node.nodeType === 'NewExpression' || isSelfdestructCall(node) || isDeleteUnaryOperation(node) - } + } - private isCallOnNonConstExternalInterfaceFunction (node: FunctionCallAstNode, context: Context): boolean { - if (isExternalDirectCall(node)) { - const func: FunctionCallGraph | undefined = resolveCallGraphSymbol(context.callGraph, getFullQualifiedFunctionCallIdent(context.currentContract.node, node)) - return !func || (func && !isConstantFunction(func.node.node)) - } - return false + private isCallOnNonConstExternalInterfaceFunction (node: FunctionCallAstNode, context: Context): boolean { + if (isExternalDirectCall(node)) { + const func: FunctionCallGraph | undefined = resolveCallGraphSymbol(context.callGraph, getFullQualifiedFunctionCallIdent(context.currentContract.node, node)) + return !func || (func && !isConstantFunction(func.node.node)) } + return false + } } diff --git a/libs/remix-analyzer/src/solidity-analyzer/modules/deleteDynamicArrays.ts b/libs/remix-analyzer/src/solidity-analyzer/modules/deleteDynamicArrays.ts index dd814b382e..166bc151bd 100644 --- a/libs/remix-analyzer/src/solidity-analyzer/modules/deleteDynamicArrays.ts +++ b/libs/remix-analyzer/src/solidity-analyzer/modules/deleteDynamicArrays.ts @@ -4,28 +4,28 @@ import algorithm from './algorithmCategories' import { AnalyzerModule, ModuleAlgorithm, ModuleCategory, ReportObj, CompilationResult, UnaryOperationAstNode, SupportedVersion } from './../../types' export default class deleteDynamicArrays implements AnalyzerModule { - rel: UnaryOperationAstNode[] = [] - name = 'Delete dynamic array: ' - description = 'Use require/assert to ensure complete deletion' - category: ModuleCategory = category.GAS - algorithm: ModuleAlgorithm = algorithm.EXACT - version: SupportedVersion = { - start: '0.4.12' - } + rel: UnaryOperationAstNode[] = [] + name = 'Delete dynamic array: ' + description = 'Use require/assert to ensure complete deletion' + category: ModuleCategory = category.GAS + algorithm: ModuleAlgorithm = algorithm.EXACT + version: SupportedVersion = { + start: '0.4.12' + } - visit (node: UnaryOperationAstNode): void { - if (isDeleteOfDynamicArray(node)) this.rel.push(node) - } + visit (node: UnaryOperationAstNode): void { + if (isDeleteOfDynamicArray(node)) this.rel.push(node) + } - // eslint-disable-next-line @typescript-eslint/no-unused-vars - report (compilationResults: CompilationResult): ReportObj[] { - const version = getCompilerVersion(compilationResults.contracts) - return this.rel.map((node) => { - return { - warning: 'The "delete" operation when applied to a dynamically sized array in Solidity generates code to delete each of the elements contained. If the array is large, this operation can surpass the block gas limit and raise an OOG exception. Also nested dynamically sized objects can produce the same results.', - location: node.src, - more: `https://solidity.readthedocs.io/en/${version}/types.html#delete` - } - }) - } + // eslint-disable-next-line @typescript-eslint/no-unused-vars + report (compilationResults: CompilationResult): ReportObj[] { + const version = getCompilerVersion(compilationResults.contracts) + return this.rel.map((node) => { + return { + warning: 'The "delete" operation when applied to a dynamically sized array in Solidity generates code to delete each of the elements contained. If the array is large, this operation can surpass the block gas limit and raise an OOG exception. Also nested dynamically sized objects can produce the same results.', + location: node.src, + more: `https://solidity.readthedocs.io/en/${version}/types.html#delete` + } + }) + } } diff --git a/libs/remix-analyzer/src/solidity-analyzer/modules/deleteFromDynamicArray.ts b/libs/remix-analyzer/src/solidity-analyzer/modules/deleteFromDynamicArray.ts index 714da95c65..b789648b57 100644 --- a/libs/remix-analyzer/src/solidity-analyzer/modules/deleteFromDynamicArray.ts +++ b/libs/remix-analyzer/src/solidity-analyzer/modules/deleteFromDynamicArray.ts @@ -4,27 +4,27 @@ import { isDeleteFromDynamicArray, isMappingIndexAccess } from './staticAnalysis import { AnalyzerModule, ModuleAlgorithm, ModuleCategory, ReportObj, CompilationResult, UnaryOperationAstNode, SupportedVersion } from './../../types' export default class deleteFromDynamicArray implements AnalyzerModule { - relevantNodes: UnaryOperationAstNode[] = [] - name = 'Delete from dynamic array: ' - description = '\'delete\' leaves a gap in array' - category: ModuleCategory = category.MISC - algorithm: ModuleAlgorithm = algorithm.EXACT - version: SupportedVersion = { - start: '0.4.12' - } + relevantNodes: UnaryOperationAstNode[] = [] + name = 'Delete from dynamic array: ' + description = '\'delete\' leaves a gap in array' + category: ModuleCategory = category.MISC + algorithm: ModuleAlgorithm = algorithm.EXACT + version: SupportedVersion = { + start: '0.4.12' + } - visit (node: UnaryOperationAstNode): void { - if (isDeleteFromDynamicArray(node) && !isMappingIndexAccess(node.subExpression)) this.relevantNodes.push(node) - } + visit (node: UnaryOperationAstNode): void { + if (isDeleteFromDynamicArray(node) && !isMappingIndexAccess(node.subExpression)) this.relevantNodes.push(node) + } - // eslint-disable-next-line @typescript-eslint/no-unused-vars - report (compilationResults: CompilationResult): ReportObj[] { - return this.relevantNodes.map((node) => { - return { - warning: 'Using "delete" on an array leaves a gap. The length of the array remains the same. If you want to remove the empty position you need to shift items manually and update the "length" property.', - location: node.src, - more: 'https://github.com/miguelmota/solidity-idiosyncrasies#examples' - } - }) - } + // eslint-disable-next-line @typescript-eslint/no-unused-vars + report (compilationResults: CompilationResult): ReportObj[] { + return this.relevantNodes.map((node) => { + return { + warning: 'Using "delete" on an array leaves a gap. The length of the array remains the same. If you want to remove the empty position you need to shift items manually and update the "length" property.', + location: node.src, + more: 'https://github.com/miguelmota/solidity-idiosyncrasies#examples' + } + }) + } } diff --git a/libs/remix-analyzer/src/solidity-analyzer/modules/erc20Decimals.ts b/libs/remix-analyzer/src/solidity-analyzer/modules/erc20Decimals.ts index c9264920c7..d429869f2d 100644 --- a/libs/remix-analyzer/src/solidity-analyzer/modules/erc20Decimals.ts +++ b/libs/remix-analyzer/src/solidity-analyzer/modules/erc20Decimals.ts @@ -3,68 +3,68 @@ import { getFunctionDefinitionName, helpers, getDeclaredVariableName, getDeclare import algorithm from './algorithmCategories' import AbstractAst from './abstractAstView' import { - AnalyzerModule, ModuleAlgorithm, ModuleCategory, ReportObj, VisitFunction, ReportFunction, ContractHLAst, - FunctionHLAst, VariableDeclarationAstNode, SupportedVersion + AnalyzerModule, ModuleAlgorithm, ModuleCategory, ReportObj, VisitFunction, ReportFunction, ContractHLAst, + FunctionHLAst, VariableDeclarationAstNode, SupportedVersion } from './../../types' export default class erc20Decimals implements AnalyzerModule { - name = 'ERC20: ' - description = '\'decimals\' should be \'uint8\'' - category: ModuleCategory = category.ERC - algorithm: ModuleAlgorithm = algorithm.EXACT - version: SupportedVersion = { - start: '0.4.12' - } + name = 'ERC20: ' + description = '\'decimals\' should be \'uint8\'' + category: ModuleCategory = category.ERC + algorithm: ModuleAlgorithm = algorithm.EXACT + version: SupportedVersion = { + start: '0.4.12' + } - abstractAst: AbstractAst = new AbstractAst() + abstractAst: AbstractAst = new AbstractAst() - // eslint-disable-next-line @typescript-eslint/no-unused-vars - visit: VisitFunction = this.abstractAst.build_visit((node: any) => false) - report: ReportFunction = this.abstractAst.build_report(this._report.bind(this)) + // eslint-disable-next-line @typescript-eslint/no-unused-vars + visit: VisitFunction = this.abstractAst.build_visit((node: any) => false) + report: ReportFunction = this.abstractAst.build_report(this._report.bind(this)) - // eslint-disable-next-line @typescript-eslint/no-unused-vars - private _report (contracts: ContractHLAst[], multipleContractsWithSameName: boolean): ReportObj[] { - const warnings: ReportObj[] = [] + // eslint-disable-next-line @typescript-eslint/no-unused-vars + private _report (contracts: ContractHLAst[], multipleContractsWithSameName: boolean): ReportObj[] { + const warnings: ReportObj[] = [] - contracts.forEach((contract: ContractHLAst) => { - const contractAbiSignatures: string[] = contract.functions.map((f: FunctionHLAst) => helpers.buildAbiSignature(getFunctionDefinitionName(f.node), f.parameters)) + contracts.forEach((contract: ContractHLAst) => { + const contractAbiSignatures: string[] = contract.functions.map((f: FunctionHLAst) => helpers.buildAbiSignature(getFunctionDefinitionName(f.node), f.parameters)) - if (this.isERC20(contractAbiSignatures)) { - const decimalsVar: VariableDeclarationAstNode[] = contract.stateVariables.filter((stateVar: VariableDeclarationAstNode) => getDeclaredVariableName(stateVar) === 'decimals' && (getDeclaredVariableType(stateVar) !== 'uint8' || stateVar.visibility !== 'public')) - const decimalsFun: FunctionHLAst[] = contract.functions.filter((f: FunctionHLAst) => getFunctionDefinitionName(f.node) === 'decimals' && + if (this.isERC20(contractAbiSignatures)) { + const decimalsVar: VariableDeclarationAstNode[] = contract.stateVariables.filter((stateVar: VariableDeclarationAstNode) => getDeclaredVariableName(stateVar) === 'decimals' && (getDeclaredVariableType(stateVar) !== 'uint8' || stateVar.visibility !== 'public')) + const decimalsFun: FunctionHLAst[] = contract.functions.filter((f: FunctionHLAst) => getFunctionDefinitionName(f.node) === 'decimals' && ( - (f.returns.length === 0 || f.returns.length > 1) || + (f.returns.length === 0 || f.returns.length > 1) || (f.returns.length === 1 && (f.returns[0].type !== 'uint8' || f.node.visibility !== 'public')) ) - ) - if (decimalsVar.length > 0) { - for (const node of decimalsVar) { - warnings.push({ - warning: 'ERC20 contract\'s "decimals" variable should be "uint8" type', - location: node.src, - more: 'https://github.com/ethereum/EIPs/blob/master/EIPS/eip-20.md#decimals' - }) - } - } else if (decimalsFun.length > 0) { - for (const fn of decimalsFun) { - warnings.push({ - warning: 'ERC20 contract\'s "decimals" function should have "uint8" as return type', - location: fn.node.src, - more: 'https://github.com/ethereum/EIPs/blob/master/EIPS/eip-20.md#decimals' - }) - } - } - } - }) - return warnings - } + ) + if (decimalsVar.length > 0) { + for (const node of decimalsVar) { + warnings.push({ + warning: 'ERC20 contract\'s "decimals" variable should be "uint8" type', + location: node.src, + more: 'https://github.com/ethereum/EIPs/blob/master/EIPS/eip-20.md#decimals' + }) + } + } else if (decimalsFun.length > 0) { + for (const fn of decimalsFun) { + warnings.push({ + warning: 'ERC20 contract\'s "decimals" function should have "uint8" as return type', + location: fn.node.src, + more: 'https://github.com/ethereum/EIPs/blob/master/EIPS/eip-20.md#decimals' + }) + } + } + } + }) + return warnings + } - private isERC20 (funSignatures: string[]): boolean { - return funSignatures.includes('totalSupply()') && + private isERC20 (funSignatures: string[]): boolean { + return funSignatures.includes('totalSupply()') && funSignatures.includes('balanceOf(address)') && funSignatures.includes('transfer(address,uint256)') && funSignatures.includes('transferFrom(address,address,uint256)') && funSignatures.includes('approve(address,uint256)') && funSignatures.includes('allowance(address,address)') - } + } } diff --git a/libs/remix-analyzer/src/solidity-analyzer/modules/etherTransferInLoop.ts b/libs/remix-analyzer/src/solidity-analyzer/modules/etherTransferInLoop.ts index a8f8ae1d55..5cef2a59e7 100644 --- a/libs/remix-analyzer/src/solidity-analyzer/modules/etherTransferInLoop.ts +++ b/libs/remix-analyzer/src/solidity-analyzer/modules/etherTransferInLoop.ts @@ -2,45 +2,45 @@ import category from './categories' import algorithm from './algorithmCategories' import { isLoop, isTransfer, getCompilerVersion } from './staticAnalysisCommon' import { - AnalyzerModule, ModuleAlgorithm, ModuleCategory, ReportObj, CompilationResult, ForStatementAstNode, - WhileStatementAstNode, ExpressionStatementAstNode, SupportedVersion + AnalyzerModule, ModuleAlgorithm, ModuleCategory, ReportObj, CompilationResult, ForStatementAstNode, + WhileStatementAstNode, ExpressionStatementAstNode, SupportedVersion } from './../../types' export default class etherTransferInLoop implements AnalyzerModule { - relevantNodes: ExpressionStatementAstNode[] = [] - name = 'Ether transfer in loop: ' - description = 'Transferring Ether in a for/while/do-while loop' - category: ModuleCategory = category.GAS - algorithm: ModuleAlgorithm = algorithm.EXACT - version: SupportedVersion = { - start: '0.4.12' - } + relevantNodes: ExpressionStatementAstNode[] = [] + name = 'Ether transfer in loop: ' + description = 'Transferring Ether in a for/while/do-while loop' + category: ModuleCategory = category.GAS + algorithm: ModuleAlgorithm = algorithm.EXACT + version: SupportedVersion = { + start: '0.4.12' + } - visit (node: ForStatementAstNode | WhileStatementAstNode): void { - let transferNodes: ExpressionStatementAstNode[] = [] - if (isLoop(node)) { - if (node.body && node.body.nodeType === 'Block') { - transferNodes = node.body.statements.filter(child => - (child.nodeType === 'ExpressionStatement' && + visit (node: ForStatementAstNode | WhileStatementAstNode): void { + let transferNodes: ExpressionStatementAstNode[] = [] + if (isLoop(node)) { + if (node.body && node.body.nodeType === 'Block') { + transferNodes = node.body.statements.filter(child => + (child.nodeType === 'ExpressionStatement' && child.expression.nodeType === 'FunctionCall' && isTransfer(child.expression.expression))) - } else if (node.body && node.body.nodeType === 'ExpressionStatement' && node.body.expression.nodeType === 'FunctionCall' && isTransfer(node.body.expression.expression)) { transferNodes.push(node.body) } - // When loop body is described without braces - if (transferNodes.length > 0) { - this.relevantNodes.push(...transferNodes) - } - } + } else if (node.body && node.body.nodeType === 'ExpressionStatement' && node.body.expression.nodeType === 'FunctionCall' && isTransfer(node.body.expression.expression)) { transferNodes.push(node.body) } + // When loop body is described without braces + if (transferNodes.length > 0) { + this.relevantNodes.push(...transferNodes) + } } + } - // eslint-disable-next-line @typescript-eslint/no-unused-vars - report (compilationResults: CompilationResult): ReportObj[] { - const version = getCompilerVersion(compilationResults.contracts) - return this.relevantNodes.map((node) => { - return { - warning: 'Ether payout should not be done in a loop: Due to the block gas limit, transactions can only consume a certain amount of gas. The number of iterations in a loop can grow beyond the block gas limit which can cause the complete contract to be stalled at a certain point. If required then make sure that number of iterations are low and you trust each address involved.', - location: node.src, - more: `https://solidity.readthedocs.io/en/${version}/security-considerations.html#gas-limit-and-loops` - } - }) - } + // eslint-disable-next-line @typescript-eslint/no-unused-vars + report (compilationResults: CompilationResult): ReportObj[] { + const version = getCompilerVersion(compilationResults.contracts) + return this.relevantNodes.map((node) => { + return { + warning: 'Ether payout should not be done in a loop: Due to the block gas limit, transactions can only consume a certain amount of gas. The number of iterations in a loop can grow beyond the block gas limit which can cause the complete contract to be stalled at a certain point. If required then make sure that number of iterations are low and you trust each address involved.', + location: node.src, + more: `https://solidity.readthedocs.io/en/${version}/security-considerations.html#gas-limit-and-loops` + } + }) + } } diff --git a/libs/remix-analyzer/src/solidity-analyzer/modules/forLoopIteratesOverDynamicArray.ts b/libs/remix-analyzer/src/solidity-analyzer/modules/forLoopIteratesOverDynamicArray.ts index ff85469cf2..d24bf75c9f 100644 --- a/libs/remix-analyzer/src/solidity-analyzer/modules/forLoopIteratesOverDynamicArray.ts +++ b/libs/remix-analyzer/src/solidity-analyzer/modules/forLoopIteratesOverDynamicArray.ts @@ -4,34 +4,34 @@ import { isDynamicArrayLengthAccess, getCompilerVersion } from './staticAnalysis import { AnalyzerModule, ModuleAlgorithm, ModuleCategory, ReportObj, CompilationResult, ForStatementAstNode, SupportedVersion } from './../../types' export default class forLoopIteratesOverDynamicArray implements AnalyzerModule { - relevantNodes: ForStatementAstNode[] = [] - name = 'For loop over dynamic array: ' - description = 'Iterations depend on dynamic array\'s size' - category: ModuleCategory = category.GAS - algorithm: ModuleAlgorithm = algorithm.EXACT - version: SupportedVersion = { - start: '0.4.12' - } + relevantNodes: ForStatementAstNode[] = [] + name = 'For loop over dynamic array: ' + description = 'Iterations depend on dynamic array\'s size' + category: ModuleCategory = category.GAS + algorithm: ModuleAlgorithm = algorithm.EXACT + version: SupportedVersion = { + start: '0.4.12' + } - visit (node: ForStatementAstNode): void { - const { condition } = node - // Check if condition is `i < array.length - 1` - if ((condition && condition.nodeType === 'BinaryOperation' && condition.rightExpression.nodeType === 'BinaryOperation' && isDynamicArrayLengthAccess(condition.rightExpression.leftExpression)) || + visit (node: ForStatementAstNode): void { + const { condition } = node + // Check if condition is `i < array.length - 1` + if ((condition && condition.nodeType === 'BinaryOperation' && condition.rightExpression.nodeType === 'BinaryOperation' && isDynamicArrayLengthAccess(condition.rightExpression.leftExpression)) || // or condition is `i < array.length` (condition && condition.nodeType === 'BinaryOperation' && isDynamicArrayLengthAccess(condition.rightExpression))) { - this.relevantNodes.push(node) - } + this.relevantNodes.push(node) } + } - // eslint-disable-next-line @typescript-eslint/no-unused-vars - report (compilationResults: CompilationResult): ReportObj[] { - const version = getCompilerVersion(compilationResults.contracts) - return this.relevantNodes.map((node) => { - return { - warning: 'Loops that do not have a fixed number of iterations, for example, loops that depend on storage values, have to be used carefully. Due to the block gas limit, transactions can only consume a certain amount of gas. The number of iterations in a loop can grow beyond the block gas limit which can cause the complete contract to be stalled at a certain point. \n Additionally, using unbounded loops incurs in a lot of avoidable gas costs. Carefully test how many items at maximum you can pass to such functions to make it successful.', - location: node.src, - more: `https://solidity.readthedocs.io/en/${version}/security-considerations.html#gas-limit-and-loops` - } - }) - } + // eslint-disable-next-line @typescript-eslint/no-unused-vars + report (compilationResults: CompilationResult): ReportObj[] { + const version = getCompilerVersion(compilationResults.contracts) + return this.relevantNodes.map((node) => { + return { + warning: 'Loops that do not have a fixed number of iterations, for example, loops that depend on storage values, have to be used carefully. Due to the block gas limit, transactions can only consume a certain amount of gas. The number of iterations in a loop can grow beyond the block gas limit which can cause the complete contract to be stalled at a certain point. \n Additionally, using unbounded loops incurs in a lot of avoidable gas costs. Carefully test how many items at maximum you can pass to such functions to make it successful.', + location: node.src, + more: `https://solidity.readthedocs.io/en/${version}/security-considerations.html#gas-limit-and-loops` + } + }) + } } diff --git a/libs/remix-analyzer/src/solidity-analyzer/modules/functionCallGraph.ts b/libs/remix-analyzer/src/solidity-analyzer/modules/functionCallGraph.ts index a83666d68b..d30fa7972a 100644 --- a/libs/remix-analyzer/src/solidity-analyzer/modules/functionCallGraph.ts +++ b/libs/remix-analyzer/src/solidity-analyzer/modules/functionCallGraph.ts @@ -2,8 +2,8 @@ import { FunctionHLAst, ContractHLAst, FunctionCallGraph, ContractCallGraph, Context, FunctionCallAstNode } from '../../types' import { - isLocalCallGraphRelevantNode, isExternalDirectCall, getFullQualifiedFunctionCallIdent, - getFullQuallyfiedFuncDefinitionIdent, getContractName + isLocalCallGraphRelevantNode, isExternalDirectCall, getFullQualifiedFunctionCallIdent, + getFullQuallyfiedFuncDefinitionIdent, getContractName } from './staticAnalysisCommon' type filterNodesFunction = (node: FunctionCallAstNode) => boolean @@ -11,16 +11,16 @@ type NodeIdentFunction = (node: FunctionCallAstNode) => string type FunDefIdentFunction = (node: FunctionHLAst) => string function buildLocalFuncCallGraphInternal (functions: FunctionHLAst[], nodeFilter: filterNodesFunction, extractNodeIdent: NodeIdentFunction, extractFuncDefIdent: FunDefIdentFunction): Record { - const callGraph: Record = {} - functions.forEach((func: FunctionHLAst) => { - const calls: string[] = func.relevantNodes - .filter(nodeFilter) - .map(extractNodeIdent) - .filter((name: string) => name !== extractFuncDefIdent(func)) // filter self recursive call + const callGraph: Record = {} + functions.forEach((func: FunctionHLAst) => { + const calls: string[] = func.relevantNodes + .filter(nodeFilter) + .map(extractNodeIdent) + .filter((name: string) => name !== extractFuncDefIdent(func)) // filter self recursive call - callGraph[extractFuncDefIdent(func)] = { node: func, calls: calls } - }) - return callGraph + callGraph[extractFuncDefIdent(func)] = { node: func, calls: calls } + }) + return callGraph } /** @@ -48,15 +48,15 @@ function buildLocalFuncCallGraphInternal (functions: FunctionHLAst[], nodeFilter * @return {map (string -> Contract Call Graph)} returns map from contract name to contract call graph */ export function buildGlobalFuncCallGraph (contracts: ContractHLAst[]): Record { - const callGraph: Record = {} - contracts.forEach((contract: ContractHLAst) => { - const filterNodes: filterNodesFunction = (node: FunctionCallAstNode) => { return isLocalCallGraphRelevantNode(node) || isExternalDirectCall(node) } - const getNodeIdent: NodeIdentFunction = (node: FunctionCallAstNode) => { return getFullQualifiedFunctionCallIdent(contract.node, node) } - const getFunDefIdent: FunDefIdentFunction = (funcDef: FunctionHLAst) => { return getFullQuallyfiedFuncDefinitionIdent(contract.node, funcDef.node, funcDef.parameters) } + const callGraph: Record = {} + contracts.forEach((contract: ContractHLAst) => { + const filterNodes: filterNodesFunction = (node: FunctionCallAstNode) => { return isLocalCallGraphRelevantNode(node) || isExternalDirectCall(node) } + const getNodeIdent: NodeIdentFunction = (node: FunctionCallAstNode) => { return getFullQualifiedFunctionCallIdent(contract.node, node) } + const getFunDefIdent: FunDefIdentFunction = (funcDef: FunctionHLAst) => { return getFullQuallyfiedFuncDefinitionIdent(contract.node, funcDef.node, funcDef.parameters) } - callGraph[getContractName(contract.node)] = { contract: contract, functions: buildLocalFuncCallGraphInternal(contract.functions, filterNodes, getNodeIdent, getFunDefIdent) } - }) - return callGraph + callGraph[getContractName(contract.node)] = { contract: contract, functions: buildLocalFuncCallGraphInternal(contract.functions, filterNodes, getNodeIdent, getFunDefIdent) } + }) + return callGraph } /** @@ -68,47 +68,47 @@ export function buildGlobalFuncCallGraph (contracts: ContractHLAst[]): Record, funcName: string, context: Context, nodeCheck: ((node: any, context: Context) => boolean)): boolean { - return analyseCallGraphInternal(callGraph, funcName, context, (a, b) => a || b, nodeCheck, {}) + return analyseCallGraphInternal(callGraph, funcName, context, (a, b) => a || b, nodeCheck, {}) } function analyseCallGraphInternal (callGraph: Record, funcName: string, context: Context, combinator, nodeCheck: ((node: any, context: Context) => boolean), visited : Record): boolean { - const current: FunctionCallGraph | undefined = resolveCallGraphSymbol(callGraph, funcName) + const current: FunctionCallGraph | undefined = resolveCallGraphSymbol(callGraph, funcName) - if (current === undefined || visited[funcName] === true) return true - visited[funcName] = true + if (current === undefined || visited[funcName] === true) return true + visited[funcName] = true - return combinator(current.node.relevantNodes.reduce((acc, val) => combinator(acc, nodeCheck(val, context)), false), - current.calls.reduce((acc, val) => combinator(acc, analyseCallGraphInternal(callGraph, val, context, combinator, nodeCheck, visited)), false)) + return combinator(current.node.relevantNodes.reduce((acc, val) => combinator(acc, nodeCheck(val, context)), false), + current.calls.reduce((acc, val) => combinator(acc, analyseCallGraphInternal(callGraph, val, context, combinator, nodeCheck, visited)), false)) } export function resolveCallGraphSymbol (callGraph: Record, funcName: string): FunctionCallGraph | undefined { - return resolveCallGraphSymbolInternal(callGraph, funcName, false) + return resolveCallGraphSymbolInternal(callGraph, funcName, false) } function resolveCallGraphSymbolInternal (callGraph: Record, funcName: string, silent: boolean): FunctionCallGraph | undefined { - let current: FunctionCallGraph | null = null - if (funcName.includes('.')) { - const parts: string[] = funcName.split('.') - const contractPart: string = parts[0] - const functionPart: string = parts[1] - const currentContract: ContractCallGraph = callGraph[contractPart] - if (!(currentContract === undefined)) { - current = currentContract.functions[funcName] - // resolve inheritance hierarchy - if (current === undefined) { - // resolve inheritance lookup in linearized fashion - const inheritsFromNames: string[] = currentContract.contract.inheritsFrom.reverse() - for (let i = 0; i < inheritsFromNames.length; i++) { - const res: FunctionCallGraph | undefined = resolveCallGraphSymbolInternal(callGraph, inheritsFromNames[i] + '.' + functionPart, true) - if (!(res === undefined)) return res - } - } - } else { - if (!silent) console.log(`static analysis functionCallGraph.js: Contract ${contractPart} not found in function call graph.`) + let current: FunctionCallGraph | null = null + if (funcName.includes('.')) { + const parts: string[] = funcName.split('.') + const contractPart: string = parts[0] + const functionPart: string = parts[1] + const currentContract: ContractCallGraph = callGraph[contractPart] + if (!(currentContract === undefined)) { + current = currentContract.functions[funcName] + // resolve inheritance hierarchy + if (current === undefined) { + // resolve inheritance lookup in linearized fashion + const inheritsFromNames: string[] = currentContract.contract.inheritsFrom.reverse() + for (let i = 0; i < inheritsFromNames.length; i++) { + const res: FunctionCallGraph | undefined = resolveCallGraphSymbolInternal(callGraph, inheritsFromNames[i] + '.' + functionPart, true) + if (!(res === undefined)) return res } + } } else { - throw new Error('functionCallGraph.js: function does not have full qualified name.') + if (!silent) console.log(`static analysis functionCallGraph.js: Contract ${contractPart} not found in function call graph.`) } - if (current === undefined && !silent) console.log(`static analysis functionCallGraph.js: ${funcName} not found in function call graph.`) - if (current !== null) { return current } + } else { + throw new Error('functionCallGraph.js: function does not have full qualified name.') + } + if (current === undefined && !silent) console.log(`static analysis functionCallGraph.js: ${funcName} not found in function call graph.`) + if (current !== null) { return current } } diff --git a/libs/remix-analyzer/src/solidity-analyzer/modules/gasCosts.ts b/libs/remix-analyzer/src/solidity-analyzer/modules/gasCosts.ts index 882860d736..c8761f57cc 100644 --- a/libs/remix-analyzer/src/solidity-analyzer/modules/gasCosts.ts +++ b/libs/remix-analyzer/src/solidity-analyzer/modules/gasCosts.ts @@ -2,90 +2,90 @@ import category from './categories' import algorithm from './algorithmCategories' import { getFunctionDefinitionName, helpers, isVariableTurnedIntoGetter, getMethodParamsSplittedTypeDesc } from './staticAnalysisCommon' import { - ModuleAlgorithm, ModuleCategory, ReportObj, CompilationResult, CompiledContract, AnalyzerModule, - FunctionDefinitionAstNode, VariableDeclarationAstNode, SupportedVersion + ModuleAlgorithm, ModuleCategory, ReportObj, CompilationResult, CompiledContract, AnalyzerModule, + FunctionDefinitionAstNode, VariableDeclarationAstNode, SupportedVersion } from './../../types' export default class gasCosts implements AnalyzerModule { - name = 'Gas costs: ' - description = 'Too high gas requirement of functions' - category: ModuleCategory = category.GAS - algorithm: ModuleAlgorithm = algorithm.EXACT - version: SupportedVersion = { - start: '0.4.12' - } + name = 'Gas costs: ' + description = 'Too high gas requirement of functions' + category: ModuleCategory = category.GAS + algorithm: ModuleAlgorithm = algorithm.EXACT + version: SupportedVersion = { + start: '0.4.12' + } - warningNodes: any[] = [] - visit (node: FunctionDefinitionAstNode | VariableDeclarationAstNode): void { - if ((node.nodeType === 'FunctionDefinition' && node.kind !== 'constructor' && node.implemented) || + warningNodes: any[] = [] + visit (node: FunctionDefinitionAstNode | VariableDeclarationAstNode): void { + if ((node.nodeType === 'FunctionDefinition' && node.kind !== 'constructor' && node.implemented) || (node.nodeType === 'VariableDeclaration' && isVariableTurnedIntoGetter(node))) { this.warningNodes.push(node) } - } + } - report (compilationResults: CompilationResult): ReportObj[] { - const report: ReportObj[] = [] - const methodsWithSignature: Record[] = this.warningNodes.map(node => { - let signature: string - if (node.nodeType === 'FunctionDefinition') { - const functionName: string = getFunctionDefinitionName(node) - signature = helpers.buildAbiSignature(functionName, getMethodParamsSplittedTypeDesc(node, compilationResults.contracts)) - } else { signature = node.name + '()' } + report (compilationResults: CompilationResult): ReportObj[] { + const report: ReportObj[] = [] + const methodsWithSignature: Record[] = this.warningNodes.map(node => { + let signature: string + if (node.nodeType === 'FunctionDefinition') { + const functionName: string = getFunctionDefinitionName(node) + signature = helpers.buildAbiSignature(functionName, getMethodParamsSplittedTypeDesc(node, compilationResults.contracts)) + } else { signature = node.name + '()' } - return { - name: node.name, - src: node.src, - signature: signature - } - }) - for (const method of methodsWithSignature) { - for (const filename in compilationResults.contracts) { - for (const contractName in compilationResults.contracts[filename]) { - const contract: CompiledContract = compilationResults.contracts[filename][contractName] - const methodGas: Record | undefined = this.checkMethodGas(contract, method.signature) - if (methodGas && methodGas.isInfinite) { - if (methodGas.isFallback) { - report.push({ - warning: `Fallback function of contract ${contractName} requires too much gas (${methodGas.msg}). + return { + name: node.name, + src: node.src, + signature: signature + } + }) + for (const method of methodsWithSignature) { + for (const filename in compilationResults.contracts) { + for (const contractName in compilationResults.contracts[filename]) { + const contract: CompiledContract = compilationResults.contracts[filename][contractName] + const methodGas: Record | undefined = this.checkMethodGas(contract, method.signature) + if (methodGas && methodGas.isInfinite) { + if (methodGas.isFallback) { + report.push({ + warning: `Fallback function of contract ${contractName} requires too much gas (${methodGas.msg}). If the fallback function requires more than 2300 gas, the contract cannot receive Ether.`, - location: method.src - }) - } else { - report.push({ - warning: `Gas requirement of function ${contractName}.${method.name} ${methodGas.msg}: + location: method.src + }) + } else { + report.push({ + warning: `Gas requirement of function ${contractName}.${method.name} ${methodGas.msg}: If the gas requirement of a function is higher than the block gas limit, it cannot be executed. Please avoid loops in your functions or actions that modify large areas of storage (this includes clearing or copying arrays in storage)`, - location: method.src - }) - } - } else continue - } + location: method.src + }) } + } else continue } - return report + } } + return report + } - private checkMethodGas (contract: CompiledContract, methodSignature: string): Record | undefined { - if (contract.evm && contract.evm.gasEstimates && contract.evm.gasEstimates.external) { - if (methodSignature === '()') { - const fallback: string = contract.evm.gasEstimates.external[''] - if (fallback !== undefined && (fallback === null || parseInt(fallback) >= 2100 || fallback === 'infinite')) { - return { - isInfinite: true, - isFallback: true, - msg: fallback - } - } - } else { - const gas: string = contract.evm.gasEstimates.external[methodSignature] - const gasString: string = gas === null ? 'unknown or not constant' : 'is ' + gas - if (gas === null || parseInt(gas) >= 3000000 || gas === 'infinite') { - return { - isInfinite: true, - isFallback: false, - msg: gasString - } - } - } + private checkMethodGas (contract: CompiledContract, methodSignature: string): Record | undefined { + if (contract.evm && contract.evm.gasEstimates && contract.evm.gasEstimates.external) { + if (methodSignature === '()') { + const fallback: string = contract.evm.gasEstimates.external[''] + if (fallback !== undefined && (fallback === null || parseInt(fallback) >= 2100 || fallback === 'infinite')) { + return { + isInfinite: true, + isFallback: true, + msg: fallback + } + } + } else { + const gas: string = contract.evm.gasEstimates.external[methodSignature] + const gasString: string = gas === null ? 'unknown or not constant' : 'is ' + gas + if (gas === null || parseInt(gas) >= 3000000 || gas === 'infinite') { + return { + isInfinite: true, + isFallback: false, + msg: gasString + } } + } } + } } diff --git a/libs/remix-analyzer/src/solidity-analyzer/modules/guardConditions.ts b/libs/remix-analyzer/src/solidity-analyzer/modules/guardConditions.ts index b2c67fbef4..a98518fdb5 100644 --- a/libs/remix-analyzer/src/solidity-analyzer/modules/guardConditions.ts +++ b/libs/remix-analyzer/src/solidity-analyzer/modules/guardConditions.ts @@ -4,28 +4,28 @@ import algorithm from './algorithmCategories' import { AnalyzerModule, ModuleAlgorithm, ModuleCategory, ReportObj, CompilationResult, FunctionCallAstNode, SupportedVersion } from './../../types' export default class guardConditions implements AnalyzerModule { - guards: FunctionCallAstNode[] = [] - name = 'Guard conditions: ' - description = 'Ensure appropriate use of require/assert' - category: ModuleCategory = category.MISC - algorithm: ModuleAlgorithm = algorithm.EXACT - version: SupportedVersion = { - start: '0.4.12' - } + guards: FunctionCallAstNode[] = [] + name = 'Guard conditions: ' + description = 'Ensure appropriate use of require/assert' + category: ModuleCategory = category.MISC + algorithm: ModuleAlgorithm = algorithm.EXACT + version: SupportedVersion = { + start: '0.4.12' + } - visit (node: FunctionCallAstNode): void { - if (isRequireCall(node) || isAssertCall(node)) this.guards.push(node) - } + visit (node: FunctionCallAstNode): void { + if (isRequireCall(node) || isAssertCall(node)) this.guards.push(node) + } - // eslint-disable-next-line @typescript-eslint/no-unused-vars - report (compilationResults: CompilationResult): ReportObj[] { - const version = getCompilerVersion(compilationResults.contracts) - return this.guards.map((node) => { - return { - warning: 'Use "assert(x)" if you never ever want x to be false, not in any circumstance (apart from a bug in your code). Use "require(x)" if x can be false, due to e.g. invalid input or a failing external component.', - location: node.src, - more: `https://solidity.readthedocs.io/en/${version}/control-structures.html#error-handling-assert-require-revert-and-exceptions` - } - }) - } + // eslint-disable-next-line @typescript-eslint/no-unused-vars + report (compilationResults: CompilationResult): ReportObj[] { + const version = getCompilerVersion(compilationResults.contracts) + return this.guards.map((node) => { + return { + warning: 'Use "assert(x)" if you never ever want x to be false, not in any circumstance (apart from a bug in your code). Use "require(x)" if x can be false, due to e.g. invalid input or a failing external component.', + location: node.src, + more: `https://solidity.readthedocs.io/en/${version}/control-structures.html#error-handling-assert-require-revert-and-exceptions` + } + }) + } } diff --git a/libs/remix-analyzer/src/solidity-analyzer/modules/inlineAssembly.ts b/libs/remix-analyzer/src/solidity-analyzer/modules/inlineAssembly.ts index 851fc2f10d..63e30f9bbb 100644 --- a/libs/remix-analyzer/src/solidity-analyzer/modules/inlineAssembly.ts +++ b/libs/remix-analyzer/src/solidity-analyzer/modules/inlineAssembly.ts @@ -4,29 +4,29 @@ import { AnalyzerModule, ModuleAlgorithm, ModuleCategory, ReportObj, Compilation import { getCompilerVersion } from './staticAnalysisCommon' export default class inlineAssembly implements AnalyzerModule { - inlineAssNodes: InlineAssemblyAstNode[] = [] - name = 'Inline assembly: ' - description = 'Inline assembly used' - category: ModuleCategory = category.SECURITY - algorithm: ModuleAlgorithm = algorithm.EXACT - version: SupportedVersion = { - start: '0.4.12' - } + inlineAssNodes: InlineAssemblyAstNode[] = [] + name = 'Inline assembly: ' + description = 'Inline assembly used' + category: ModuleCategory = category.SECURITY + algorithm: ModuleAlgorithm = algorithm.EXACT + version: SupportedVersion = { + start: '0.4.12' + } - visit (node: InlineAssemblyAstNode): void { - if (node.nodeType === 'InlineAssembly') this.inlineAssNodes.push(node) - } + visit (node: InlineAssemblyAstNode): void { + if (node.nodeType === 'InlineAssembly') this.inlineAssNodes.push(node) + } - // eslint-disable-next-line @typescript-eslint/no-unused-vars - report (compilationResults: CompilationResult): ReportObj[] { - const version = getCompilerVersion(compilationResults.contracts) - return this.inlineAssNodes.map((node) => { - return { - warning: `The Contract uses inline assembly, this is only advised in rare cases. + // eslint-disable-next-line @typescript-eslint/no-unused-vars + report (compilationResults: CompilationResult): ReportObj[] { + const version = getCompilerVersion(compilationResults.contracts) + return this.inlineAssNodes.map((node) => { + return { + warning: `The Contract uses inline assembly, this is only advised in rare cases. Additionally static analysis modules do not parse inline Assembly, this can lead to wrong analysis results.`, - location: node.src, - more: `https://solidity.readthedocs.io/en/${version}/assembly.html` - } - }) - } + location: node.src, + more: `https://solidity.readthedocs.io/en/${version}/assembly.html` + } + }) + } } diff --git a/libs/remix-analyzer/src/solidity-analyzer/modules/intDivisionTruncate.ts b/libs/remix-analyzer/src/solidity-analyzer/modules/intDivisionTruncate.ts index b54053202b..0153c86a86 100644 --- a/libs/remix-analyzer/src/solidity-analyzer/modules/intDivisionTruncate.ts +++ b/libs/remix-analyzer/src/solidity-analyzer/modules/intDivisionTruncate.ts @@ -4,26 +4,26 @@ import algorithm from './algorithmCategories' import { AnalyzerModule, ModuleAlgorithm, ModuleCategory, ReportObj, CompilationResult, BinaryOperationAstNode, SupportedVersion } from './../../types' export default class intDivisionTruncate implements AnalyzerModule { - warningNodes: BinaryOperationAstNode[] = [] - name = 'Data truncated: ' - description = 'Division on int/uint values truncates the result' - category: ModuleCategory = category.MISC - algorithm: ModuleAlgorithm = algorithm.EXACT - version: SupportedVersion = { - start: '0.4.12' - } + warningNodes: BinaryOperationAstNode[] = [] + name = 'Data truncated: ' + description = 'Division on int/uint values truncates the result' + category: ModuleCategory = category.MISC + algorithm: ModuleAlgorithm = algorithm.EXACT + version: SupportedVersion = { + start: '0.4.12' + } - visit (node: BinaryOperationAstNode): void { - if (isIntDivision(node)) this.warningNodes.push(node) - } + visit (node: BinaryOperationAstNode): void { + if (isIntDivision(node)) this.warningNodes.push(node) + } - // eslint-disable-next-line @typescript-eslint/no-unused-vars - report (compilationResults: CompilationResult): ReportObj[] { - return this.warningNodes.map((item) => { - return { - warning: 'Division of integer values yields an integer value again. That means e.g. 10 / 100 = 0 instead of 0.1 since the result is an integer again. This does not hold for division of (only) literal values since those yield rational constants.', - location: item.src - } - }) - } + // eslint-disable-next-line @typescript-eslint/no-unused-vars + report (compilationResults: CompilationResult): ReportObj[] { + return this.warningNodes.map((item) => { + return { + warning: 'Division of integer values yields an integer value again. That means e.g. 10 / 100 = 0 instead of 0.1 since the result is an integer again. This does not hold for division of (only) literal values since those yield rational constants.', + location: item.src + } + }) + } } diff --git a/libs/remix-analyzer/src/solidity-analyzer/modules/list.ts b/libs/remix-analyzer/src/solidity-analyzer/modules/list.ts index a6687343be..4d1fee0071 100644 --- a/libs/remix-analyzer/src/solidity-analyzer/modules/list.ts +++ b/libs/remix-analyzer/src/solidity-analyzer/modules/list.ts @@ -21,25 +21,25 @@ import etherTransferInLoop from './etherTransferInLoop' import intDivisionTruncate from './intDivisionTruncate' export default [ - txOrigin, - gasCosts, - thisLocal, - checksEffectsInteraction, - erc20Decimals, - constantFunctions, - similarVariableNames, - inlineAssembly, - blockTimestamp, - lowLevelCalls, - blockBlockhash, - noReturn, - selfdestruct, - guardConditions, - deleteDynamicArrays, - assignAndCompare, - stringBytesLength, - deleteFromDynamicArray, - forLoopIteratesOverDynamicArray, - etherTransferInLoop, - intDivisionTruncate + txOrigin, + gasCosts, + thisLocal, + checksEffectsInteraction, + erc20Decimals, + constantFunctions, + similarVariableNames, + inlineAssembly, + blockTimestamp, + lowLevelCalls, + blockBlockhash, + noReturn, + selfdestruct, + guardConditions, + deleteDynamicArrays, + assignAndCompare, + stringBytesLength, + deleteFromDynamicArray, + forLoopIteratesOverDynamicArray, + etherTransferInLoop, + intDivisionTruncate ] diff --git a/libs/remix-analyzer/src/solidity-analyzer/modules/lowLevelCalls.ts b/libs/remix-analyzer/src/solidity-analyzer/modules/lowLevelCalls.ts index 04193a6afb..df8a26189a 100644 --- a/libs/remix-analyzer/src/solidity-analyzer/modules/lowLevelCalls.ts +++ b/libs/remix-analyzer/src/solidity-analyzer/modules/lowLevelCalls.ts @@ -9,67 +9,67 @@ interface llcNode { } export default class lowLevelCalls implements AnalyzerModule { - llcNodes: llcNode[] = [] - name = 'Low level calls: ' - description = 'Should only be used by experienced devs' - category: ModuleCategory = category.SECURITY - algorithm: ModuleAlgorithm = algorithm.EXACT - version: SupportedVersion = { - start: '0.4.12' - } + llcNodes: llcNode[] = [] + name = 'Low level calls: ' + description = 'Should only be used by experienced devs' + category: ModuleCategory = category.SECURITY + algorithm: ModuleAlgorithm = algorithm.EXACT + version: SupportedVersion = { + start: '0.4.12' + } - visit (node : MemberAccessAstNode): void { - if (isLLCall(node)) { - this.llcNodes.push({ node: node, type: lowLevelCallTypes.CALL }) - } else if (isLLDelegatecall(node)) { - this.llcNodes.push({ node: node, type: lowLevelCallTypes.DELEGATECALL }) - } else if (isLLSend(node)) { - this.llcNodes.push({ node: node, type: lowLevelCallTypes.SEND }) - } else if (isLLDelegatecall04(node)) { - this.llcNodes.push({ node: node, type: lowLevelCallTypes.DELEGATECALL }) - } else if (isLLSend04(node)) { - this.llcNodes.push({ node: node, type: lowLevelCallTypes.SEND }) - } else if (isLLCall04(node)) { - this.llcNodes.push({ node: node, type: lowLevelCallTypes.CALL }) - } else if (isLLCallcode(node)) { - this.llcNodes.push({ node: node, type: lowLevelCallTypes.CALLCODE }) - } + visit (node : MemberAccessAstNode): void { + if (isLLCall(node)) { + this.llcNodes.push({ node: node, type: lowLevelCallTypes.CALL }) + } else if (isLLDelegatecall(node)) { + this.llcNodes.push({ node: node, type: lowLevelCallTypes.DELEGATECALL }) + } else if (isLLSend(node)) { + this.llcNodes.push({ node: node, type: lowLevelCallTypes.SEND }) + } else if (isLLDelegatecall04(node)) { + this.llcNodes.push({ node: node, type: lowLevelCallTypes.DELEGATECALL }) + } else if (isLLSend04(node)) { + this.llcNodes.push({ node: node, type: lowLevelCallTypes.SEND }) + } else if (isLLCall04(node)) { + this.llcNodes.push({ node: node, type: lowLevelCallTypes.CALL }) + } else if (isLLCallcode(node)) { + this.llcNodes.push({ node: node, type: lowLevelCallTypes.CALLCODE }) } + } - // eslint-disable-next-line @typescript-eslint/no-unused-vars - report (compilationResults: CompilationResult): ReportObj[] { - const version = getCompilerVersion(compilationResults.contracts) - return this.llcNodes.map((item, i) => { - let text = '' - let morehref = '' - switch (item.type) { - case lowLevelCallTypes.CALL: - text = `Use of "call": should be avoided whenever possible. + // eslint-disable-next-line @typescript-eslint/no-unused-vars + report (compilationResults: CompilationResult): ReportObj[] { + const version = getCompilerVersion(compilationResults.contracts) + return this.llcNodes.map((item, i) => { + let text = '' + let morehref = '' + switch (item.type) { + case lowLevelCallTypes.CALL: + text = `Use of "call": should be avoided whenever possible. It can lead to unexpected behavior if return value is not handled properly. Please use Direct Calls via specifying the called contract's interface.` - morehref = `https://solidity.readthedocs.io/en/${version}/control-structures.html?#external-function-calls` - break - case lowLevelCallTypes.CALLCODE: - text = `Use of "callcode": should be avoided whenever possible. + morehref = `https://solidity.readthedocs.io/en/${version}/control-structures.html?#external-function-calls` + break + case lowLevelCallTypes.CALLCODE: + text = `Use of "callcode": should be avoided whenever possible. External code, that is called can change the state of the calling contract and send ether from the caller's balance. If this is wanted behaviour, use the Solidity library feature if possible.` - morehref = `https://solidity.readthedocs.io/en/${version}/contracts.html#libraries` - break - case lowLevelCallTypes.DELEGATECALL: - text = `Use of "delegatecall": should be avoided whenever possible. + morehref = `https://solidity.readthedocs.io/en/${version}/contracts.html#libraries` + break + case lowLevelCallTypes.DELEGATECALL: + text = `Use of "delegatecall": should be avoided whenever possible. External code, that is called can change the state of the calling contract and send ether from the caller's balance. If this is wanted behaviour, use the Solidity library feature if possible.` - morehref = `https://solidity.readthedocs.io/en/${version}/contracts.html#libraries` - break - case lowLevelCallTypes.SEND: - text = `Use of "send": "send" does not throw an exception when not successful, make sure you deal with the failure case accordingly. + morehref = `https://solidity.readthedocs.io/en/${version}/contracts.html#libraries` + break + case lowLevelCallTypes.SEND: + text = `Use of "send": "send" does not throw an exception when not successful, make sure you deal with the failure case accordingly. Use "transfer" whenever failure of the ether transfer should rollback the whole transaction. Note: if you "send/transfer" ether to a contract the fallback function is called, the callees fallback function is very limited due to the limited amount of gas provided by "send/transfer". No state changes are possible but the callee can log the event or revert the transfer. "send/transfer" is syntactic sugar for a "call" to the fallback function with 2300 gas and a specified ether value.` - morehref = `https://solidity.readthedocs.io/en/${version}/security-considerations.html#sending-and-receiving-ether` - break - } - return { warning: text, more: morehref, location: item.node.src } - }) - } + morehref = `https://solidity.readthedocs.io/en/${version}/security-considerations.html#sending-and-receiving-ether` + break + } + return { warning: text, more: morehref, location: item.node.src } + }) + } } diff --git a/libs/remix-analyzer/src/solidity-analyzer/modules/noReturn.ts b/libs/remix-analyzer/src/solidity-analyzer/modules/noReturn.ts index 70588169d3..d9563e366f 100644 --- a/libs/remix-analyzer/src/solidity-analyzer/modules/noReturn.ts +++ b/libs/remix-analyzer/src/solidity-analyzer/modules/noReturn.ts @@ -3,68 +3,68 @@ import { hasFunctionBody, getFullQuallyfiedFuncDefinitionIdent, getEffectedVaria import algorithm from './algorithmCategories' import AbstractAst from './abstractAstView' import { - AnalyzerModule, ModuleAlgorithm, ModuleCategory, ReportObj, ContractHLAst, FunctionHLAst, - VisitFunction, ReportFunction, ReturnAstNode, AssignmentAstNode, SupportedVersion + AnalyzerModule, ModuleAlgorithm, ModuleCategory, ReportObj, ContractHLAst, FunctionHLAst, + VisitFunction, ReportFunction, ReturnAstNode, AssignmentAstNode, SupportedVersion } from './../../types' export default class noReturn implements AnalyzerModule { - name = 'No return: ' - description = 'Function with \'returns\' not returning' - category: ModuleCategory = category.MISC - algorithm: ModuleAlgorithm = algorithm.EXACT - version: SupportedVersion = { - start: '0.4.12' - } + name = 'No return: ' + description = 'Function with \'returns\' not returning' + category: ModuleCategory = category.MISC + algorithm: ModuleAlgorithm = algorithm.EXACT + version: SupportedVersion = { + start: '0.4.12' + } - abstractAst: AbstractAst = new AbstractAst() + abstractAst: AbstractAst = new AbstractAst() - visit: VisitFunction = this.abstractAst.build_visit( - (node: ReturnAstNode | AssignmentAstNode) => node.nodeType === 'Return' || node.nodeType === 'Assignment' - ) + visit: VisitFunction = this.abstractAst.build_visit( + (node: ReturnAstNode | AssignmentAstNode) => node.nodeType === 'Return' || node.nodeType === 'Assignment' + ) - report: ReportFunction = this.abstractAst.build_report(this._report.bind(this)) - private _report (contracts: ContractHLAst[], multipleContractsWithSameName: boolean, version: string): ReportObj[] { - const warnings: ReportObj[] = [] + report: ReportFunction = this.abstractAst.build_report(this._report.bind(this)) + private _report (contracts: ContractHLAst[], multipleContractsWithSameName: boolean, version: string): ReportObj[] { + const warnings: ReportObj[] = [] - contracts.forEach((contract: ContractHLAst) => { - contract.functions.filter((func: FunctionHLAst) => hasFunctionBody(func.node)).forEach((func: FunctionHLAst) => { - const funcName: string = getFullQuallyfiedFuncDefinitionIdent(contract.node, func.node, func.parameters) - if (this.hasNamedAndUnnamedReturns(func)) { - warnings.push({ - warning: `${funcName}: Mixing of named and unnamed return parameters is not advised.`, - location: func.node.src - }) - } else if (this.shouldReturn(func) && !(this.hasReturnStatement(func) || (this.hasNamedReturns(func) && this.hasAssignToAllNamedReturns(func)))) { - warnings.push({ - warning: `${funcName}: Defines a return type but never explicitly returns a value.`, - location: func.node.src - }) - } - }) - }) - return warnings - } + contracts.forEach((contract: ContractHLAst) => { + contract.functions.filter((func: FunctionHLAst) => hasFunctionBody(func.node)).forEach((func: FunctionHLAst) => { + const funcName: string = getFullQuallyfiedFuncDefinitionIdent(contract.node, func.node, func.parameters) + if (this.hasNamedAndUnnamedReturns(func)) { + warnings.push({ + warning: `${funcName}: Mixing of named and unnamed return parameters is not advised.`, + location: func.node.src + }) + } else if (this.shouldReturn(func) && !(this.hasReturnStatement(func) || (this.hasNamedReturns(func) && this.hasAssignToAllNamedReturns(func)))) { + warnings.push({ + warning: `${funcName}: Defines a return type but never explicitly returns a value.`, + location: func.node.src + }) + } + }) + }) + return warnings + } - private shouldReturn (func: FunctionHLAst): boolean { - return func.returns.length > 0 - } + private shouldReturn (func: FunctionHLAst): boolean { + return func.returns.length > 0 + } - private hasReturnStatement (func: FunctionHLAst): boolean { - return func.relevantNodes.filter(n => n.nodeType === 'Return').length > 0 - } + private hasReturnStatement (func: FunctionHLAst): boolean { + return func.relevantNodes.filter(n => n.nodeType === 'Return').length > 0 + } - private hasAssignToAllNamedReturns (func: FunctionHLAst): boolean { - const namedReturns: string[] = func.returns.filter(n => n.name.length > 0).map((n) => n.name) - const assignedVars: string[] = func.relevantNodes.filter(n => n.nodeType === 'Assignment').map(getEffectedVariableName) - const diff: string[] = namedReturns.filter(e => !assignedVars.includes(e)) - return diff.length === 0 - } + private hasAssignToAllNamedReturns (func: FunctionHLAst): boolean { + const namedReturns: string[] = func.returns.filter(n => n.name.length > 0).map((n) => n.name) + const assignedVars: string[] = func.relevantNodes.filter(n => n.nodeType === 'Assignment').map(getEffectedVariableName) + const diff: string[] = namedReturns.filter(e => !assignedVars.includes(e)) + return diff.length === 0 + } - private hasNamedReturns (func: FunctionHLAst): boolean { - return func.returns.filter((n) => n.name.length > 0).length > 0 - } + private hasNamedReturns (func: FunctionHLAst): boolean { + return func.returns.filter((n) => n.name.length > 0).length > 0 + } - private hasNamedAndUnnamedReturns (func: FunctionHLAst): boolean { - return func.returns.filter((n) => n.name.length === 0).length > 0 && this.hasNamedReturns(func) - } + private hasNamedAndUnnamedReturns (func: FunctionHLAst): boolean { + return func.returns.filter((n) => n.name.length === 0).length > 0 && this.hasNamedReturns(func) + } } diff --git a/libs/remix-analyzer/src/solidity-analyzer/modules/selfdestruct.ts b/libs/remix-analyzer/src/solidity-analyzer/modules/selfdestruct.ts index e1ad68ecee..b9776122dc 100644 --- a/libs/remix-analyzer/src/solidity-analyzer/modules/selfdestruct.ts +++ b/libs/remix-analyzer/src/solidity-analyzer/modules/selfdestruct.ts @@ -5,48 +5,48 @@ import AbstractAst from './abstractAstView' import { AnalyzerModule, ModuleAlgorithm, ModuleCategory, ReportObj, ContractHLAst, VisitFunction, ReportFunction, SupportedVersion } from './../../types' export default class selfdestruct implements AnalyzerModule { - name = 'Selfdestruct: ' - description = 'Contracts using destructed contract can be broken' - category: ModuleCategory = category.SECURITY - algorithm: ModuleAlgorithm = algorithm.HEURISTIC - version: SupportedVersion = { - start: '0.4.12' - } + name = 'Selfdestruct: ' + description = 'Contracts using destructed contract can be broken' + category: ModuleCategory = category.SECURITY + algorithm: ModuleAlgorithm = algorithm.HEURISTIC + version: SupportedVersion = { + start: '0.4.12' + } - abstractAst: AbstractAst = new AbstractAst() + abstractAst: AbstractAst = new AbstractAst() - visit: VisitFunction = this.abstractAst.build_visit( - (node: any) => isStatement(node) || (node.nodeType === 'FunctionCall' && isSelfdestructCall(node)) - ) + visit: VisitFunction = this.abstractAst.build_visit( + (node: any) => isStatement(node) || (node.nodeType === 'FunctionCall' && isSelfdestructCall(node)) + ) - report: ReportFunction = this.abstractAst.build_report(this._report.bind(this)) - // eslint-disable-next-line @typescript-eslint/no-unused-vars - private _report (contracts: ContractHLAst[], multipleContractsWithSameName: boolean, version: string): ReportObj[] { - const warnings: ReportObj[] = [] + report: ReportFunction = this.abstractAst.build_report(this._report.bind(this)) + // eslint-disable-next-line @typescript-eslint/no-unused-vars + private _report (contracts: ContractHLAst[], multipleContractsWithSameName: boolean, version: string): ReportObj[] { + const warnings: ReportObj[] = [] - contracts.forEach((contract) => { - contract.functions.forEach((func) => { - let hasSelf = false - func.relevantNodes.forEach((node) => { - if (isSelfdestructCall(node)) { - warnings.push({ - warning: 'Use of selfdestruct: Can block calling contracts unexpectedly. Be especially careful if this contract is planned to be used by other contracts (i.e. library contracts, interactions). Selfdestruction of the callee contract can leave callers in an inoperable state.', - location: node.src, - more: 'https://paritytech.io/blog/security-alert.html' - }) - hasSelf = true - } - if (isStatement(node) && hasSelf) { - warnings.push({ - warning: 'Use of selfdestruct: No code after selfdestruct is executed. Selfdestruct is a terminal.', - location: node.src, - more: `https://solidity.readthedocs.io/en/${version}/introduction-to-smart-contracts.html#deactivate-and-self-destruct` - }) - hasSelf = false - } - }) + contracts.forEach((contract) => { + contract.functions.forEach((func) => { + let hasSelf = false + func.relevantNodes.forEach((node) => { + if (isSelfdestructCall(node)) { + warnings.push({ + warning: 'Use of selfdestruct: Can block calling contracts unexpectedly. Be especially careful if this contract is planned to be used by other contracts (i.e. library contracts, interactions). Selfdestruction of the callee contract can leave callers in an inoperable state.', + location: node.src, + more: 'https://paritytech.io/blog/security-alert.html' }) + hasSelf = true + } + if (isStatement(node) && hasSelf) { + warnings.push({ + warning: 'Use of selfdestruct: No code after selfdestruct is executed. Selfdestruct is a terminal.', + location: node.src, + more: `https://solidity.readthedocs.io/en/${version}/introduction-to-smart-contracts.html#deactivate-and-self-destruct` + }) + hasSelf = false + } }) - return warnings - } + }) + }) + return warnings + } } diff --git a/libs/remix-analyzer/src/solidity-analyzer/modules/similarVariableNames.ts b/libs/remix-analyzer/src/solidity-analyzer/modules/similarVariableNames.ts index 35591dc8b1..76f70d61b6 100644 --- a/libs/remix-analyzer/src/solidity-analyzer/modules/similarVariableNames.ts +++ b/libs/remix-analyzer/src/solidity-analyzer/modules/similarVariableNames.ts @@ -14,87 +14,87 @@ interface SimilarRecord { } export default class similarVariableNames implements AnalyzerModule { - name = 'Similar variable names: ' - description = 'Variable names are too similar' - category: ModuleCategory = category.MISC - algorithm: ModuleAlgorithm = algorithm.EXACT - version: SupportedVersion = { - start: '0.4.12' - } + name = 'Similar variable names: ' + description = 'Variable names are too similar' + category: ModuleCategory = category.MISC + algorithm: ModuleAlgorithm = algorithm.EXACT + version: SupportedVersion = { + start: '0.4.12' + } - abstractAst:AbstractAst = new AbstractAst() + abstractAst:AbstractAst = new AbstractAst() - // eslint-disable-next-line @typescript-eslint/no-unused-vars - visit: VisitFunction = this.abstractAst.build_visit((node: any) => false) + // eslint-disable-next-line @typescript-eslint/no-unused-vars + visit: VisitFunction = this.abstractAst.build_visit((node: any) => false) - report: ReportFunction = this.abstractAst.build_report(this._report.bind(this)) + report: ReportFunction = this.abstractAst.build_report(this._report.bind(this)) - private _report (contracts: ContractHLAst[], multipleContractsWithSameName: boolean, version: string): ReportObj[] { - const warnings: ReportObj[] = [] - const hasModifiers: boolean = contracts.some((item) => item.modifiers.length > 0) + private _report (contracts: ContractHLAst[], multipleContractsWithSameName: boolean, version: string): ReportObj[] { + const warnings: ReportObj[] = [] + const hasModifiers: boolean = contracts.some((item) => item.modifiers.length > 0) - contracts.forEach((contract) => { - contract.functions.forEach((func) => { - const funcName: string = getFullQuallyfiedFuncDefinitionIdent(contract.node, func.node, func.parameters) - let hasModifiersComments = '' - if (hasModifiers) { - hasModifiersComments = 'Note: Modifiers are currently not considered by this static analysis.' - } - let multipleContractsWithSameNameComments = '' - if (multipleContractsWithSameName) { - multipleContractsWithSameNameComments = 'Note: Import aliases are currently not supported by this static analysis.' - } - const vars: string[] = this.getFunctionVariables(contract, func).map(getDeclaredVariableName) - this.findSimilarVarNames(vars).map((sim) => { - // check if function is implemented - if (func.node.implemented) { - const astWalker = new AstWalker() - const functionBody: any = func.node.body - // Walk through all statements of function - astWalker.walk(functionBody, (node) => { - // check if these is an identifier node which is one of the tracked similar variables - if ((node.nodeType === 'Identifier' || node.nodeType === 'VariableDeclaration') && + contracts.forEach((contract) => { + contract.functions.forEach((func) => { + const funcName: string = getFullQuallyfiedFuncDefinitionIdent(contract.node, func.node, func.parameters) + let hasModifiersComments = '' + if (hasModifiers) { + hasModifiersComments = 'Note: Modifiers are currently not considered by this static analysis.' + } + let multipleContractsWithSameNameComments = '' + if (multipleContractsWithSameName) { + multipleContractsWithSameNameComments = 'Note: Import aliases are currently not supported by this static analysis.' + } + const vars: string[] = this.getFunctionVariables(contract, func).map(getDeclaredVariableName) + this.findSimilarVarNames(vars).map((sim) => { + // check if function is implemented + if (func.node.implemented) { + const astWalker = new AstWalker() + const functionBody: any = func.node.body + // Walk through all statements of function + astWalker.walk(functionBody, (node) => { + // check if these is an identifier node which is one of the tracked similar variables + if ((node.nodeType === 'Identifier' || node.nodeType === 'VariableDeclaration') && (node.name === sim.var1 || node.name === sim.var2)) { - warnings.push({ - warning: `${funcName} : Variables have very similar names "${sim.var1}" and "${sim.var2}". ${hasModifiersComments} ${multipleContractsWithSameNameComments}`, - location: node.src - }) - } - return true - }) - } + warnings.push({ + warning: `${funcName} : Variables have very similar names "${sim.var1}" and "${sim.var2}". ${hasModifiersComments} ${multipleContractsWithSameNameComments}`, + location: node.src }) + } + return true }) + } }) - return warnings - } + }) + }) + return warnings + } - private findSimilarVarNames (vars: string[]): SimilarRecord[] { - const similar: SimilarRecord[] = [] - const comb: Record = {} - vars.map((varName1: string) => vars.map((varName2: string) => { - if (varName1.length > 1 && varName2.length > 1 && + private findSimilarVarNames (vars: string[]): SimilarRecord[] { + const similar: SimilarRecord[] = [] + const comb: Record = {} + vars.map((varName1: string) => vars.map((varName2: string) => { + if (varName1.length > 1 && varName2.length > 1 && varName2 !== varName1 && !this.isCommonPrefixedVersion(varName1, varName2) && !this.isCommonNrSuffixVersion(varName1, varName2) && !(comb[varName1 + ';' + varName2] || comb[varName2 + ';' + varName1])) { - comb[varName1 + ';' + varName2] = true - const distance: number = get(varName1, varName2) - if (distance <= 2) similar.push({ var1: varName1, var2: varName2, distance: distance }) - } - })) - return similar - } + comb[varName1 + ';' + varName2] = true + const distance: number = get(varName1, varName2) + if (distance <= 2) similar.push({ var1: varName1, var2: varName2, distance: distance }) + } + })) + return similar + } - private isCommonPrefixedVersion (varName1: string, varName2: string): boolean { - return (varName1.startsWith('_') && varName1.slice(1) === varName2) || (varName2.startsWith('_') && varName2.slice(1) === varName1) - } + private isCommonPrefixedVersion (varName1: string, varName2: string): boolean { + return (varName1.startsWith('_') && varName1.slice(1) === varName2) || (varName2.startsWith('_') && varName2.slice(1) === varName1) + } - private isCommonNrSuffixVersion (varName1: string, varName2: string): boolean { - const ref: string = '^' + util.escapeRegExp(varName1.slice(0, -1)) + '[0-9]*$' - return varName2.match(ref) != null - } + private isCommonNrSuffixVersion (varName1: string, varName2: string): boolean { + const ref: string = '^' + util.escapeRegExp(varName1.slice(0, -1)) + '[0-9]*$' + return varName2.match(ref) != null + } - private getFunctionVariables (contract: ContractHLAst, func: FunctionHLAst): VariableDeclarationAstNode[] { - return contract.stateVariables.concat(func.localVariables) - } + private getFunctionVariables (contract: ContractHLAst, func: FunctionHLAst): VariableDeclarationAstNode[] { + return contract.stateVariables.concat(func.localVariables) + } } diff --git a/libs/remix-analyzer/src/solidity-analyzer/modules/staticAnalysisCommon.ts b/libs/remix-analyzer/src/solidity-analyzer/modules/staticAnalysisCommon.ts index 6d258d5909..b72b71de35 100644 --- a/libs/remix-analyzer/src/solidity-analyzer/modules/staticAnalysisCommon.ts +++ b/libs/remix-analyzer/src/solidity-analyzer/modules/staticAnalysisCommon.ts @@ -1,10 +1,10 @@ 'use strict' import { - FunctionDefinitionAstNode, ModifierDefinitionAstNode, ParameterListAstNode, ForStatementAstNode, - WhileStatementAstNode, VariableDeclarationAstNode, ContractDefinitionAstNode, InheritanceSpecifierAstNode, - MemberAccessAstNode, BinaryOperationAstNode, FunctionCallAstNode, ExpressionStatementAstNode, UnaryOperationAstNode, - IdentifierAstNode, IndexAccessAstNode, BlockAstNode, AssignmentAstNode, InlineAssemblyAstNode, IfStatementAstNode, CompiledContractObj, ABIParameter, CompiledContract + FunctionDefinitionAstNode, ModifierDefinitionAstNode, ParameterListAstNode, ForStatementAstNode, + WhileStatementAstNode, VariableDeclarationAstNode, ContractDefinitionAstNode, InheritanceSpecifierAstNode, + MemberAccessAstNode, BinaryOperationAstNode, FunctionCallAstNode, ExpressionStatementAstNode, UnaryOperationAstNode, + IdentifierAstNode, IndexAccessAstNode, BlockAstNode, AssignmentAstNode, InlineAssemblyAstNode, IfStatementAstNode, CompiledContractObj, ABIParameter, CompiledContract } from '../../types' import { util } from '@remix-project/remix-lib' @@ -15,156 +15,156 @@ type SpecialObjDetail = { } const nodeTypes: Record = { - SOURCEUNIT: 'SourceUnit', - PRAGMADIRECTIVE: 'PragmaDirective', - IMPORTDIRECTIVE: 'ImportDirective', - CONTRACTDEFINITION: 'ContractDefinition', - INHERITANCESPECIFIER: 'InheritanceSpecifier', - USINGFORDIRECTIVE: 'UsingForDirective', - STRUCTDEFINITION: 'StructDefinition', - ENUMDEFINITION: 'EnumDefinition', - ENUMVALUE: 'EnumValue', - PARAMETERLIST: 'ParameterList', - OVERRIDESPECIFIER: 'OverrideSpecifier', - FUNCTIONDEFINITION: 'FunctionDefinition', - VARIABLEDECLARATION: 'VariableDeclaration', - MODIFIERDEFINITION: 'ModifierDefinition', - MODIFIERINVOCATION: 'ModifierInvocation', - EVENTDEFINITION: 'EventDefinition', - ELEMENTARYTYPENAME: 'ElementaryTypeName', - USERDEFINEDTYPENAME: 'UserDefinedTypeName', - FUNCTIONTYPENAME: 'FunctionTypeName', - MAPPING: 'Mapping', - ARRAYTYPENAME: 'ArrayTypeName', - INLINEASSEMBLY: 'InlineAssembly', - BLOCK: 'Block', - PLACEHOLDERSTATEMENT: 'PlaceholderStatement', - IFSTATEMENT: 'IfStatement', - TRYCATCHCLAUSE: 'TryCatchClause', - TRYSTATEMENT: 'TryStatement', - WHILESTATEMENT: 'WhileStatement', - DOWHILESTATEMENT: 'DoWhileStatement', - FORSTATEMENT: 'ForStatement', - CONTINUE: 'Continue', - BREAK: 'Break', - RETURN: 'Return', - THROW: 'Throw', - EMITSTATEMENT: 'EmitStatement', - VARIABLEDECLARATIONSTATEMENT: 'VariableDeclarationStatement', - EXPRESSIONSTATEMENT: 'ExpressionStatement', - CONDITIONAL: 'Conditional', - ASSIGNMENT: 'Assignment', - TUPLEEXPRESSION: 'TupleExpression', - UNARYOPERATION: 'UnaryOperation', - BINARYOPERATION: 'BinaryOperation', - FUNCTIONCALL: 'FunctionCall', - FUNCTIONCALLOPTIONS: 'FunctionCallOptions', - NEWEXPRESSION: 'NewExpression', - MEMBERACCESS: 'MemberAccess', - INDEXACCESS: 'IndexAccess', - INDEXRANGEACCESS: 'IndexRangeAccess', - ELEMENTARYTYPENAMEEXPRESSION: 'ElementaryTypeNameExpression', - LITERAL: 'Literal', - IDENTIFIER: 'Identifier', - STRUCTUREDDOCUMENTATION: 'StructuredDocumentation' + SOURCEUNIT: 'SourceUnit', + PRAGMADIRECTIVE: 'PragmaDirective', + IMPORTDIRECTIVE: 'ImportDirective', + CONTRACTDEFINITION: 'ContractDefinition', + INHERITANCESPECIFIER: 'InheritanceSpecifier', + USINGFORDIRECTIVE: 'UsingForDirective', + STRUCTDEFINITION: 'StructDefinition', + ENUMDEFINITION: 'EnumDefinition', + ENUMVALUE: 'EnumValue', + PARAMETERLIST: 'ParameterList', + OVERRIDESPECIFIER: 'OverrideSpecifier', + FUNCTIONDEFINITION: 'FunctionDefinition', + VARIABLEDECLARATION: 'VariableDeclaration', + MODIFIERDEFINITION: 'ModifierDefinition', + MODIFIERINVOCATION: 'ModifierInvocation', + EVENTDEFINITION: 'EventDefinition', + ELEMENTARYTYPENAME: 'ElementaryTypeName', + USERDEFINEDTYPENAME: 'UserDefinedTypeName', + FUNCTIONTYPENAME: 'FunctionTypeName', + MAPPING: 'Mapping', + ARRAYTYPENAME: 'ArrayTypeName', + INLINEASSEMBLY: 'InlineAssembly', + BLOCK: 'Block', + PLACEHOLDERSTATEMENT: 'PlaceholderStatement', + IFSTATEMENT: 'IfStatement', + TRYCATCHCLAUSE: 'TryCatchClause', + TRYSTATEMENT: 'TryStatement', + WHILESTATEMENT: 'WhileStatement', + DOWHILESTATEMENT: 'DoWhileStatement', + FORSTATEMENT: 'ForStatement', + CONTINUE: 'Continue', + BREAK: 'Break', + RETURN: 'Return', + THROW: 'Throw', + EMITSTATEMENT: 'EmitStatement', + VARIABLEDECLARATIONSTATEMENT: 'VariableDeclarationStatement', + EXPRESSIONSTATEMENT: 'ExpressionStatement', + CONDITIONAL: 'Conditional', + ASSIGNMENT: 'Assignment', + TUPLEEXPRESSION: 'TupleExpression', + UNARYOPERATION: 'UnaryOperation', + BINARYOPERATION: 'BinaryOperation', + FUNCTIONCALL: 'FunctionCall', + FUNCTIONCALLOPTIONS: 'FunctionCallOptions', + NEWEXPRESSION: 'NewExpression', + MEMBERACCESS: 'MemberAccess', + INDEXACCESS: 'IndexAccess', + INDEXRANGEACCESS: 'IndexRangeAccess', + ELEMENTARYTYPENAMEEXPRESSION: 'ElementaryTypeNameExpression', + LITERAL: 'Literal', + IDENTIFIER: 'Identifier', + STRUCTUREDDOCUMENTATION: 'StructuredDocumentation' } const basicTypes: Record = { - UINT: 'uint256', - BOOL: 'bool', - ADDRESS: 'address', - PAYABLE_ADDRESS: 'address payable', - BYTES32: 'bytes32', - STRING_MEM: 'string memory', - BYTES_MEM: 'bytes memory', - BYTES4: 'bytes4' + UINT: 'uint256', + BOOL: 'bool', + ADDRESS: 'address', + PAYABLE_ADDRESS: 'address payable', + BYTES32: 'bytes32', + STRING_MEM: 'string memory', + BYTES_MEM: 'bytes memory', + BYTES4: 'bytes4' } const basicRegex: Record = { - CONTRACTTYPE: '^contract ', - FUNCTIONTYPE: '^function \\(', - EXTERNALFUNCTIONTYPE: '^function \\(.*\\).* external', - CONSTANTFUNCTIONTYPE: '^function \\(.*\\).* (view|pure)', - REFTYPE: '(storage)|(mapping\\()|(\\[\\])', - FUNCTIONSIGNATURE: '^function \\(([^\\(]*)\\)', - LIBRARYTYPE: '^type\\(library (.*)\\)' + CONTRACTTYPE: '^contract ', + FUNCTIONTYPE: '^function \\(', + EXTERNALFUNCTIONTYPE: '^function \\(.*\\).* external', + CONSTANTFUNCTIONTYPE: '^function \\(.*\\).* (view|pure)', + REFTYPE: '(storage)|(mapping\\()|(\\[\\])', + FUNCTIONSIGNATURE: '^function \\(([^\\(]*)\\)', + LIBRARYTYPE: '^type\\(library (.*)\\)' } const basicFunctionTypes: Record = { - SEND: buildFunctionSignature([basicTypes.UINT], [basicTypes.BOOL], false), - 'CALL-0.4': buildFunctionSignature([], [basicTypes.BOOL], true), - CALL: buildFunctionSignature([basicTypes.BYTES_MEM], [basicTypes.BOOL, basicTypes.BYTES_MEM], true), - 'DELEGATECALL-0.4': buildFunctionSignature([], [basicTypes.BOOL], false), - DELEGATECALL: buildFunctionSignature([basicTypes.BYTES_MEM], [basicTypes.BOOL, basicTypes.BYTES_MEM], false), - TRANSFER: buildFunctionSignature([basicTypes.UINT], [], false) + SEND: buildFunctionSignature([basicTypes.UINT], [basicTypes.BOOL], false), + 'CALL-0.4': buildFunctionSignature([], [basicTypes.BOOL], true), + CALL: buildFunctionSignature([basicTypes.BYTES_MEM], [basicTypes.BOOL, basicTypes.BYTES_MEM], true), + 'DELEGATECALL-0.4': buildFunctionSignature([], [basicTypes.BOOL], false), + DELEGATECALL: buildFunctionSignature([basicTypes.BYTES_MEM], [basicTypes.BOOL, basicTypes.BYTES_MEM], false), + TRANSFER: buildFunctionSignature([basicTypes.UINT], [], false) } const builtinFunctions: Record = { - 'keccak256()': true, - 'sha3()': true, - 'sha256()': true, - 'ripemd160()': true, - 'ecrecover(bytes32,uint8,bytes32,bytes32)': true, - 'addmod(uint256,uint256,uint256)': true, - 'mulmod(uint256,uint256,uint256)': true, - 'selfdestruct(address)': true, - 'selfdestruct(address payable)': true, - 'revert()': true, - 'revert(string memory)': true, - 'assert(bool)': true, - 'require(bool)': true, - 'require(bool,string memory)': true, - 'gasleft()': true, - 'blockhash(uint256)': true, - 'address(address)': true + 'keccak256()': true, + 'sha3()': true, + 'sha256()': true, + 'ripemd160()': true, + 'ecrecover(bytes32,uint8,bytes32,bytes32)': true, + 'addmod(uint256,uint256,uint256)': true, + 'mulmod(uint256,uint256,uint256)': true, + 'selfdestruct(address)': true, + 'selfdestruct(address payable)': true, + 'revert()': true, + 'revert(string memory)': true, + 'assert(bool)': true, + 'require(bool)': true, + 'require(bool,string memory)': true, + 'gasleft()': true, + 'blockhash(uint256)': true, + 'address(address)': true } const lowLevelCallTypes: Record> = { - 'CALL-0.4': { ident: 'call', type: basicFunctionTypes['CALL-0.4'] }, - CALL: { ident: 'call', type: basicFunctionTypes.CALL }, - CALLCODE: { ident: 'callcode', type: basicFunctionTypes['CALL-0.4'] }, - 'DELEGATECALL-0.4': { ident: 'delegatecall', type: basicFunctionTypes['DELEGATECALL-0.4'] }, - DELEGATECALL: { ident: 'delegatecall', type: basicFunctionTypes.DELEGATECALL }, - SEND: { ident: 'send', type: basicFunctionTypes.SEND }, - TRANSFER: { ident: 'transfer', type: basicFunctionTypes.TRANSFER } + 'CALL-0.4': { ident: 'call', type: basicFunctionTypes['CALL-0.4'] }, + CALL: { ident: 'call', type: basicFunctionTypes.CALL }, + CALLCODE: { ident: 'callcode', type: basicFunctionTypes['CALL-0.4'] }, + 'DELEGATECALL-0.4': { ident: 'delegatecall', type: basicFunctionTypes['DELEGATECALL-0.4'] }, + DELEGATECALL: { ident: 'delegatecall', type: basicFunctionTypes.DELEGATECALL }, + SEND: { ident: 'send', type: basicFunctionTypes.SEND }, + TRANSFER: { ident: 'transfer', type: basicFunctionTypes.TRANSFER } } const specialVariables: Record = { - BLOCKTIMESTAMP: { obj: 'block', member: 'timestamp', type: basicTypes.UINT }, - BLOCKHASH: { - obj: 'block', - member: 'blockhash', - type: buildFunctionSignature([basicTypes.UINT], [basicTypes.BYTES32], false, 'view') - } + BLOCKTIMESTAMP: { obj: 'block', member: 'timestamp', type: basicTypes.UINT }, + BLOCKHASH: { + obj: 'block', + member: 'blockhash', + type: buildFunctionSignature([basicTypes.UINT], [basicTypes.BYTES32], false, 'view') + } } const abiNamespace: Record = { - ENCODE: { - obj: 'abi', - member: 'encode', - type: buildFunctionSignature([], [basicTypes.BYTES_MEM], false, 'pure') - }, - ENCODEPACKED: { - obj: 'abi', - member: 'encodePacked', - type: buildFunctionSignature([], [basicTypes.BYTES_MEM], false, 'pure') - }, - ENCODE_SELECT: { - obj: 'abi', - member: 'encodeWithSelector', - type: buildFunctionSignature([basicTypes.BYTES4], [basicTypes.BYTES_MEM], false, 'pure') - }, - ENCODE_SIG: { - obj: 'abi', - member: 'encodeWithSignature', - type: buildFunctionSignature([basicTypes.STRING_MEM], [basicTypes.BYTES_MEM], false, 'pure') - } + ENCODE: { + obj: 'abi', + member: 'encode', + type: buildFunctionSignature([], [basicTypes.BYTES_MEM], false, 'pure') + }, + ENCODEPACKED: { + obj: 'abi', + member: 'encodePacked', + type: buildFunctionSignature([], [basicTypes.BYTES_MEM], false, 'pure') + }, + ENCODE_SELECT: { + obj: 'abi', + member: 'encodeWithSelector', + type: buildFunctionSignature([basicTypes.BYTES4], [basicTypes.BYTES_MEM], false, 'pure') + }, + ENCODE_SIG: { + obj: 'abi', + member: 'encodeWithSignature', + type: buildFunctionSignature([basicTypes.STRING_MEM], [basicTypes.BYTES_MEM], false, 'pure') + } } // #################### Trivial Getters // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types function getType (node: any): string { - return node.typeDescriptions.typeString + return node.typeDescriptions.typeString } // #################### Complex Getters @@ -175,7 +175,7 @@ function getType (node: any): string { * @return {string} type of function call */ function getFunctionCallType (func: FunctionCallAstNode): string { - return getType(func.expression) + return getType(func.expression) } /** @@ -185,11 +185,11 @@ function getFunctionCallType (func: FunctionCallAstNode): string { * @return {string} variable name written to */ function getEffectedVariableName (effectNode: AssignmentAstNode | UnaryOperationAstNode): string { - if (!isEffect(effectNode)) throw new Error('staticAnalysisCommon.js: not an effect Node') - if (effectNode.nodeType === 'Assignment' || effectNode.nodeType === 'UnaryOperation') { - const IdentNode: IdentifierAstNode = findFirstSubNodeLTR(effectNode, exactMatch(nodeTypes.IDENTIFIER)) - return IdentNode.name - } else throw new Error('staticAnalysisCommon.js: wrong node type') + if (!isEffect(effectNode)) throw new Error('staticAnalysisCommon.js: not an effect Node') + if (effectNode.nodeType === 'Assignment' || effectNode.nodeType === 'UnaryOperation') { + const IdentNode: IdentifierAstNode = findFirstSubNodeLTR(effectNode, exactMatch(nodeTypes.IDENTIFIER)) + return IdentNode.name + } else throw new Error('staticAnalysisCommon.js: wrong node type') } /** @@ -199,8 +199,8 @@ function getEffectedVariableName (effectNode: AssignmentAstNode | UnaryOperation * @return {string} name of the function called */ function getLocalCallName (localCallNode: FunctionCallAstNode): string { - if (!isLocalCall(localCallNode) && !isAbiNamespaceCall(localCallNode)) throw new Error('staticAnalysisCommon.js: not a local call Node') - return localCallNode.expression.name + if (!isLocalCall(localCallNode) && !isAbiNamespaceCall(localCallNode)) throw new Error('staticAnalysisCommon.js: not a local call Node') + return localCallNode.expression.name } /** @@ -210,8 +210,8 @@ function getLocalCallName (localCallNode: FunctionCallAstNode): string { * @return {string} name of the function called */ function getThisLocalCallName (thisLocalCallNode: FunctionCallAstNode): string { - if (!isThisLocalCall(thisLocalCallNode.expression)) throw new Error('staticAnalysisCommon.js: not a this local call Node') - return thisLocalCallNode.expression.memberName + if (!isThisLocalCall(thisLocalCallNode.expression)) throw new Error('staticAnalysisCommon.js: not a this local call Node') + return thisLocalCallNode.expression.memberName } /** @@ -221,8 +221,8 @@ function getThisLocalCallName (thisLocalCallNode: FunctionCallAstNode): string { * @return {string} name of the function called */ function getSuperLocalCallName (superLocalCallNode: FunctionCallAstNode): string { - if (!isSuperLocalCall(superLocalCallNode.expression)) throw new Error('staticAnalysisCommon.js: not a super local call Node') - return superLocalCallNode.expression.memberName + if (!isSuperLocalCall(superLocalCallNode.expression)) throw new Error('staticAnalysisCommon.js: not a super local call Node') + return superLocalCallNode.expression.memberName } /** @@ -234,8 +234,8 @@ function getSuperLocalCallName (superLocalCallNode: FunctionCallAstNode): string * @return {string} name of the contract the function is defined in */ function getExternalDirectCallContractName (extDirectCall: FunctionCallAstNode): string { - if (!isExternalDirectCall(extDirectCall)) throw new Error('staticAnalysisCommon.js: not an external direct call Node') - return extDirectCall.expression.expression.typeDescriptions.typeString.replace(new RegExp(basicRegex.CONTRACTTYPE), '') + if (!isExternalDirectCall(extDirectCall)) throw new Error('staticAnalysisCommon.js: not an external direct call Node') + return extDirectCall.expression.expression.typeDescriptions.typeString.replace(new RegExp(basicRegex.CONTRACTTYPE), '') } /** @@ -249,8 +249,8 @@ function getExternalDirectCallContractName (extDirectCall: FunctionCallAstNode): * @return {string} name of the contract the function is defined in */ function getThisLocalCallContractName (thisLocalCall: FunctionCallAstNode): string { - if (!isThisLocalCall(thisLocalCall.expression)) throw new Error('staticAnalysisCommon.js: not a this local call Node') - return thisLocalCall.expression.expression.typeDescriptions.typeString.replace(new RegExp(basicRegex.CONTRACTTYPE), '') + if (!isThisLocalCall(thisLocalCall.expression)) throw new Error('staticAnalysisCommon.js: not a this local call Node') + return thisLocalCall.expression.expression.typeDescriptions.typeString.replace(new RegExp(basicRegex.CONTRACTTYPE), '') } /** @@ -262,8 +262,8 @@ function getThisLocalCallContractName (thisLocalCall: FunctionCallAstNode): stri * @return {string} name of the function called */ function getExternalDirectCallMemberName (extDirectCall: FunctionCallAstNode): string { - if (!isExternalDirectCall(extDirectCall)) throw new Error('staticAnalysisCommon.js: not an external direct call Node') - return extDirectCall.expression.memberName + if (!isExternalDirectCall(extDirectCall)) throw new Error('staticAnalysisCommon.js: not an external direct call Node') + return extDirectCall.expression.memberName } /** @@ -274,8 +274,8 @@ function getExternalDirectCallMemberName (extDirectCall: FunctionCallAstNode): s * @return {string} name of a contract defined */ function getContractName (contract: ContractDefinitionAstNode): string { - if (!nodeType(contract, exactMatch(nodeTypes.CONTRACTDEFINITION))) throw new Error('staticAnalysisCommon.js: not a ContractDefinition Node') - return contract.name + if (!nodeType(contract, exactMatch(nodeTypes.CONTRACTDEFINITION))) throw new Error('staticAnalysisCommon.js: not a ContractDefinition Node') + return contract.name } /** @@ -286,8 +286,8 @@ function getContractName (contract: ContractDefinitionAstNode): string { * @return {string} name of a function defined */ function getFunctionDefinitionName (funcDef: FunctionDefinitionAstNode): string { - if (!nodeType(funcDef, exactMatch(nodeTypes.FUNCTIONDEFINITION))) throw new Error('staticAnalysisCommon.js: not a FunctionDefinition Node') - return funcDef.name + if (!nodeType(funcDef, exactMatch(nodeTypes.FUNCTIONDEFINITION))) throw new Error('staticAnalysisCommon.js: not a FunctionDefinition Node') + return funcDef.name } /** @@ -298,8 +298,8 @@ function getFunctionDefinitionName (funcDef: FunctionDefinitionAstNode): string * @return {string} name of contract inherited from */ function getInheritsFromName (inheritsNode: InheritanceSpecifierAstNode): string { - if (!nodeType(inheritsNode, exactMatch(nodeTypes.INHERITANCESPECIFIER))) throw new Error('staticAnalysisCommon.js: not an InheritanceSpecifier Node') - return inheritsNode.baseName.name + if (!nodeType(inheritsNode, exactMatch(nodeTypes.INHERITANCESPECIFIER))) throw new Error('staticAnalysisCommon.js: not an InheritanceSpecifier Node') + return inheritsNode.baseName.name } /** @@ -310,8 +310,8 @@ function getInheritsFromName (inheritsNode: InheritanceSpecifierAstNode): string * @return {string} variable name */ function getDeclaredVariableName (varDeclNode: VariableDeclarationAstNode): string { - if (!nodeType(varDeclNode, exactMatch(nodeTypes.VARIABLEDECLARATION))) throw new Error('staticAnalysisCommon.js: not a VariableDeclaration Node') - return varDeclNode.name + if (!nodeType(varDeclNode, exactMatch(nodeTypes.VARIABLEDECLARATION))) throw new Error('staticAnalysisCommon.js: not a VariableDeclaration Node') + return varDeclNode.name } /** @@ -322,7 +322,7 @@ function getDeclaredVariableName (varDeclNode: VariableDeclarationAstNode): stri * @return {string} variable type */ function getDeclaredVariableType (varDeclNode: VariableDeclarationAstNode): string { - return varDeclNode.typeName.name + return varDeclNode.typeName.name } /** @@ -336,7 +336,7 @@ function getDeclaredVariableType (varDeclNode: VariableDeclarationAstNode): stri * @return {list variable declaration} state variable node list */ function getStateVariableDeclarationsFromContractNode (contractNode: ContractDefinitionAstNode): VariableDeclarationAstNode[] { - return contractNode.nodes.filter(el => el.nodeType === 'VariableDeclaration') + return contractNode.nodes.filter(el => el.nodeType === 'VariableDeclaration') } /** @@ -347,8 +347,8 @@ function getStateVariableDeclarationsFromContractNode (contractNode: ContractDef * @return {parameterlist node} parameterlist node */ function getFunctionOrModifierDefinitionParameterPart (funcNode: FunctionDefinitionAstNode | ModifierDefinitionAstNode): ParameterListAstNode { - if (!nodeTypeIn(funcNode, [exactMatch(nodeTypes.FUNCTIONDEFINITION), exactMatch(nodeTypes.MODIFIERDEFINITION)])) throw new Error('staticAnalysisCommon.js: not a FunctionDefinition or ModifierDefinition Node') - return funcNode.parameters + if (!nodeTypeIn(funcNode, [exactMatch(nodeTypes.FUNCTIONDEFINITION), exactMatch(nodeTypes.MODIFIERDEFINITION)])) throw new Error('staticAnalysisCommon.js: not a FunctionDefinition or ModifierDefinition Node') + return funcNode.parameters } /** @@ -359,7 +359,7 @@ function getFunctionOrModifierDefinitionParameterPart (funcNode: FunctionDefinit * @return {parameterlist node} parameterlist node */ function getFunctionDefinitionReturnParameterPart (funcNode: FunctionDefinitionAstNode): ParameterListAstNode { - return funcNode.returnParameters + return funcNode.returnParameters } /** @@ -370,21 +370,21 @@ function getFunctionDefinitionReturnParameterPart (funcNode: FunctionDefinitionA * @return {string} parameter signature */ function getFunctionCallTypeParameterType (func: FunctionCallAstNode): string | undefined { - const type: string = getFunctionCallType(func) - if (type.startsWith('function (')) { - let paramTypes = '' - let openPar = 1 - for (let x = 10; x < type.length; x++) { - const c: string = type.charAt(x) - if (c === '(') openPar++ - else if (c === ')') openPar-- - - if (openPar === 0) return paramTypes - paramTypes += c - } - } else { - throw new Error('staticAnalysisCommon.js: cannot extract parameter types from function call') + const type: string = getFunctionCallType(func) + if (type.startsWith('function (')) { + let paramTypes = '' + let openPar = 1 + for (let x = 10; x < type.length; x++) { + const c: string = type.charAt(x) + if (c === '(') openPar++ + else if (c === ')') openPar-- + + if (openPar === 0) return paramTypes + paramTypes += c } + } else { + throw new Error('staticAnalysisCommon.js: cannot extract parameter types from function call') + } } /** @@ -398,9 +398,9 @@ function getFunctionCallTypeParameterType (func: FunctionCallAstNode): string | * @return {string} name of the lib defined */ function getLibraryCallContractName (node: FunctionCallAstNode): string | undefined { - if (!isLibraryCall(node.expression)) throw new Error('staticAnalysisCommon.js: not a library call Node') - const types: RegExpExecArray | null = new RegExp(basicRegex.LIBRARYTYPE).exec(node.expression.expression.typeDescriptions.typeString) - if (types) { return types[1] } + if (!isLibraryCall(node.expression)) throw new Error('staticAnalysisCommon.js: not a library call Node') + const types: RegExpExecArray | null = new RegExp(basicRegex.LIBRARYTYPE).exec(node.expression.expression.typeDescriptions.typeString) + if (types) { return types[1] } } /** @@ -414,8 +414,8 @@ function getLibraryCallContractName (node: FunctionCallAstNode): string | undefi * @return {string} name of function called on the library */ function getLibraryCallMemberName (funcCall: FunctionCallAstNode): string { - if (!isLibraryCall(funcCall.expression)) throw new Error('staticAnalysisCommon.js: not a library call Node') - return funcCall.expression.memberName + if (!isLibraryCall(funcCall.expression)) throw new Error('staticAnalysisCommon.js: not a library call Node') + return funcCall.expression.memberName } /** @@ -431,38 +431,38 @@ function getLibraryCallMemberName (funcCall: FunctionCallAstNode): string { * @return {string} full qualified identifier for the function call */ function getFullQualifiedFunctionCallIdent (contract: ContractDefinitionAstNode, func: FunctionCallAstNode): string { - if (isLocalCall(func)) return getContractName(contract) + '.' + getLocalCallName(func) + '(' + getFunctionCallTypeParameterType(func) + ')' - else if (isThisLocalCall(func.expression)) return getThisLocalCallContractName(func) + '.' + getThisLocalCallName(func) + '(' + getFunctionCallTypeParameterType(func) + ')' - else if (isSuperLocalCall(func.expression)) return getContractName(contract) + '.' + getSuperLocalCallName(func) + '(' + getFunctionCallTypeParameterType(func) + ')' - else if (isExternalDirectCall(func)) return getExternalDirectCallContractName(func) + '.' + getExternalDirectCallMemberName(func) + '(' + getFunctionCallTypeParameterType(func) + ')' - else if (isLibraryCall(func.expression)) return getLibraryCallContractName(func) + '.' + getLibraryCallMemberName(func) + '(' + getFunctionCallTypeParameterType(func) + ')' - else throw new Error('staticAnalysisCommon.js: Can not get function name from non function call node') + if (isLocalCall(func)) return getContractName(contract) + '.' + getLocalCallName(func) + '(' + getFunctionCallTypeParameterType(func) + ')' + else if (isThisLocalCall(func.expression)) return getThisLocalCallContractName(func) + '.' + getThisLocalCallName(func) + '(' + getFunctionCallTypeParameterType(func) + ')' + else if (isSuperLocalCall(func.expression)) return getContractName(contract) + '.' + getSuperLocalCallName(func) + '(' + getFunctionCallTypeParameterType(func) + ')' + else if (isExternalDirectCall(func)) return getExternalDirectCallContractName(func) + '.' + getExternalDirectCallMemberName(func) + '(' + getFunctionCallTypeParameterType(func) + ')' + else if (isLibraryCall(func.expression)) return getLibraryCallContractName(func) + '.' + getLibraryCallMemberName(func) + '(' + getFunctionCallTypeParameterType(func) + ')' + else throw new Error('staticAnalysisCommon.js: Can not get function name from non function call node') } function getFullQuallyfiedFuncDefinitionIdent (contract: ContractDefinitionAstNode, func: FunctionDefinitionAstNode, paramTypes: any[]): string { - return getContractName(contract) + '.' + getFunctionDefinitionName(func) + '(' + util.concatWithSeperator(paramTypes, ',') + ')' + return getContractName(contract) + '.' + getFunctionDefinitionName(func) + '(' + util.concatWithSeperator(paramTypes, ',') + ')' } function getUnAssignedTopLevelBinOps (subScope: BlockAstNode | IfStatementAstNode | WhileStatementAstNode | ForStatementAstNode): ExpressionStatementAstNode[] { - let result: ExpressionStatementAstNode[] = [] - if (subScope && subScope.nodeType === 'Block') result = subScope.statements.filter(isBinaryOpInExpression) - // for 'without braces' loops - else if (subScope && subScope.nodeType && subScope.nodeType !== 'Block' && isSubScopeStatement(subScope)) { - if (subScope.nodeType === 'IfStatement') { - if ((subScope.trueBody && subScope.trueBody.nodeType === 'ExpressionStatement' && isBinaryOpInExpression(subScope.trueBody))) { result.push(subScope.trueBody) } - if (subScope.falseBody && subScope.falseBody.nodeType === 'ExpressionStatement' && isBinaryOpInExpression(subScope.falseBody)) { result.push(subScope.falseBody) } - } else { - if (subScope.body && subScope.body.nodeType === 'ExpressionStatement' && isBinaryOpInExpression(subScope.body)) { result.push(subScope.body) } - } + let result: ExpressionStatementAstNode[] = [] + if (subScope && subScope.nodeType === 'Block') result = subScope.statements.filter(isBinaryOpInExpression) + // for 'without braces' loops + else if (subScope && subScope.nodeType && subScope.nodeType !== 'Block' && isSubScopeStatement(subScope)) { + if (subScope.nodeType === 'IfStatement') { + if ((subScope.trueBody && subScope.trueBody.nodeType === 'ExpressionStatement' && isBinaryOpInExpression(subScope.trueBody))) { result.push(subScope.trueBody) } + if (subScope.falseBody && subScope.falseBody.nodeType === 'ExpressionStatement' && isBinaryOpInExpression(subScope.falseBody)) { result.push(subScope.falseBody) } + } else { + if (subScope.body && subScope.body.nodeType === 'ExpressionStatement' && isBinaryOpInExpression(subScope.body)) { result.push(subScope.body) } } - return result + } + return result } // #################### Trivial Node Identification // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types function isStatement (node: any): boolean { - return nodeType(node, 'Statement$') || node.nodeType === 'Block' || node.nodeType === 'Return' + return nodeType(node, 'Statement$') || node.nodeType === 'Block' || node.nodeType === 'Return' } // #################### Complex Node Identification @@ -473,7 +473,7 @@ function isStatement (node: any): boolean { * @return {bool} */ function hasFunctionBody (funcNode: FunctionDefinitionAstNode): boolean { - return funcNode.body !== null + return funcNode.body !== null } /** @@ -482,7 +482,7 @@ function hasFunctionBody (funcNode: FunctionDefinitionAstNode): boolean { * @return {bool} */ function isDeleteOfDynamicArray (node: UnaryOperationAstNode): boolean { - return isDeleteUnaryOperation(node) && isDynamicArrayAccess(node.subExpression) + return isDeleteUnaryOperation(node) && isDynamicArrayAccess(node.subExpression) } /** @@ -491,7 +491,7 @@ function isDeleteOfDynamicArray (node: UnaryOperationAstNode): boolean { * @return {bool} */ function isDynamicArrayAccess (node: IdentifierAstNode): boolean { - return getType(node).endsWith('[] storage ref') || typeDescription(node, 'bytes storage ref') || typeDescription(node, 'string storage ref') + return getType(node).endsWith('[] storage ref') || typeDescription(node, 'bytes storage ref') || typeDescription(node, 'string storage ref') } /** @@ -500,7 +500,7 @@ function isDynamicArrayAccess (node: IdentifierAstNode): boolean { * @return {bool} */ function isDynamicArrayLengthAccess (node: MemberAccessAstNode): boolean { - return (node.memberName === 'length') && // accessing 'length' member + return (node.memberName === 'length') && // accessing 'length' member node.expression.typeDescriptions.typeString.indexOf('[]') !== -1 // member is accessed from dynamic array, notice [] without any number } @@ -510,7 +510,7 @@ function isDynamicArrayLengthAccess (node: MemberAccessAstNode): boolean { * @return {bool} */ function isDeleteFromDynamicArray (node: UnaryOperationAstNode): boolean { - return isDeleteUnaryOperation(node) && node.subExpression.nodeType === 'IndexAccess' + return isDeleteUnaryOperation(node) && node.subExpression.nodeType === 'IndexAccess' } /** @@ -519,7 +519,7 @@ function isDeleteFromDynamicArray (node: UnaryOperationAstNode): boolean { * @return {bool} */ function isMappingIndexAccess (node: IndexAccessAstNode): boolean { - return node.typeDescriptions.typeString.startsWith('mapping') + return node.typeDescriptions.typeString.startsWith('mapping') } /** @@ -528,7 +528,7 @@ function isMappingIndexAccess (node: IndexAccessAstNode): boolean { * @return {bool} */ function isLocalCallGraphRelevantNode (node: FunctionCallAstNode): boolean { - return ((isLocalCall(node) || isSuperLocalCall(node.expression) || isLibraryCall(node.expression)) && !isBuiltinFunctionCall(node)) + return ((isLocalCall(node) || isSuperLocalCall(node.expression) || isLibraryCall(node.expression)) && !isBuiltinFunctionCall(node)) } /** @@ -537,7 +537,7 @@ function isLocalCallGraphRelevantNode (node: FunctionCallAstNode): boolean { * @return {bool} */ function isBuiltinFunctionCall (node: FunctionCallAstNode): boolean { - return (node.nodeType === 'FunctionCall' && isLocalCall(node) && builtinFunctions[getLocalCallName(node) + '(' + getFunctionCallTypeParameterType(node) + ')'] === true) || isAbiNamespaceCall(node) + return (node.nodeType === 'FunctionCall' && isLocalCall(node) && builtinFunctions[getLocalCallName(node) + '(' + getFunctionCallTypeParameterType(node) + ')'] === true) || isAbiNamespaceCall(node) } /** @@ -546,7 +546,7 @@ function isBuiltinFunctionCall (node: FunctionCallAstNode): boolean { * @return {bool} */ function isAbiNamespaceCall (node: FunctionCallAstNode): boolean { - return Object.keys(abiNamespace).some((key) => Object.prototype.hasOwnProperty.call(abiNamespace, key) && node.expression && isSpecialVariableAccess(node.expression, abiNamespace[key])) + return Object.keys(abiNamespace).some((key) => Object.prototype.hasOwnProperty.call(abiNamespace, key) && node.expression && isSpecialVariableAccess(node.expression, abiNamespace[key])) } /** @@ -555,7 +555,7 @@ function isAbiNamespaceCall (node: FunctionCallAstNode): boolean { * @return {bool} */ function isSelfdestructCall (node: FunctionCallAstNode): boolean { - return isBuiltinFunctionCall(node) && getLocalCallName(node) === 'selfdestruct' + return isBuiltinFunctionCall(node) && getLocalCallName(node) === 'selfdestruct' } /** @@ -564,7 +564,7 @@ function isSelfdestructCall (node: FunctionCallAstNode): boolean { * @return {bool} */ function isAssertCall (node: FunctionCallAstNode): boolean { - return isBuiltinFunctionCall(node) && getLocalCallName(node) === 'assert' + return isBuiltinFunctionCall(node) && getLocalCallName(node) === 'assert' } /** @@ -573,7 +573,7 @@ function isAssertCall (node: FunctionCallAstNode): boolean { * @return {bool} */ function isRequireCall (node: FunctionCallAstNode): boolean { - return isBuiltinFunctionCall(node) && getLocalCallName(node) === 'require' + return isBuiltinFunctionCall(node) && getLocalCallName(node) === 'require' } /** @@ -582,7 +582,7 @@ function isRequireCall (node: FunctionCallAstNode): boolean { * @return {bool} */ function isStorageVariableDeclaration (node: VariableDeclarationAstNode): boolean { - return node.storageLocation === 'storage' && new RegExp(basicRegex.REFTYPE).test(node.typeDescriptions.typeIdentifier) + return node.storageLocation === 'storage' && new RegExp(basicRegex.REFTYPE).test(node.typeDescriptions.typeIdentifier) } /** @@ -591,7 +591,7 @@ function isStorageVariableDeclaration (node: VariableDeclarationAstNode): boolea * @return {bool} */ function isInteraction (node: FunctionCallAstNode): boolean { - return isLLCall(node.expression) || isLLSend(node.expression) || isExternalDirectCall(node) || isTransfer(node.expression) || + return isLLCall(node.expression) || isLLSend(node.expression) || isExternalDirectCall(node) || isTransfer(node.expression) || isLLCall04(node.expression) || isLLSend04(node.expression) || // to cover case of address.call.value.gas , See: inheritance.sol (node.expression && node.expression.expression && isLLCall(node.expression.expression)) || @@ -604,7 +604,7 @@ function isInteraction (node: FunctionCallAstNode): boolean { * @return {bool} */ function isEffect (node: AssignmentAstNode | UnaryOperationAstNode | InlineAssemblyAstNode): boolean { - return node.nodeType === 'Assignment' || + return node.nodeType === 'Assignment' || (node.nodeType === 'UnaryOperation' && (isPlusPlusUnaryOperation(node) || isMinusMinusUnaryOperation(node))) || node.nodeType === 'InlineAssembly' } @@ -616,7 +616,7 @@ function isEffect (node: AssignmentAstNode | UnaryOperationAstNode | InlineAssem * @return {bool} */ function isWriteOnStateVariable (effectNode: AssignmentAstNode | InlineAssemblyAstNode | UnaryOperationAstNode, stateVariables: VariableDeclarationAstNode[]): boolean { - return effectNode.nodeType === 'InlineAssembly' || (isEffect(effectNode) && isStateVariable(getEffectedVariableName(effectNode), stateVariables)) + return effectNode.nodeType === 'InlineAssembly' || (isEffect(effectNode) && isStateVariable(getEffectedVariableName(effectNode), stateVariables)) } /** @@ -626,7 +626,7 @@ function isWriteOnStateVariable (effectNode: AssignmentAstNode | InlineAssemblyA * @return {bool} */ function isStateVariable (name: string, stateVariables: VariableDeclarationAstNode[]): boolean { - return stateVariables.some((item: VariableDeclarationAstNode) => item.stateVariable && name === getDeclaredVariableName(item)) + return stateVariables.some((item: VariableDeclarationAstNode) => item.stateVariable && name === getDeclaredVariableName(item)) } /** @@ -635,7 +635,7 @@ function isStateVariable (name: string, stateVariables: VariableDeclarationAstNo * @return {bool} */ function isConstantFunction (node: FunctionDefinitionAstNode): boolean { - return node.stateMutability === 'view' || node.stateMutability === 'pure' + return node.stateMutability === 'view' || node.stateMutability === 'pure' } /** @@ -644,7 +644,7 @@ function isConstantFunction (node: FunctionDefinitionAstNode): boolean { * @return {bool} */ function isVariableTurnedIntoGetter (varDeclNode: VariableDeclarationAstNode): boolean { - return varDeclNode.stateVariable && varDeclNode.visibility === 'public' + return varDeclNode.stateVariable && varDeclNode.visibility === 'public' } /** @@ -653,7 +653,7 @@ function isVariableTurnedIntoGetter (varDeclNode: VariableDeclarationAstNode): b * @return {bool} */ function isPayableFunction (node: FunctionDefinitionAstNode): boolean { - return node.stateMutability === 'payable' + return node.stateMutability === 'payable' } /** @@ -662,7 +662,7 @@ function isPayableFunction (node: FunctionDefinitionAstNode): boolean { * @return {bool} */ function isConstructor (node: FunctionDefinitionAstNode): boolean { - return node.kind === 'constructor' + return node.kind === 'constructor' } /** @@ -671,7 +671,7 @@ function isConstructor (node: FunctionDefinitionAstNode): boolean { * @return {bool} */ function isIntDivision (node: BinaryOperationAstNode): boolean { - return operator(node, exactMatch(util.escapeRegExp('/'))) && typeDescription(node.rightExpression, util.escapeRegExp('int')) + return operator(node, exactMatch(util.escapeRegExp('/'))) && typeDescription(node.rightExpression, util.escapeRegExp('int')) } /** @@ -680,21 +680,21 @@ function isIntDivision (node: BinaryOperationAstNode): boolean { * @return {bool} */ function isSubScopeWithTopLevelUnAssignedBinOp (node: BlockAstNode | IfStatementAstNode | WhileStatementAstNode | ForStatementAstNode): boolean | undefined { - if (node.nodeType === 'Block') return node.statements.some(isBinaryOpInExpression) - // for 'without braces' loops - else if (node && node.nodeType && isSubScopeStatement(node)) { - if (node.nodeType === 'IfStatement') { - return (node.trueBody && node.trueBody.nodeType === 'ExpressionStatement' && isBinaryOpInExpression(node.trueBody)) || + if (node.nodeType === 'Block') return node.statements.some(isBinaryOpInExpression) + // for 'without braces' loops + else if (node && node.nodeType && isSubScopeStatement(node)) { + if (node.nodeType === 'IfStatement') { + return (node.trueBody && node.trueBody.nodeType === 'ExpressionStatement' && isBinaryOpInExpression(node.trueBody)) || (node.falseBody && node.falseBody.nodeType === 'ExpressionStatement' && isBinaryOpInExpression(node.falseBody)) - } else { return node.body && node.body.nodeType === 'ExpressionStatement' && isBinaryOpInExpression(node.body) } - } + } else { return node.body && node.body.nodeType === 'ExpressionStatement' && isBinaryOpInExpression(node.body) } + } } function isSubScopeStatement (node: IfStatementAstNode | WhileStatementAstNode | ForStatementAstNode): boolean { - if (node.nodeType === 'IfStatement') { - return (node.trueBody && node.trueBody.nodeType && !nodeType(node.trueBody, exactMatch(nodeTypes.BLOCK))) || + if (node.nodeType === 'IfStatement') { + return (node.trueBody && node.trueBody.nodeType && !nodeType(node.trueBody, exactMatch(nodeTypes.BLOCK))) || (node.falseBody && node.falseBody.nodeType && !nodeType(node.falseBody, exactMatch(nodeTypes.BLOCK))) - } else { return node.body && node.body.nodeType && !nodeType(node.body, exactMatch(nodeTypes.BLOCK)) } + } else { return node.body && node.body.nodeType && !nodeType(node.body, exactMatch(nodeTypes.BLOCK)) } } /** @@ -703,7 +703,7 @@ function isSubScopeStatement (node: IfStatementAstNode | WhileStatementAstNode | * @return {bool} */ function isBinaryOpInExpression (node: ExpressionStatementAstNode): boolean { - return node.nodeType === 'ExpressionStatement' && node.expression.nodeType === 'BinaryOperation' + return node.nodeType === 'ExpressionStatement' && node.expression.nodeType === 'BinaryOperation' } /** @@ -712,7 +712,7 @@ function isBinaryOpInExpression (node: ExpressionStatementAstNode): boolean { * @return {bool} */ function isPlusPlusUnaryOperation (node: UnaryOperationAstNode): boolean { - return node.operator === '++' + return node.operator === '++' } /** @@ -721,7 +721,7 @@ function isPlusPlusUnaryOperation (node: UnaryOperationAstNode): boolean { * @return {bool} */ function isDeleteUnaryOperation (node: UnaryOperationAstNode): boolean { - return node.operator === 'delete' + return node.operator === 'delete' } /** @@ -730,7 +730,7 @@ function isDeleteUnaryOperation (node: UnaryOperationAstNode): boolean { * @return {bool} */ function isMinusMinusUnaryOperation (node: UnaryOperationAstNode): boolean { - return node.operator === '--' + return node.operator === '--' } /** @@ -739,7 +739,7 @@ function isMinusMinusUnaryOperation (node: UnaryOperationAstNode): boolean { * @return {bool} */ function isFullyImplementedContract (node: ContractDefinitionAstNode): boolean { - return node.fullyImplemented === true + return node.fullyImplemented === true } /** @@ -748,7 +748,7 @@ function isFullyImplementedContract (node: ContractDefinitionAstNode): boolean { * @return {bool} */ function isLibrary (node: ContractDefinitionAstNode): boolean { - return node.contractKind === 'library' + return node.contractKind === 'library' } /** @@ -757,7 +757,7 @@ function isLibrary (node: ContractDefinitionAstNode): boolean { * @return {bool} */ function isCallToNonConstLocalFunction (node: FunctionCallAstNode): boolean { - return isLocalCall(node) && !expressionTypeDescription(node, basicRegex.CONSTANTFUNCTIONTYPE) + return isLocalCall(node) && !expressionTypeDescription(node, basicRegex.CONSTANTFUNCTIONTYPE) } /** @@ -766,7 +766,7 @@ function isCallToNonConstLocalFunction (node: FunctionCallAstNode): boolean { * @return {bool} */ function isLibraryCall (node: MemberAccessAstNode): boolean { - return isMemberAccess(node, basicRegex.FUNCTIONTYPE, undefined, basicRegex.LIBRARYTYPE, undefined) + return isMemberAccess(node, basicRegex.FUNCTIONTYPE, undefined, basicRegex.LIBRARYTYPE, undefined) } /** @@ -775,7 +775,7 @@ function isLibraryCall (node: MemberAccessAstNode): boolean { * @return {bool} */ function isExternalDirectCall (node: FunctionCallAstNode): boolean { - return isMemberAccess(node.expression, basicRegex.EXTERNALFUNCTIONTYPE, undefined, basicRegex.CONTRACTTYPE, undefined) && !isThisLocalCall(node.expression) && !isSuperLocalCall(node.expression) + return isMemberAccess(node.expression, basicRegex.EXTERNALFUNCTIONTYPE, undefined, basicRegex.CONTRACTTYPE, undefined) && !isThisLocalCall(node.expression) && !isSuperLocalCall(node.expression) } /** @@ -784,7 +784,7 @@ function isExternalDirectCall (node: FunctionCallAstNode): boolean { * @return {bool} */ function isNowAccess (node: IdentifierAstNode): boolean { - return node.name === 'now' && typeDescription(node, exactMatch(basicTypes.UINT)) + return node.name === 'now' && typeDescription(node, exactMatch(basicTypes.UINT)) } /** @@ -793,7 +793,7 @@ function isNowAccess (node: IdentifierAstNode): boolean { * @return {bool} */ function isTxOriginAccess (node: MemberAccessAstNode): boolean { - return isMemberAccess(node, 'address', 'tx', 'tx', 'origin') + return isMemberAccess(node, 'address', 'tx', 'tx', 'origin') } /** @@ -802,7 +802,7 @@ function isTxOriginAccess (node: MemberAccessAstNode): boolean { * @return {bool} */ function isBlockTimestampAccess (node: MemberAccessAstNode): boolean { - return isSpecialVariableAccess(node, specialVariables.BLOCKTIMESTAMP) + return isSpecialVariableAccess(node, specialVariables.BLOCKTIMESTAMP) } /** @@ -811,7 +811,7 @@ function isBlockTimestampAccess (node: MemberAccessAstNode): boolean { * @return {bool} */ function isBlockBlockHashAccess (node: FunctionCallAstNode): boolean { - return (isBuiltinFunctionCall(node) && getLocalCallName(node) === 'blockhash') || + return (isBuiltinFunctionCall(node) && getLocalCallName(node) === 'blockhash') || isSpecialVariableAccess(node.expression, specialVariables.BLOCKHASH) } @@ -821,7 +821,7 @@ function isBlockBlockHashAccess (node: FunctionCallAstNode): boolean { * @return {bool} */ function isThisLocalCall (node: MemberAccessAstNode): boolean { - return isMemberAccess(node, basicRegex.FUNCTIONTYPE, exactMatch('this'), basicRegex.CONTRACTTYPE, undefined) + return isMemberAccess(node, basicRegex.FUNCTIONTYPE, exactMatch('this'), basicRegex.CONTRACTTYPE, undefined) } /** @@ -830,7 +830,7 @@ function isThisLocalCall (node: MemberAccessAstNode): boolean { * @return {bool} */ function isSuperLocalCall (node: MemberAccessAstNode): boolean { - return isMemberAccess(node, basicRegex.FUNCTIONTYPE, exactMatch('super'), basicRegex.CONTRACTTYPE, undefined) + return isMemberAccess(node, basicRegex.FUNCTIONTYPE, exactMatch('super'), basicRegex.CONTRACTTYPE, undefined) } /** @@ -839,7 +839,7 @@ function isSuperLocalCall (node: MemberAccessAstNode): boolean { * @return {bool} */ function isLocalCall (node: FunctionCallAstNode): boolean { - return node.nodeType === 'FunctionCall' && node.kind === 'functionCall' && + return node.nodeType === 'FunctionCall' && node.kind === 'functionCall' && node.expression.nodeType === 'Identifier' && expressionTypeDescription(node, basicRegex.FUNCTIONTYPE) && !expressionTypeDescription(node, basicRegex.EXTERNALFUNCTIONTYPE) } @@ -850,7 +850,7 @@ function isLocalCall (node: FunctionCallAstNode): boolean { * @return {bool} */ function isLowLevelCall (node: MemberAccessAstNode): boolean { - return isLLCall(node) || + return isLLCall(node) || isLLDelegatecall(node) || isLLSend(node) || isLLSend04(node) || @@ -865,9 +865,9 @@ function isLowLevelCall (node: MemberAccessAstNode): boolean { * @return {bool} */ function isLLSend04 (node: MemberAccessAstNode): boolean { - return isMemberAccess(node, - exactMatch(util.escapeRegExp(lowLevelCallTypes.SEND.type)), - undefined, exactMatch(basicTypes.ADDRESS), exactMatch(lowLevelCallTypes.SEND.ident)) + return isMemberAccess(node, + exactMatch(util.escapeRegExp(lowLevelCallTypes.SEND.type)), + undefined, exactMatch(basicTypes.ADDRESS), exactMatch(lowLevelCallTypes.SEND.ident)) } /** @@ -876,9 +876,9 @@ function isLLSend04 (node: MemberAccessAstNode): boolean { * @return {bool} */ function isLLSend (node: MemberAccessAstNode): boolean { - return isMemberAccess(node, - exactMatch(util.escapeRegExp(lowLevelCallTypes.SEND.type)), - undefined, exactMatch(basicTypes.PAYABLE_ADDRESS), exactMatch(lowLevelCallTypes.SEND.ident)) + return isMemberAccess(node, + exactMatch(util.escapeRegExp(lowLevelCallTypes.SEND.type)), + undefined, exactMatch(basicTypes.PAYABLE_ADDRESS), exactMatch(lowLevelCallTypes.SEND.ident)) } /** @@ -887,12 +887,12 @@ function isLLSend (node: MemberAccessAstNode): boolean { * @return {bool} */ function isLLCall (node: MemberAccessAstNode): boolean { - return isMemberAccess(node, - exactMatch(util.escapeRegExp(lowLevelCallTypes.CALL.type)), - undefined, exactMatch(basicTypes.ADDRESS), exactMatch(lowLevelCallTypes.CALL.ident)) || + return isMemberAccess(node, + exactMatch(util.escapeRegExp(lowLevelCallTypes.CALL.type)), + undefined, exactMatch(basicTypes.ADDRESS), exactMatch(lowLevelCallTypes.CALL.ident)) || isMemberAccess(node, - exactMatch(util.escapeRegExp(lowLevelCallTypes.CALL.type)), - undefined, exactMatch(basicTypes.PAYABLE_ADDRESS), exactMatch(lowLevelCallTypes.CALL.ident)) + exactMatch(util.escapeRegExp(lowLevelCallTypes.CALL.type)), + undefined, exactMatch(basicTypes.PAYABLE_ADDRESS), exactMatch(lowLevelCallTypes.CALL.ident)) } /** @@ -901,9 +901,9 @@ function isLLCall (node: MemberAccessAstNode): boolean { * @return {bool} */ function isLLCall04 (node: MemberAccessAstNode): boolean { - return isMemberAccess(node, - exactMatch(util.escapeRegExp(lowLevelCallTypes['CALL-0.4'].type)), - undefined, exactMatch(basicTypes.ADDRESS), exactMatch(lowLevelCallTypes['CALL-0.4'].ident)) + return isMemberAccess(node, + exactMatch(util.escapeRegExp(lowLevelCallTypes['CALL-0.4'].type)), + undefined, exactMatch(basicTypes.ADDRESS), exactMatch(lowLevelCallTypes['CALL-0.4'].ident)) } /** @@ -912,9 +912,9 @@ function isLLCall04 (node: MemberAccessAstNode): boolean { * @return {bool} */ function isLLCallcode (node: MemberAccessAstNode): boolean { - return isMemberAccess(node, - exactMatch(util.escapeRegExp(lowLevelCallTypes.CALLCODE.type)), - undefined, exactMatch(basicTypes.ADDRESS), exactMatch(lowLevelCallTypes.CALLCODE.ident)) + return isMemberAccess(node, + exactMatch(util.escapeRegExp(lowLevelCallTypes.CALLCODE.type)), + undefined, exactMatch(basicTypes.ADDRESS), exactMatch(lowLevelCallTypes.CALLCODE.ident)) } /** @@ -923,9 +923,9 @@ function isLLCallcode (node: MemberAccessAstNode): boolean { * @return {bool} */ function isLLDelegatecall (node: MemberAccessAstNode): boolean { - return isMemberAccess(node, - exactMatch(util.escapeRegExp(lowLevelCallTypes.DELEGATECALL.type)), - undefined, matches(basicTypes.PAYABLE_ADDRESS, basicTypes.ADDRESS), exactMatch(lowLevelCallTypes.DELEGATECALL.ident)) + return isMemberAccess(node, + exactMatch(util.escapeRegExp(lowLevelCallTypes.DELEGATECALL.type)), + undefined, matches(basicTypes.PAYABLE_ADDRESS, basicTypes.ADDRESS), exactMatch(lowLevelCallTypes.DELEGATECALL.ident)) } /** @@ -934,9 +934,9 @@ function isLLDelegatecall (node: MemberAccessAstNode): boolean { * @return {bool} */ function isLLDelegatecall04 (node: MemberAccessAstNode): boolean { - return isMemberAccess(node, - exactMatch(util.escapeRegExp(lowLevelCallTypes['DELEGATECALL-0.4'].type)), - undefined, matches(basicTypes.PAYABLE_ADDRESS, basicTypes.ADDRESS), exactMatch(lowLevelCallTypes['DELEGATECALL-0.4'].ident)) + return isMemberAccess(node, + exactMatch(util.escapeRegExp(lowLevelCallTypes['DELEGATECALL-0.4'].type)), + undefined, matches(basicTypes.PAYABLE_ADDRESS, basicTypes.ADDRESS), exactMatch(lowLevelCallTypes['DELEGATECALL-0.4'].ident)) } /** @@ -945,23 +945,23 @@ function isLLDelegatecall04 (node: MemberAccessAstNode): boolean { * @return {bool} */ function isTransfer (node: MemberAccessAstNode): boolean { - return isMemberAccess(node, - exactMatch(util.escapeRegExp(lowLevelCallTypes.TRANSFER.type)), - undefined, matches(basicTypes.ADDRESS, basicTypes.PAYABLE_ADDRESS), exactMatch(lowLevelCallTypes.TRANSFER.ident)) + return isMemberAccess(node, + exactMatch(util.escapeRegExp(lowLevelCallTypes.TRANSFER.type)), + undefined, matches(basicTypes.ADDRESS, basicTypes.PAYABLE_ADDRESS), exactMatch(lowLevelCallTypes.TRANSFER.ident)) } function isStringToBytesConversion (node: FunctionCallAstNode): boolean { - return isExplicitCast(node, util.escapeRegExp('string *'), util.escapeRegExp('bytes')) + return isExplicitCast(node, util.escapeRegExp('string *'), util.escapeRegExp('bytes')) } function isExplicitCast (node: FunctionCallAstNode, castFromType: string, castToType: string): boolean { - return node.kind === 'typeConversion' && + return node.kind === 'typeConversion' && nodeType(node.expression, exactMatch(nodeTypes.ELEMENTARYTYPENAMEEXPRESSION)) && node.expression.typeName === castToType && nodeType(node.arguments[0], exactMatch(nodeTypes.IDENTIFIER)) && typeDescription(node.arguments[0], castFromType) } function isBytesLengthCheck (node: MemberAccessAstNode): boolean { - return isMemberAccess(node, exactMatch(util.escapeRegExp(basicTypes.UINT)), undefined, util.escapeRegExp('bytes *'), 'length') + return isMemberAccess(node, exactMatch(util.escapeRegExp(basicTypes.UINT)), undefined, util.escapeRegExp('bytes *'), 'length') } /** @@ -971,7 +971,7 @@ function isBytesLengthCheck (node: MemberAccessAstNode): boolean { */ // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types function isLoop (node: any): boolean { - return nodeType(node, exactMatch(nodeTypes.FORSTATEMENT)) || + return nodeType(node, exactMatch(nodeTypes.FORSTATEMENT)) || nodeType(node, exactMatch(nodeTypes.WHILESTATEMENT)) || nodeType(node, exactMatch(nodeTypes.DOWHILESTATEMENT)) } @@ -979,63 +979,63 @@ function isLoop (node: any): boolean { // #################### Complex Node Identification - Private function isMemberAccess (node: MemberAccessAstNode, retType: string, accessor: string| undefined, accessorType: string, memberName: string | undefined): boolean { - if (node && nodeType(node, exactMatch('MemberAccess'))) { - const nodeTypeDef: boolean = typeDescription(node, retType) - const nodeMemName: boolean = memName(node, memberName) - const nodeExpMemName: boolean = memName(node.expression, accessor) - const nodeExpTypeDef: boolean = expressionTypeDescription(node, accessorType) - return nodeTypeDef && nodeMemName && nodeExpTypeDef && nodeExpMemName - } else return false + if (node && nodeType(node, exactMatch('MemberAccess'))) { + const nodeTypeDef: boolean = typeDescription(node, retType) + const nodeMemName: boolean = memName(node, memberName) + const nodeExpMemName: boolean = memName(node.expression, accessor) + const nodeExpTypeDef: boolean = expressionTypeDescription(node, accessorType) + return nodeTypeDef && nodeMemName && nodeExpTypeDef && nodeExpMemName + } else return false } function isSpecialVariableAccess (node: MemberAccessAstNode, varType: SpecialObjDetail): boolean { - return isMemberAccess(node, exactMatch(util.escapeRegExp(varType.type)), varType.obj, varType.obj, varType.member) + return isMemberAccess(node, exactMatch(util.escapeRegExp(varType.type)), varType.obj, varType.obj, varType.member) } // #################### Node Identification Primitives // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types function expressionTypeDescription (node: any, typeRegex: string): boolean { - return new RegExp(typeRegex).test(node.expression.typeDescriptions.typeString) + return new RegExp(typeRegex).test(node.expression.typeDescriptions.typeString) } // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types function typeDescription (node: any, typeRegex: string): boolean { - return new RegExp(typeRegex).test(node.typeDescriptions.typeString) + return new RegExp(typeRegex).test(node.typeDescriptions.typeString) } // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types function nodeType (node: any, typeRegex: string): boolean { - return new RegExp(typeRegex).test(node.nodeType) + return new RegExp(typeRegex).test(node.nodeType) } // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types function nodeTypeIn (node: any, typeRegex: string[]): boolean { - return typeRegex.some((typeRegex) => nodeType(node, typeRegex)) + return typeRegex.some((typeRegex) => nodeType(node, typeRegex)) } // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types function memName (node: any, memNameRegex: any): boolean { - return (node && !memNameRegex) || new RegExp(memNameRegex).test(node.name) || new RegExp(memNameRegex).test(node.memberName) + return (node && !memNameRegex) || new RegExp(memNameRegex).test(node.name) || new RegExp(memNameRegex).test(node.memberName) } // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types function operator (node: any, opRegex: string): boolean { - return new RegExp(opRegex).test(node.operator) + return new RegExp(opRegex).test(node.operator) } // #################### Helpers function exactMatch (regexStr: string): string { - return '^' + regexStr + '$' + return '^' + regexStr + '$' } function matches (...fnArgs: any[]): string { - const args: any[] = [] - for (let k = 0; k < fnArgs.length; k++) { - args.push(fnArgs[k]) - } - return '(' + args.join('|') + ')' + const args: any[] = [] + for (let k = 0; k < fnArgs.length; k++) { + args.push(fnArgs[k]) + } + return '(' + args.join('|') + ')' } /** @@ -1046,7 +1046,7 @@ function matches (...fnArgs: any[]): string { * Note: developed keeping identifier node search in mind to get first identifier node from left in subscope */ function findFirstSubNodeLTR (node: any, type: string): any { - if (node.nodeType && nodeType(node, type)) { return node } else if (node.nodeType && nodeType(node, exactMatch('Assignment'))) { return findFirstSubNodeLTR(node.leftHandSide, type) } else if (node.nodeType && nodeType(node, exactMatch('MemberAccess'))) { return findFirstSubNodeLTR(node.expression, type) } else if (node.nodeType && nodeType(node, exactMatch('IndexAccess'))) { return findFirstSubNodeLTR(node.baseExpression, type) } else if (node.nodeType && nodeType(node, exactMatch('UnaryOperation'))) { return findFirstSubNodeLTR(node.subExpression, type) } + if (node.nodeType && nodeType(node, type)) { return node } else if (node.nodeType && nodeType(node, exactMatch('Assignment'))) { return findFirstSubNodeLTR(node.leftHandSide, type) } else if (node.nodeType && nodeType(node, exactMatch('MemberAccess'))) { return findFirstSubNodeLTR(node.expression, type) } else if (node.nodeType && nodeType(node, exactMatch('IndexAccess'))) { return findFirstSubNodeLTR(node.baseExpression, type) } else if (node.nodeType && nodeType(node, exactMatch('UnaryOperation'))) { return findFirstSubNodeLTR(node.subExpression, type) } } /** @@ -1059,52 +1059,52 @@ function findFirstSubNodeLTR (node: any, type: string): any { */ // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types function buildFunctionSignature (paramTypes: any[], returnTypes: any[], isPayable: boolean, additionalMods?: any): string { - return 'function (' + util.concatWithSeperator(paramTypes, ',') + ')' + ((isPayable) ? ' payable' : '') + ((additionalMods) ? ' ' + additionalMods : '') + ((returnTypes.length) ? ' returns (' + util.concatWithSeperator(returnTypes, ',') + ')' : '') + return 'function (' + util.concatWithSeperator(paramTypes, ',') + ')' + ((isPayable) ? ' payable' : '') + ((additionalMods) ? ' ' + additionalMods : '') + ((returnTypes.length) ? ' returns (' + util.concatWithSeperator(returnTypes, ',') + ')' : '') } function buildAbiSignature (funName: string, paramTypes: any[]): string { - return funName + '(' + util.concatWithSeperator(paramTypes, ',') + ')' + return funName + '(' + util.concatWithSeperator(paramTypes, ',') + ')' } // To create the method signature similar to contract.evm.gasEstimates.external object // For address payable, return address function getMethodParamsSplittedTypeDesc (node: FunctionDefinitionAstNode, contracts: CompiledContractObj): string[] { - return node.parameters.parameters.map((varNode, varIndex) => { - let finalTypeString - const typeString = varNode.typeDescriptions.typeString - if (typeString.includes('struct')) { - const fnName = node.name - for (const filename in contracts) { - for (const contractName in contracts[filename]) { - const methodABI = contracts[filename][contractName].abi - .find(e => e.name === fnName && e.inputs?.length && + return node.parameters.parameters.map((varNode, varIndex) => { + let finalTypeString + const typeString = varNode.typeDescriptions.typeString + if (typeString.includes('struct')) { + const fnName = node.name + for (const filename in contracts) { + for (const contractName in contracts[filename]) { + const methodABI = contracts[filename][contractName].abi + .find(e => e.name === fnName && e.inputs?.length && e.inputs[varIndex]['type'].includes('tuple') && e.inputs[varIndex]['internalType'] === typeString) - if (methodABI && methodABI.inputs) { - const inputs = methodABI.inputs[varIndex] - const typeStr = getTypeStringFromComponents(inputs['components']) - finalTypeString = typeStr + inputs['type'].replace('tuple', '') - } - } - } - } else { finalTypeString = typeString.split(' ')[0] } - return finalTypeString - }) + if (methodABI && methodABI.inputs) { + const inputs = methodABI.inputs[varIndex] + const typeStr = getTypeStringFromComponents(inputs['components']) + finalTypeString = typeStr + inputs['type'].replace('tuple', '') + } + } + } + } else { finalTypeString = typeString.split(' ')[0] } + return finalTypeString + }) } function getTypeStringFromComponents (components: ABIParameter[]) { - let typeString = '(' - for (let i = 0; i < components.length; i++) { - const param = components[i] - if (param.type.includes('tuple') && param.components && param.components.length > 0) { - typeString = typeString + getTypeStringFromComponents(param.components) - typeString = typeString + param.type.replace('tuple', '') - } else { typeString = typeString + param.type } - - if (i !== components.length - 1) { typeString = typeString + ',' } - } - typeString = typeString + ')' - return typeString + let typeString = '(' + for (let i = 0; i < components.length; i++) { + const param = components[i] + if (param.type.includes('tuple') && param.components && param.components.length > 0) { + typeString = typeString + getTypeStringFromComponents(param.components) + typeString = typeString + param.type.replace('tuple', '') + } else { typeString = typeString + param.type } + + if (i !== components.length - 1) { typeString = typeString + ',' } + } + typeString = typeString + ')' + return typeString } /** @@ -1113,119 +1113,119 @@ function getTypeStringFromComponents (components: ABIParameter[]) { * @param contractFiles compiled contract object */ function getCompilerVersion (contractFiles: CompiledContractObj): string { - let version = 'latest' - const fileNames: string[] = Object.keys(contractFiles) - const contracts = contractFiles[fileNames[0]] - const contractNames: string[] = Object.keys(contracts) - const contract: CompiledContract = contracts[contractNames[0]] - // For some compiler/contract, metadata is "" - if (contract && contract.metadata) { - const metadata = JSON.parse(contract.metadata) - const compilerVersion: string = metadata.compiler.version - if (!compilerVersion.includes('nightly')) { version = 'v' + compilerVersion.split('+commit')[0] } - } - return version + let version = 'latest' + const fileNames: string[] = Object.keys(contractFiles) + const contracts = contractFiles[fileNames[0]] + const contractNames: string[] = Object.keys(contracts) + const contract: CompiledContract = contracts[contractNames[0]] + // For some compiler/contract, metadata is "" + if (contract && contract.metadata) { + const metadata = JSON.parse(contract.metadata) + const compilerVersion: string = metadata.compiler.version + if (!compilerVersion.includes('nightly')) { version = 'v' + compilerVersion.split('+commit')[0] } + } + return version } const helpers = { - expressionTypeDescription, - nodeType, - memName, - operator, - buildFunctionSignature, - buildAbiSignature + expressionTypeDescription, + nodeType, + memName, + operator, + buildFunctionSignature, + buildAbiSignature } export { - // #################### Trivial Getters - getType, - // #################### Complex Getters - getThisLocalCallName, - getSuperLocalCallName, - getFunctionCallType, - getContractName, - getEffectedVariableName, - getDeclaredVariableName, - getDeclaredVariableType, - getLocalCallName, - getInheritsFromName, - getExternalDirectCallContractName, - getThisLocalCallContractName, - getExternalDirectCallMemberName, - getFunctionDefinitionName, - getFunctionCallTypeParameterType, - getLibraryCallContractName, - getLibraryCallMemberName, - getFullQualifiedFunctionCallIdent, - getFullQuallyfiedFuncDefinitionIdent, - getStateVariableDeclarationsFromContractNode, - getFunctionOrModifierDefinitionParameterPart, - getFunctionDefinitionReturnParameterPart, - getUnAssignedTopLevelBinOps, - getMethodParamsSplittedTypeDesc, - getCompilerVersion, - - // #################### Complex Node Identification - isDeleteOfDynamicArray, - isDeleteFromDynamicArray, - isAbiNamespaceCall, - isSpecialVariableAccess, - isVariableTurnedIntoGetter, - isDynamicArrayAccess, - isDynamicArrayLengthAccess, - isMappingIndexAccess, - isSubScopeWithTopLevelUnAssignedBinOp, - hasFunctionBody, - isInteraction, - isEffect, - isTxOriginAccess, - isNowAccess, - isBlockTimestampAccess, - isBlockBlockHashAccess, - isThisLocalCall, - isSuperLocalCall, - isLibraryCall, - isLocalCallGraphRelevantNode, - isLocalCall, - isWriteOnStateVariable, - isStateVariable, - isTransfer, - isLowLevelCall, - isLLCall, - isLLCall04, - isLLCallcode, - isLLDelegatecall, - isLLDelegatecall04, - isLLSend, - isLLSend04, - isExternalDirectCall, - isFullyImplementedContract, - isLibrary, - isCallToNonConstLocalFunction, - isPlusPlusUnaryOperation, - isMinusMinusUnaryOperation, - isBuiltinFunctionCall, - isSelfdestructCall, - isAssertCall, - isRequireCall, - isIntDivision, - isStringToBytesConversion, - isBytesLengthCheck, - isLoop, - - // #################### Trivial Node Identification - isDeleteUnaryOperation, - isStorageVariableDeclaration, - isConstantFunction, - isPayableFunction, - isConstructor, - isStatement, - - // #################### Constants - nodeTypes, - basicTypes, - basicFunctionTypes, - lowLevelCallTypes, - specialVariables, - helpers + // #################### Trivial Getters + getType, + // #################### Complex Getters + getThisLocalCallName, + getSuperLocalCallName, + getFunctionCallType, + getContractName, + getEffectedVariableName, + getDeclaredVariableName, + getDeclaredVariableType, + getLocalCallName, + getInheritsFromName, + getExternalDirectCallContractName, + getThisLocalCallContractName, + getExternalDirectCallMemberName, + getFunctionDefinitionName, + getFunctionCallTypeParameterType, + getLibraryCallContractName, + getLibraryCallMemberName, + getFullQualifiedFunctionCallIdent, + getFullQuallyfiedFuncDefinitionIdent, + getStateVariableDeclarationsFromContractNode, + getFunctionOrModifierDefinitionParameterPart, + getFunctionDefinitionReturnParameterPart, + getUnAssignedTopLevelBinOps, + getMethodParamsSplittedTypeDesc, + getCompilerVersion, + + // #################### Complex Node Identification + isDeleteOfDynamicArray, + isDeleteFromDynamicArray, + isAbiNamespaceCall, + isSpecialVariableAccess, + isVariableTurnedIntoGetter, + isDynamicArrayAccess, + isDynamicArrayLengthAccess, + isMappingIndexAccess, + isSubScopeWithTopLevelUnAssignedBinOp, + hasFunctionBody, + isInteraction, + isEffect, + isTxOriginAccess, + isNowAccess, + isBlockTimestampAccess, + isBlockBlockHashAccess, + isThisLocalCall, + isSuperLocalCall, + isLibraryCall, + isLocalCallGraphRelevantNode, + isLocalCall, + isWriteOnStateVariable, + isStateVariable, + isTransfer, + isLowLevelCall, + isLLCall, + isLLCall04, + isLLCallcode, + isLLDelegatecall, + isLLDelegatecall04, + isLLSend, + isLLSend04, + isExternalDirectCall, + isFullyImplementedContract, + isLibrary, + isCallToNonConstLocalFunction, + isPlusPlusUnaryOperation, + isMinusMinusUnaryOperation, + isBuiltinFunctionCall, + isSelfdestructCall, + isAssertCall, + isRequireCall, + isIntDivision, + isStringToBytesConversion, + isBytesLengthCheck, + isLoop, + + // #################### Trivial Node Identification + isDeleteUnaryOperation, + isStorageVariableDeclaration, + isConstantFunction, + isPayableFunction, + isConstructor, + isStatement, + + // #################### Constants + nodeTypes, + basicTypes, + basicFunctionTypes, + lowLevelCallTypes, + specialVariables, + helpers } diff --git a/libs/remix-analyzer/src/solidity-analyzer/modules/stringBytesLength.ts b/libs/remix-analyzer/src/solidity-analyzer/modules/stringBytesLength.ts index 57c02b84dd..423c83041f 100644 --- a/libs/remix-analyzer/src/solidity-analyzer/modules/stringBytesLength.ts +++ b/libs/remix-analyzer/src/solidity-analyzer/modules/stringBytesLength.ts @@ -4,33 +4,33 @@ import { isStringToBytesConversion, isBytesLengthCheck, getCompilerVersion } fro import { AnalyzerModule, ModuleAlgorithm, ModuleCategory, ReportObj, CompilationResult, MemberAccessAstNode, FunctionCallAstNode, SupportedVersion } from './../../types' export default class stringBytesLength implements AnalyzerModule { - name = 'String length: ' - description = 'Bytes length != String length' - category: ModuleCategory = category.MISC - algorithm: ModuleAlgorithm = algorithm.EXACT - version: SupportedVersion = { - start: '0.4.12' - } + name = 'String length: ' + description = 'Bytes length != String length' + category: ModuleCategory = category.MISC + algorithm: ModuleAlgorithm = algorithm.EXACT + version: SupportedVersion = { + start: '0.4.12' + } - stringToBytesConversions: FunctionCallAstNode[] = [] - bytesLengthChecks: MemberAccessAstNode[] = [] + stringToBytesConversions: FunctionCallAstNode[] = [] + bytesLengthChecks: MemberAccessAstNode[] = [] - visit (node: FunctionCallAstNode | MemberAccessAstNode): void { - if (node.nodeType === 'FunctionCall' && isStringToBytesConversion(node)) this.stringToBytesConversions.push(node) - else if (node.nodeType === 'MemberAccess' && isBytesLengthCheck(node)) this.bytesLengthChecks.push(node) - } + visit (node: FunctionCallAstNode | MemberAccessAstNode): void { + if (node.nodeType === 'FunctionCall' && isStringToBytesConversion(node)) this.stringToBytesConversions.push(node) + else if (node.nodeType === 'MemberAccess' && isBytesLengthCheck(node)) this.bytesLengthChecks.push(node) + } - // eslint-disable-next-line @typescript-eslint/no-unused-vars - report (compilationResults: CompilationResult): ReportObj[] { - const version = getCompilerVersion(compilationResults.contracts) - if (this.stringToBytesConversions.length > 0 && this.bytesLengthChecks.length > 0) { - return [{ - warning: '"bytes" and "string" lengths are not the same since strings are assumed to be UTF-8 encoded (according to the ABI defintion) therefore one character is not nessesarily encoded in one byte of data.', - location: this.bytesLengthChecks[0].src, - more: `https://solidity.readthedocs.io/en/${version}/abi-spec.html#argument-encoding` - }] - } else { - return [] - } + // eslint-disable-next-line @typescript-eslint/no-unused-vars + report (compilationResults: CompilationResult): ReportObj[] { + const version = getCompilerVersion(compilationResults.contracts) + if (this.stringToBytesConversions.length > 0 && this.bytesLengthChecks.length > 0) { + return [{ + warning: '"bytes" and "string" lengths are not the same since strings are assumed to be UTF-8 encoded (according to the ABI defintion) therefore one character is not nessesarily encoded in one byte of data.', + location: this.bytesLengthChecks[0].src, + more: `https://solidity.readthedocs.io/en/${version}/abi-spec.html#argument-encoding` + }] + } else { + return [] } + } } diff --git a/libs/remix-analyzer/src/solidity-analyzer/modules/thisLocal.ts b/libs/remix-analyzer/src/solidity-analyzer/modules/thisLocal.ts index 9c13b4c2ac..450142ce7f 100644 --- a/libs/remix-analyzer/src/solidity-analyzer/modules/thisLocal.ts +++ b/libs/remix-analyzer/src/solidity-analyzer/modules/thisLocal.ts @@ -4,28 +4,28 @@ import algorithm from './algorithmCategories' import { AnalyzerModule, ModuleAlgorithm, ModuleCategory, ReportObj, CompilationResult, MemberAccessAstNode, SupportedVersion } from './../../types' export default class thisLocal implements AnalyzerModule { - warningNodes: MemberAccessAstNode[] = [] - name = 'This on local calls: ' - description = 'Invocation of local functions via \'this\'' - category: ModuleCategory = category.GAS - algorithm: ModuleAlgorithm = algorithm.EXACT - version: SupportedVersion = { - start: '0.4.12' - } + warningNodes: MemberAccessAstNode[] = [] + name = 'This on local calls: ' + description = 'Invocation of local functions via \'this\'' + category: ModuleCategory = category.GAS + algorithm: ModuleAlgorithm = algorithm.EXACT + version: SupportedVersion = { + start: '0.4.12' + } - visit (node: MemberAccessAstNode): void { - if (node.nodeType === 'MemberAccess' && isThisLocalCall(node)) this.warningNodes.push(node) - } + visit (node: MemberAccessAstNode): void { + if (node.nodeType === 'MemberAccess' && isThisLocalCall(node)) this.warningNodes.push(node) + } - // eslint-disable-next-line @typescript-eslint/no-unused-vars - report (compilationResults: CompilationResult): ReportObj[] { - const version = getCompilerVersion(compilationResults.contracts) - return this.warningNodes.map(function (item, i) { - return { - warning: 'Use of "this" for local functions: Never use "this" to call functions in the same contract, it only consumes more gas than normal local calls.', - location: item.src, - more: `https://solidity.readthedocs.io/en/${version}/control-structures.html#external-function-calls` - } - }) - } + // eslint-disable-next-line @typescript-eslint/no-unused-vars + report (compilationResults: CompilationResult): ReportObj[] { + const version = getCompilerVersion(compilationResults.contracts) + return this.warningNodes.map(function (item, i) { + return { + warning: 'Use of "this" for local functions: Never use "this" to call functions in the same contract, it only consumes more gas than normal local calls.', + location: item.src, + more: `https://solidity.readthedocs.io/en/${version}/control-structures.html#external-function-calls` + } + }) + } } diff --git a/libs/remix-analyzer/src/solidity-analyzer/modules/txOrigin.ts b/libs/remix-analyzer/src/solidity-analyzer/modules/txOrigin.ts index 1a7a0cd0d7..ad12f3cb65 100644 --- a/libs/remix-analyzer/src/solidity-analyzer/modules/txOrigin.ts +++ b/libs/remix-analyzer/src/solidity-analyzer/modules/txOrigin.ts @@ -4,29 +4,29 @@ import { isTxOriginAccess, getCompilerVersion } from './staticAnalysisCommon' import { AnalyzerModule, ModuleAlgorithm, ModuleCategory, ReportObj, CompilationResult, MemberAccessAstNode, SupportedVersion } from './../../types' export default class txOrigin implements AnalyzerModule { - txOriginNodes: MemberAccessAstNode[] = [] - name = 'Transaction origin: ' - description = '\'tx.origin\' used' - category: ModuleCategory = category.SECURITY - algorithm: ModuleAlgorithm = algorithm.EXACT - version: SupportedVersion = { - start: '0.4.12' - } + txOriginNodes: MemberAccessAstNode[] = [] + name = 'Transaction origin: ' + description = '\'tx.origin\' used' + category: ModuleCategory = category.SECURITY + algorithm: ModuleAlgorithm = algorithm.EXACT + version: SupportedVersion = { + start: '0.4.12' + } - visit (node: MemberAccessAstNode): void { - if (isTxOriginAccess(node)) this.txOriginNodes.push(node) - } + visit (node: MemberAccessAstNode): void { + if (isTxOriginAccess(node)) this.txOriginNodes.push(node) + } - // eslint-disable-next-line @typescript-eslint/no-unused-vars - report (compilationResults: CompilationResult): ReportObj[] { - const version = getCompilerVersion(compilationResults.contracts) - return this.txOriginNodes.map((item, i) => { - return { - warning: `Use of tx.origin: "tx.origin" is useful only in very exceptional cases. + // eslint-disable-next-line @typescript-eslint/no-unused-vars + report (compilationResults: CompilationResult): ReportObj[] { + const version = getCompilerVersion(compilationResults.contracts) + return this.txOriginNodes.map((item, i) => { + return { + warning: `Use of tx.origin: "tx.origin" is useful only in very exceptional cases. If you use it for authentication, you usually want to replace it by "msg.sender", because otherwise any contract you call can act on your behalf.`, - location: item.src, - more: `https://solidity.readthedocs.io/en/${version}/security-considerations.html#tx-origin` - } - }) - } + location: item.src, + more: `https://solidity.readthedocs.io/en/${version}/security-considerations.html#tx-origin` + } + }) + } } diff --git a/libs/remix-analyzer/test/analysis/staticAnalysisCommon-test.ts b/libs/remix-analyzer/test/analysis/staticAnalysisCommon-test.ts index 859dfe9758..28d4059699 100644 --- a/libs/remix-analyzer/test/analysis/staticAnalysisCommon-test.ts +++ b/libs/remix-analyzer/test/analysis/staticAnalysisCommon-test.ts @@ -1,495 +1,495 @@ import { default as test} from "tape" import * as common from '../../src/solidity-analyzer/modules/staticAnalysisCommon' const { localCall, thisLocalCall, libCall, externalDirect, superLocal, assignment, abiNamespaceCallNodes, - inlineAssembly, unaryOperation, nowAst, blockTimestamp, stateVariableContractNode, - functionDefinition, requireCall, selfdestruct, storageVariableNodes, dynamicDeleteUnaryOp, - // eslint-disable-next-line @typescript-eslint/no-var-requires - lowlevelCall, parameterFunction, parameterFunctionCall, inheritance, blockHashAccess, contractDefinition, funcDefForComplexParams } = require('./astBlocks') + inlineAssembly, unaryOperation, nowAst, blockTimestamp, stateVariableContractNode, + functionDefinition, requireCall, selfdestruct, storageVariableNodes, dynamicDeleteUnaryOp, + // eslint-disable-next-line @typescript-eslint/no-var-requires + lowlevelCall, parameterFunction, parameterFunctionCall, inheritance, blockHashAccess, contractDefinition, funcDefForComplexParams } = require('./astBlocks') // eslint-disable-next-line @typescript-eslint/no-var-requires const compiledContractObj = require('./compilationDetails/CompiledContractObj.json') function escapeRegExp (str) { - return str.replace(/[-[\]/{}()+?.\\^$|]/g, '\\$&') + return str.replace(/[-[\]/{}()+?.\\^$|]/g, '\\$&') } test('staticAnalysisCommon.helpers.buildFunctionSignature', function (t) { - t.plan(11) + t.plan(11) - t.equal(common.helpers.buildFunctionSignature([common.basicTypes.UINT, common.basicTypes.ADDRESS], [common.basicTypes.BOOL], false), - 'function (uint256,address) returns (bool)', - 'two params and return value without payable') + t.equal(common.helpers.buildFunctionSignature([common.basicTypes.UINT, common.basicTypes.ADDRESS], [common.basicTypes.BOOL], false), + 'function (uint256,address) returns (bool)', + 'two params and return value without payable') - t.equal(common.helpers.buildFunctionSignature([common.basicTypes.UINT, common.basicTypes.ADDRESS], [common.basicTypes.BOOL], false, 'pure'), - 'function (uint256,address) pure returns (bool)', - 'two params and return value without payable but pure') + t.equal(common.helpers.buildFunctionSignature([common.basicTypes.UINT, common.basicTypes.ADDRESS], [common.basicTypes.BOOL], false, 'pure'), + 'function (uint256,address) pure returns (bool)', + 'two params and return value without payable but pure') - t.equal(common.helpers.buildFunctionSignature([common.basicTypes.UINT, common.basicTypes.ADDRESS], [common.basicTypes.BOOL], true, 'pure'), - 'function (uint256,address) payable pure returns (bool)', - 'two params and return value without payable but pure') + t.equal(common.helpers.buildFunctionSignature([common.basicTypes.UINT, common.basicTypes.ADDRESS], [common.basicTypes.BOOL], true, 'pure'), + 'function (uint256,address) payable pure returns (bool)', + 'two params and return value without payable but pure') - t.equal(common.helpers.buildFunctionSignature([common.basicTypes.UINT, common.basicTypes.BYTES32, common.basicTypes.BYTES32], [], true), - 'function (uint256,bytes32,bytes32) payable', - 'three params and no return with payable') + t.equal(common.helpers.buildFunctionSignature([common.basicTypes.UINT, common.basicTypes.BYTES32, common.basicTypes.BYTES32], [], true), + 'function (uint256,bytes32,bytes32) payable', + 'three params and no return with payable') - t.equal(common.helpers.buildFunctionSignature([common.basicTypes.BOOL], [common.basicTypes.BYTES32, common.basicTypes.ADDRESS], true), - 'function (bool) payable returns (bytes32,address)', - 'one param and two return values with payable') + t.equal(common.helpers.buildFunctionSignature([common.basicTypes.BOOL], [common.basicTypes.BYTES32, common.basicTypes.ADDRESS], true), + 'function (bool) payable returns (bytes32,address)', + 'one param and two return values with payable') - t.equal(common.lowLevelCallTypes.CALL.type, - 'function (bytes memory) payable returns (bool,bytes memory)', - 'check fixed call type') + t.equal(common.lowLevelCallTypes.CALL.type, + 'function (bytes memory) payable returns (bool,bytes memory)', + 'check fixed call type') - t.equal(common.lowLevelCallTypes['CALL-0.4'].type, - 'function () payable returns (bool)', - 'check fixed call type for versions before 0.5.0') + t.equal(common.lowLevelCallTypes['CALL-0.4'].type, + 'function () payable returns (bool)', + 'check fixed call type for versions before 0.5.0') - t.equal(common.lowLevelCallTypes.CALLCODE.type, - 'function () payable returns (bool)', - 'check fixed callcode type') + t.equal(common.lowLevelCallTypes.CALLCODE.type, + 'function () payable returns (bool)', + 'check fixed callcode type') - t.equal(common.lowLevelCallTypes.SEND.type, - 'function (uint256) returns (bool)', - 'check fixed send type') + t.equal(common.lowLevelCallTypes.SEND.type, + 'function (uint256) returns (bool)', + 'check fixed send type') - t.equal(common.lowLevelCallTypes.DELEGATECALL.type, - 'function (bytes memory) returns (bool,bytes memory)', - 'check fixed delegatecall type') + t.equal(common.lowLevelCallTypes.DELEGATECALL.type, + 'function (bytes memory) returns (bool,bytes memory)', + 'check fixed delegatecall type') - t.equal(common.lowLevelCallTypes['DELEGATECALL-0.4'].type, - 'function () returns (bool)', - 'check fixed delegatecall type for version before 0.5.0') + t.equal(common.lowLevelCallTypes['DELEGATECALL-0.4'].type, + 'function () returns (bool)', + 'check fixed delegatecall type for version before 0.5.0') }) // #################### Node Identification Primitives test('staticAnalysisCommon.helpers.name', function (t) { - t.plan(3) - const node = { name: 'now' } - const node2 = { memberName: 'call' } + t.plan(3) + const node = { name: 'now' } + const node2 = { memberName: 'call' } - t.ok(common.helpers.memName(node, 'now'), 'should work for names') - t.ok(common.helpers.memName(node2, 'call'), 'should work for memberName') - t.ok(common.helpers.memName(node2, '.all'), 'regex should work') + t.ok(common.helpers.memName(node, 'now'), 'should work for names') + t.ok(common.helpers.memName(node2, 'call'), 'should work for memberName') + t.ok(common.helpers.memName(node2, '.all'), 'regex should work') }) test('staticAnalysisCommon.helpers.operator', function (t) { - t.plan(4) - const node = { operator: '++' } - const node2 = { operator: '+++' } + t.plan(4) + const node = { operator: '++' } + const node2 = { operator: '+++' } - const escapedPP = escapeRegExp('++') - const escapedPPExact = `^${escapedPP}$` + const escapedPP = escapeRegExp('++') + const escapedPPExact = `^${escapedPP}$` - t.ok(common.helpers.operator(node, escapedPPExact), 'should work for ++') - t.notOk(common.helpers.operator(node2, escapedPPExact), 'should not work for +++') - t.ok(common.helpers.operator(node, escapedPP), 'should work for ++') - t.ok(common.helpers.operator(node2, escapedPP), 'should work for +++') + t.ok(common.helpers.operator(node, escapedPPExact), 'should work for ++') + t.notOk(common.helpers.operator(node2, escapedPPExact), 'should not work for +++') + t.ok(common.helpers.operator(node, escapedPP), 'should work for ++') + t.ok(common.helpers.operator(node2, escapedPP), 'should work for +++') }) test('staticAnalysisCommon.helpers.nodeType', function (t) { - t.plan(3) - const node = { nodeType: 'Identifier', name: 'now'} - const node2 = { nodeType: 'FunctionCall', memberName: 'call' } + t.plan(3) + const node = { nodeType: 'Identifier', name: 'now'} + const node2 = { nodeType: 'FunctionCall', memberName: 'call' } - t.ok(common.helpers.nodeType(node, common.nodeTypes.IDENTIFIER), 'should work for identifier') - t.ok(common.helpers.nodeType(node2, common.nodeTypes.FUNCTIONCALL), 'should work for function call') - t.ok(common.helpers.nodeType(node2, '^F'), 'regex should work for function call') + t.ok(common.helpers.nodeType(node, common.nodeTypes.IDENTIFIER), 'should work for identifier') + t.ok(common.helpers.nodeType(node2, common.nodeTypes.FUNCTIONCALL), 'should work for function call') + t.ok(common.helpers.nodeType(node2, '^F'), 'regex should work for function call') }) test('staticAnalysisCommon.helpers.expressionTypeDescription', function (t) { - t.plan(3) - const node = { - "expression": + t.plan(3) + const node = { + "expression": { - "argumentTypes": + "argumentTypes": [ - { - "typeIdentifier": "t_stringliteral_c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470", - "typeString": "literal_string \"\"" - } + { + "typeIdentifier": "t_stringliteral_c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470", + "typeString": "literal_string \"\"" + } ], - "expression": + "expression": { - "name": "addr", - "nodeType": "Identifier", - "src": "132:4:0", - "typeDescriptions": + "name": "addr", + "nodeType": "Identifier", + "src": "132:4:0", + "typeDescriptions": { - "typeIdentifier": "t_address_payable", - "typeString": "address payable" + "typeIdentifier": "t_address_payable", + "typeString": "address payable" } }, - "memberName": "call", - "nodeType": "MemberAccess", - "typeDescriptions": + "memberName": "call", + "nodeType": "MemberAccess", + "typeDescriptions": { - "typeIdentifier": "t_function_barecall_payable$_t_bytes_memory_ptr_$returns$_t_bool_$_t_bytes_memory_ptr_$", - "typeString": "function (bytes memory) payable returns (bool,bytes memory)" + "typeIdentifier": "t_function_barecall_payable$_t_bytes_memory_ptr_$returns$_t_bool_$_t_bytes_memory_ptr_$", + "typeString": "function (bytes memory) payable returns (bool,bytes memory)" } }, - "nodeType": "FunctionCall", - } + "nodeType": "FunctionCall", + } - t.ok(common.helpers.expressionTypeDescription(node.expression, common.basicTypes.PAYABLE_ADDRESS), 'should work for ident') - t.ok(common.helpers.expressionTypeDescription(node, escapeRegExp(common.basicFunctionTypes.CALL)), 'should work for funcall') - t.ok(common.helpers.expressionTypeDescription(node, '^function \\('), 'regex should work') + t.ok(common.helpers.expressionTypeDescription(node.expression, common.basicTypes.PAYABLE_ADDRESS), 'should work for ident') + t.ok(common.helpers.expressionTypeDescription(node, escapeRegExp(common.basicFunctionTypes.CALL)), 'should work for funcall') + t.ok(common.helpers.expressionTypeDescription(node, '^function \\('), 'regex should work') }) // #################### Trivial Getter Test test('staticAnalysisCommon.getType', function (t) { - t.plan(3) - const node = { "argumentTypes": null, - "id": 3, - "name": "a", - "nodeType": "Identifier", - "overloadedDeclarations": [], - "referencedDeclaration": 22, - "src": "52:1:0", - "typeDescriptions": + t.plan(3) + const node = { "argumentTypes": null, + "id": 3, + "name": "a", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": 22, + "src": "52:1:0", + "typeDescriptions": { - "typeIdentifier": "t_uint256", - "typeString": "uint256" + "typeIdentifier": "t_uint256", + "typeString": "uint256" } - } - t.ok(common.getType(blockHashAccess) === 'bytes32', 'gettype should work for different nodes') - t.ok(common.getType(node) === 'uint256', 'gettype should work for different nodes') - t.ok(common.getType(assignment) === 'uint256', 'gettype should work for different nodes') + } + t.ok(common.getType(blockHashAccess) === 'bytes32', 'gettype should work for different nodes') + t.ok(common.getType(node) === 'uint256', 'gettype should work for different nodes') + t.ok(common.getType(assignment) === 'uint256', 'gettype should work for different nodes') }) // #################### Complex Getter Test test('staticAnalysisCommon.getFunctionCallType', function (t) { - t.plan(4) - t.equal(common.getFunctionCallType(libCall), 'function (struct Set.Data storage pointer,uint256) returns (bool)', 'this lib call returns correct type') - t.equal(common.getFunctionCallType(thisLocalCall), 'function () external returns (uint256,uint256)', 'this local call returns correct type') - t.equal(common.getFunctionCallType(localCall), 'function (uint256,string memory)', 'local call returns correct type') - t.equal(common.getFunctionCallType(externalDirect), 'function () external', 'external call returns correct type') + t.plan(4) + t.equal(common.getFunctionCallType(libCall), 'function (struct Set.Data storage pointer,uint256) returns (bool)', 'this lib call returns correct type') + t.equal(common.getFunctionCallType(thisLocalCall), 'function () external returns (uint256,uint256)', 'this local call returns correct type') + t.equal(common.getFunctionCallType(localCall), 'function (uint256,string memory)', 'local call returns correct type') + t.equal(common.getFunctionCallType(externalDirect), 'function () external', 'external call returns correct type') }) test('staticAnalysisCommon.getEffectedVariableName', function (t) { - t.plan(3) - t.ok(common.getEffectedVariableName(assignment) === 'a', 'get right name for assignment') - t.throws(() => common.getEffectedVariableName(inlineAssembly), new RegExp('staticAnalysisCommon.js: wrong node type'), 'staticAnalysisCommon.js: not an effect Node or inline assembly, get from inline assembly should throw') - t.throws(() => common.getEffectedVariableName(externalDirect), new RegExp('staticAnalysisCommon.js: not an effect Node'), 'should throw on all other nodes') + t.plan(3) + t.ok(common.getEffectedVariableName(assignment) === 'a', 'get right name for assignment') + t.throws(() => common.getEffectedVariableName(inlineAssembly), new RegExp('staticAnalysisCommon.js: wrong node type'), 'staticAnalysisCommon.js: not an effect Node or inline assembly, get from inline assembly should throw') + t.throws(() => common.getEffectedVariableName(externalDirect), new RegExp('staticAnalysisCommon.js: not an effect Node'), 'should throw on all other nodes') }) test('staticAnalysisCommon.getLocalCallName', function (t) { - t.plan(3) - t.ok(common.getLocalCallName(localCall) === 'e', 'getLocal call name from node') - t.throws(() => common.getLocalCallName(externalDirect), new RegExp('staticAnalysisCommon.js: not a local call Node'), 'throws for externalDirect nodes') - t.throws(() => common.getLocalCallName(thisLocalCall), new RegExp('staticAnalysisCommon.js: not a local call Node'), 'throws for this local call nodes') + t.plan(3) + t.ok(common.getLocalCallName(localCall) === 'e', 'getLocal call name from node') + t.throws(() => common.getLocalCallName(externalDirect), new RegExp('staticAnalysisCommon.js: not a local call Node'), 'throws for externalDirect nodes') + t.throws(() => common.getLocalCallName(thisLocalCall), new RegExp('staticAnalysisCommon.js: not a local call Node'), 'throws for this local call nodes') }) test('staticAnalysisCommon.getThisLocalCallName', function (t) { - t.plan(3) - t.ok(common.getThisLocalCallName(thisLocalCall) === 'f', 'get this Local call name from node') - t.throws(() => common.getThisLocalCallName(externalDirect), new RegExp('staticAnalysisCommon.js: not a this local call Node'), 'throws on externalDirect nodes') - t.throws(() => common.getThisLocalCallName(localCall), new RegExp('staticAnalysisCommon.js: not a this local call Node'), 'throws on localCall nodes') + t.plan(3) + t.ok(common.getThisLocalCallName(thisLocalCall) === 'f', 'get this Local call name from node') + t.throws(() => common.getThisLocalCallName(externalDirect), new RegExp('staticAnalysisCommon.js: not a this local call Node'), 'throws on externalDirect nodes') + t.throws(() => common.getThisLocalCallName(localCall), new RegExp('staticAnalysisCommon.js: not a this local call Node'), 'throws on localCall nodes') }) test('staticAnalysisCommon.getSuperLocalCallName', function (t) { - t.plan(4) - t.equal(common.getSuperLocalCallName(superLocal), 'x', 'get local name from super local call') - t.throws(() => common.getSuperLocalCallName(thisLocalCall), new RegExp('staticAnalysisCommon.js: not a super local call Node'), 'throws on other nodes') - t.throws(() => common.getSuperLocalCallName(externalDirect), new RegExp('staticAnalysisCommon.js: not a super local call Node'),' throws on other nodes') - t.throws(() => common.getSuperLocalCallName(localCall), new RegExp('staticAnalysisCommon.js: not a super local call Node'), 'throws on other nodes') + t.plan(4) + t.equal(common.getSuperLocalCallName(superLocal), 'x', 'get local name from super local call') + t.throws(() => common.getSuperLocalCallName(thisLocalCall), new RegExp('staticAnalysisCommon.js: not a super local call Node'), 'throws on other nodes') + t.throws(() => common.getSuperLocalCallName(externalDirect), new RegExp('staticAnalysisCommon.js: not a super local call Node'),' throws on other nodes') + t.throws(() => common.getSuperLocalCallName(localCall), new RegExp('staticAnalysisCommon.js: not a super local call Node'), 'throws on other nodes') }) test('staticAnalysisCommon.getExternalDirectCallContractName', function (t) { - t.plan(3) - t.ok(common.getExternalDirectCallContractName(externalDirect) === 'c', 'external direct call contract name from node') - t.throws(() => common.getExternalDirectCallContractName(thisLocalCall), new RegExp('staticAnalysisCommon.js: not an external direct call Node'), 'throws on other nodes') - t.throws(() => common.getExternalDirectCallContractName(localCall), new RegExp('staticAnalysisCommon.js: not an external direct call Node'), 'throws on other nodes') + t.plan(3) + t.ok(common.getExternalDirectCallContractName(externalDirect) === 'c', 'external direct call contract name from node') + t.throws(() => common.getExternalDirectCallContractName(thisLocalCall), new RegExp('staticAnalysisCommon.js: not an external direct call Node'), 'throws on other nodes') + t.throws(() => common.getExternalDirectCallContractName(localCall), new RegExp('staticAnalysisCommon.js: not an external direct call Node'), 'throws on other nodes') }) test('staticAnalysisCommon.getThisLocalCallContractName', function (t) { - t.plan(3) - t.ok(common.getThisLocalCallContractName(thisLocalCall) === 'C', 'this local call contract name from node') - t.throws(() => common.getThisLocalCallContractName(localCall), new RegExp('staticAnalysisCommon.js: not a this local call Node'), 'throws on other nodes') - t.throws(() => common.getThisLocalCallContractName(externalDirect), new RegExp('staticAnalysisCommon.js: not a this local call Node'), 'throws on other nodes') + t.plan(3) + t.ok(common.getThisLocalCallContractName(thisLocalCall) === 'C', 'this local call contract name from node') + t.throws(() => common.getThisLocalCallContractName(localCall), new RegExp('staticAnalysisCommon.js: not a this local call Node'), 'throws on other nodes') + t.throws(() => common.getThisLocalCallContractName(externalDirect), new RegExp('staticAnalysisCommon.js: not a this local call Node'), 'throws on other nodes') }) test('staticAnalysisCommon.getExternalDirectCallMemberName', function (t) { - t.plan(3) - t.ok(common.getExternalDirectCallMemberName(externalDirect) === 'f', 'external direct call name from node') - t.throws(() => common.getExternalDirectCallMemberName(thisLocalCall), new RegExp('staticAnalysisCommon.js: not an external direct call Node'), 'throws on other nodes') - t.throws(() => common.getExternalDirectCallMemberName(localCall), new RegExp('staticAnalysisCommon.js: not an external direct call Node'), 'throws on other nodes') + t.plan(3) + t.ok(common.getExternalDirectCallMemberName(externalDirect) === 'f', 'external direct call name from node') + t.throws(() => common.getExternalDirectCallMemberName(thisLocalCall), new RegExp('staticAnalysisCommon.js: not an external direct call Node'), 'throws on other nodes') + t.throws(() => common.getExternalDirectCallMemberName(localCall), new RegExp('staticAnalysisCommon.js: not an external direct call Node'), 'throws on other nodes') }) test('staticAnalysisCommon.getContractName', function (t) { - t.plan(2) - t.ok(common.getContractName(contractDefinition) === 'C', 'returns right contract name') - t.throws(() => common.getContractName(inheritance), new RegExp('staticAnalysisCommon.js: not a ContractDefinition Node'), 'throws on other nodes') + t.plan(2) + t.ok(common.getContractName(contractDefinition) === 'C', 'returns right contract name') + t.throws(() => common.getContractName(inheritance), new RegExp('staticAnalysisCommon.js: not a ContractDefinition Node'), 'throws on other nodes') }) test('staticAnalysisCommon.getFunctionDefinitionName', function (t) { - t.plan(2) - t.ok(common.getFunctionDefinitionName(functionDefinition) === 'f', 'returns right function name') - t.throws(() => common.getFunctionDefinitionName(inheritance), new RegExp('staticAnalysisCommon.js: not a FunctionDefinition Node'), 'throws on other nodes') + t.plan(2) + t.ok(common.getFunctionDefinitionName(functionDefinition) === 'f', 'returns right function name') + t.throws(() => common.getFunctionDefinitionName(inheritance), new RegExp('staticAnalysisCommon.js: not a FunctionDefinition Node'), 'throws on other nodes') }) test('staticAnalysisCommon.getInheritsFromName', function (t) { - t.plan(2) - t.ok(common.getInheritsFromName(inheritance) === 'A', 'returns right contract name') - t.throws(() => common.getInheritsFromName(functionDefinition), new RegExp('staticAnalysisCommon.js: not an InheritanceSpecifier Node'), 'throws on other nodes') + t.plan(2) + t.ok(common.getInheritsFromName(inheritance) === 'A', 'returns right contract name') + t.throws(() => common.getInheritsFromName(functionDefinition), new RegExp('staticAnalysisCommon.js: not an InheritanceSpecifier Node'), 'throws on other nodes') }) test('staticAnalysisCommon.getDeclaredVariableName', function (t) { - t.plan(2) - t.ok(common.getDeclaredVariableName(storageVariableNodes.node1) === 'c', 'extract right variable name') - const node1 = JSON.parse(JSON.stringify(storageVariableNodes)) - node1.node1.nodeType = 'FunctionCall' - t.throws(() => common.getDeclaredVariableName(node1) === 'x', new RegExp('staticAnalysisCommon.js: not a VariableDeclaration Node'), 'throw if wrong node') + t.plan(2) + t.ok(common.getDeclaredVariableName(storageVariableNodes.node1) === 'c', 'extract right variable name') + const node1 = JSON.parse(JSON.stringify(storageVariableNodes)) + node1.node1.nodeType = 'FunctionCall' + t.throws(() => common.getDeclaredVariableName(node1) === 'x', new RegExp('staticAnalysisCommon.js: not a VariableDeclaration Node'), 'throw if wrong node') }) test('staticAnalysisCommon.getStateVariableDeclarationsFromContractNode', function (t) { - t.plan(3) - const res = common.getStateVariableDeclarationsFromContractNode(stateVariableContractNode).map(common.getDeclaredVariableName) - t.ok(res[0] === 'x', 'var 1 should be x') - t.ok(res[1] === 'b', 'var 2 should be b') - t.ok(res[2] === 's', 'var 3 should be s') + t.plan(3) + const res = common.getStateVariableDeclarationsFromContractNode(stateVariableContractNode).map(common.getDeclaredVariableName) + t.ok(res[0] === 'x', 'var 1 should be x') + t.ok(res[1] === 'b', 'var 2 should be b') + t.ok(res[2] === 's', 'var 3 should be s') }) test('staticAnalysisCommon.getFunctionOrModifierDefinitionParameterPart', function (t) { - t.plan(2) - t.ok(common.helpers.nodeType(common.getFunctionOrModifierDefinitionParameterPart(functionDefinition), 'ParameterList'), 'should return a parameterList') - t.throws(() => common.getFunctionOrModifierDefinitionParameterPart(contractDefinition), new RegExp('staticAnalysisCommon.js: not a FunctionDefinition or ModifierDefinition Node'), 'throws on other nodes') + t.plan(2) + t.ok(common.helpers.nodeType(common.getFunctionOrModifierDefinitionParameterPart(functionDefinition), 'ParameterList'), 'should return a parameterList') + t.throws(() => common.getFunctionOrModifierDefinitionParameterPart(contractDefinition), new RegExp('staticAnalysisCommon.js: not a FunctionDefinition or ModifierDefinition Node'), 'throws on other nodes') }) test('staticAnalysisCommon.getFunctionCallTypeParameterType', function (t) { - t.plan(4) - t.ok(common.getFunctionCallTypeParameterType(thisLocalCall) === '', 'this local call returns correct type') - t.ok(common.getFunctionCallTypeParameterType(externalDirect) === '', 'external direct call returns correct type') - t.ok(common.getFunctionCallTypeParameterType(localCall) === 'uint256,string memory', 'local call returns correct type') - t.throws(() => common.getFunctionCallTypeParameterType(thisLocalCall.expression), new RegExp('staticAnalysisCommon.js: cannot extract parameter types from function call'), 'throws on wrong type') + t.plan(4) + t.ok(common.getFunctionCallTypeParameterType(thisLocalCall) === '', 'this local call returns correct type') + t.ok(common.getFunctionCallTypeParameterType(externalDirect) === '', 'external direct call returns correct type') + t.ok(common.getFunctionCallTypeParameterType(localCall) === 'uint256,string memory', 'local call returns correct type') + t.throws(() => common.getFunctionCallTypeParameterType(thisLocalCall.expression), new RegExp('staticAnalysisCommon.js: cannot extract parameter types from function call'), 'throws on wrong type') }) test('staticAnalysisCommon.getLibraryCallContractName', function (t) { - t.plan(2) - t.equal(common.getLibraryCallContractName(libCall), 'Set', 'should return correct contract name') - t.throws(() => common.getLibraryCallContractName(contractDefinition), new RegExp('staticAnalysisCommon.js: not a library call Node'), 'should throw on wrong node') + t.plan(2) + t.equal(common.getLibraryCallContractName(libCall), 'Set', 'should return correct contract name') + t.throws(() => common.getLibraryCallContractName(contractDefinition), new RegExp('staticAnalysisCommon.js: not a library call Node'), 'should throw on wrong node') }) test('staticAnalysisCommon.getLibraryCallMemberName', function (t) { - t.plan(2) - t.equal(common.getLibraryCallMemberName(libCall), 'insert', 'should return correct member name') - t.throws(() => common.getLibraryCallMemberName(thisLocalCall), new RegExp('staticAnalysisCommon.js: not a library call Node'), 'should throw on wrong node') + t.plan(2) + t.equal(common.getLibraryCallMemberName(libCall), 'insert', 'should return correct member name') + t.throws(() => common.getLibraryCallMemberName(thisLocalCall), new RegExp('staticAnalysisCommon.js: not a library call Node'), 'should throw on wrong node') }) test('staticAnalysisCommon.getFullQualifiedFunctionCallIdent', function (t) { - t.plan(4) - t.ok(common.getFullQualifiedFunctionCallIdent(contractDefinition, thisLocalCall) === 'C.f()', 'this local call returns correct type') - t.ok(common.getFullQualifiedFunctionCallIdent(contractDefinition, externalDirect) === 'c.f()', 'external direct call returns correct type') - t.ok(common.getFullQualifiedFunctionCallIdent(contractDefinition, localCall) === 'C.e(uint256,string memory)', 'local call returns correct type') - t.throws(() => common.getFullQualifiedFunctionCallIdent(contractDefinition, assignment), new RegExp('staticAnalysisCommon.js: Can not get function name from non function call node'), 'throws on wrong type') + t.plan(4) + t.ok(common.getFullQualifiedFunctionCallIdent(contractDefinition, thisLocalCall) === 'C.f()', 'this local call returns correct type') + t.ok(common.getFullQualifiedFunctionCallIdent(contractDefinition, externalDirect) === 'c.f()', 'external direct call returns correct type') + t.ok(common.getFullQualifiedFunctionCallIdent(contractDefinition, localCall) === 'C.e(uint256,string memory)', 'local call returns correct type') + t.throws(() => common.getFullQualifiedFunctionCallIdent(contractDefinition, assignment), new RegExp('staticAnalysisCommon.js: Can not get function name from non function call node'), 'throws on wrong type') }) test('staticAnalysisCommon.getFullQuallyfiedFuncDefinitionIdent', function (t) { - t.plan(3) - t.ok(common.getFullQuallyfiedFuncDefinitionIdent(contractDefinition, functionDefinition, ['uint256', 'bool']) === 'C.f(uint256,bool)', 'creates right signature') - t.throws(() => common.getFullQuallyfiedFuncDefinitionIdent(contractDefinition, parameterFunctionCall, ['uint256', 'bool']), new RegExp('staticAnalysisCommon.js: not a FunctionDefinition Node'), 'throws on wrong nodes') - t.throws(() => common.getFullQuallyfiedFuncDefinitionIdent(parameterFunctionCall, functionDefinition, ['uint256', 'bool']), new RegExp('staticAnalysisCommon.js: not a ContractDefinition Node'), 'throws on wrong nodes') + t.plan(3) + t.ok(common.getFullQuallyfiedFuncDefinitionIdent(contractDefinition, functionDefinition, ['uint256', 'bool']) === 'C.f(uint256,bool)', 'creates right signature') + t.throws(() => common.getFullQuallyfiedFuncDefinitionIdent(contractDefinition, parameterFunctionCall, ['uint256', 'bool']), new RegExp('staticAnalysisCommon.js: not a FunctionDefinition Node'), 'throws on wrong nodes') + t.throws(() => common.getFullQuallyfiedFuncDefinitionIdent(parameterFunctionCall, functionDefinition, ['uint256', 'bool']), new RegExp('staticAnalysisCommon.js: not a ContractDefinition Node'), 'throws on wrong nodes') }) test('staticAnalysisCommon.getSplittedTypeDesc', function (t) { - t.plan(3) - t.ok(common.getMethodParamsSplittedTypeDesc(funcDefForComplexParams.withoutParams, compiledContractObj).length === 0, 'no params, no params type signature') - t.ok(common.getMethodParamsSplittedTypeDesc(funcDefForComplexParams.bytesArray, compiledContractObj)[0] === 'bytes32[]', 'creates right params type signature') - t.ok(common.getMethodParamsSplittedTypeDesc(funcDefForComplexParams.nestedStruct, compiledContractObj)[0] === '(bytes32,uint256,uint256[],address,(bytes32,uint256)[])[][]', 'creates right params type signature') + t.plan(3) + t.ok(common.getMethodParamsSplittedTypeDesc(funcDefForComplexParams.withoutParams, compiledContractObj).length === 0, 'no params, no params type signature') + t.ok(common.getMethodParamsSplittedTypeDesc(funcDefForComplexParams.bytesArray, compiledContractObj)[0] === 'bytes32[]', 'creates right params type signature') + t.ok(common.getMethodParamsSplittedTypeDesc(funcDefForComplexParams.nestedStruct, compiledContractObj)[0] === '(bytes32,uint256,uint256[],address,(bytes32,uint256)[])[][]', 'creates right params type signature') }) // #################### Complex Node Identification test('staticAnalysisCommon.isBuiltinFunctionCall', function (t) { - t.plan(1) - t.ok(common.isBuiltinFunctionCall(selfdestruct), 'selfdestruct is builtin') + t.plan(1) + t.ok(common.isBuiltinFunctionCall(selfdestruct), 'selfdestruct is builtin') }) test('staticAnalysisCommon.isStorageVariableDeclaration', function (t) { - t.plan(3) - t.ok(common.isStorageVariableDeclaration(storageVariableNodes.node1), 'struct storage pointer param is storage') - t.ok(common.isStorageVariableDeclaration(storageVariableNodes.node2), 'struct storage pointer mapping param is storage') - t.notOk(common.isStorageVariableDeclaration(storageVariableNodes.node3), 'bytes is not storage') + t.plan(3) + t.ok(common.isStorageVariableDeclaration(storageVariableNodes.node1), 'struct storage pointer param is storage') + t.ok(common.isStorageVariableDeclaration(storageVariableNodes.node2), 'struct storage pointer mapping param is storage') + t.notOk(common.isStorageVariableDeclaration(storageVariableNodes.node3), 'bytes is not storage') }) test('staticAnalysisCommon.isInteraction', function (t) { - t.plan(5) - t.ok(common.isInteraction(lowlevelCall.sendAst), 'send is interaction') - t.ok(common.isInteraction(lowlevelCall.callAst), 'call is interaction') - t.ok(common.isInteraction(externalDirect), 'ExternalDirectCall is interaction') - t.notOk(common.isInteraction(lowlevelCall.delegatecallAst), 'delegatecall is not interaction') - t.notOk(common.isInteraction(localCall), 'local call is not interaction') + t.plan(5) + t.ok(common.isInteraction(lowlevelCall.sendAst), 'send is interaction') + t.ok(common.isInteraction(lowlevelCall.callAst), 'call is interaction') + t.ok(common.isInteraction(externalDirect), 'ExternalDirectCall is interaction') + t.notOk(common.isInteraction(lowlevelCall.delegatecallAst), 'delegatecall is not interaction') + t.notOk(common.isInteraction(localCall), 'local call is not interaction') }) test('staticAnalysisCommon.isEffect', function (t) { - t.plan(5) - t.ok(common.isEffect(inlineAssembly), 'inline assembly is treated as effect') - t.ok(common.isEffect(assignment), 'assignment is treated as effect') - t.ok(common.isEffect(unaryOperation), '++ is treated as effect') - const node = JSON.parse(JSON.stringify(unaryOperation)) - node.operator = '--' - t.ok(common.isEffect(node), '-- is treated as effect') - t.notOk(common.isEffect(externalDirect.expression), 'MemberAccess not treated as effect') + t.plan(5) + t.ok(common.isEffect(inlineAssembly), 'inline assembly is treated as effect') + t.ok(common.isEffect(assignment), 'assignment is treated as effect') + t.ok(common.isEffect(unaryOperation), '++ is treated as effect') + const node = JSON.parse(JSON.stringify(unaryOperation)) + node.operator = '--' + t.ok(common.isEffect(node), '-- is treated as effect') + t.notOk(common.isEffect(externalDirect.expression), 'MemberAccess not treated as effect') }) test('staticAnalysisCommon.isWriteOnStateVariable', function (t) { - t.plan(3) - const node1 = JSON.parse(JSON.stringify(storageVariableNodes.node1)) - const node2 = node1 - const node3 = node1 - node2.name = 'y' - node3.name = 'xx' - t.ok(common.isWriteOnStateVariable(inlineAssembly, [node1, node2, node3]), 'inline Assembly is write on state') - t.notOk(common.isWriteOnStateVariable(assignment, [node1, node2, node3]), 'assignment on non state is not write on state') - node3.name = 'a' // same as assignment left hand side var name - t.ok(common.isWriteOnStateVariable(assignment, [node1, node2, node3]), 'assignment on state is write on state') + t.plan(3) + const node1 = JSON.parse(JSON.stringify(storageVariableNodes.node1)) + const node2 = node1 + const node3 = node1 + node2.name = 'y' + node3.name = 'xx' + t.ok(common.isWriteOnStateVariable(inlineAssembly, [node1, node2, node3]), 'inline Assembly is write on state') + t.notOk(common.isWriteOnStateVariable(assignment, [node1, node2, node3]), 'assignment on non state is not write on state') + node3.name = 'a' // same as assignment left hand side var name + t.ok(common.isWriteOnStateVariable(assignment, [node1, node2, node3]), 'assignment on state is write on state') }) test('staticAnalysisCommon.isStateVariable', function (t) { - t.plan(3) - t.ok(common.isStateVariable('c', [storageVariableNodes.node1, storageVariableNodes.node2]), 'is contained') - t.ok(common.isStateVariable('c', [storageVariableNodes.node2, storageVariableNodes.node1, storageVariableNodes.node1]), 'is contained twice') - t.notOk(common.isStateVariable('c', [storageVariableNodes.node2, storageVariableNodes.node3]), 'not contained') + t.plan(3) + t.ok(common.isStateVariable('c', [storageVariableNodes.node1, storageVariableNodes.node2]), 'is contained') + t.ok(common.isStateVariable('c', [storageVariableNodes.node2, storageVariableNodes.node1, storageVariableNodes.node1]), 'is contained twice') + t.notOk(common.isStateVariable('c', [storageVariableNodes.node2, storageVariableNodes.node3]), 'not contained') }) test('staticAnalysisCommon.isConstantFunction', function (t) { - t.plan(3) - t.ok(common.isConstantFunction(functionDefinition), 'should be const func definition') - functionDefinition.stateMutability = 'view' - t.ok(common.isConstantFunction(functionDefinition), 'should be const func definition') - functionDefinition.stateMutability = 'nonpayable' - t.notOk(common.isConstantFunction(functionDefinition), 'should not be const func definition') + t.plan(3) + t.ok(common.isConstantFunction(functionDefinition), 'should be const func definition') + functionDefinition.stateMutability = 'view' + t.ok(common.isConstantFunction(functionDefinition), 'should be const func definition') + functionDefinition.stateMutability = 'nonpayable' + t.notOk(common.isConstantFunction(functionDefinition), 'should not be const func definition') }) test('staticAnalysisCommon.isPlusPlusUnaryOperation', function (t) { - t.plan(2) - t.ok(common.isPlusPlusUnaryOperation(unaryOperation), 'should be unary ++') - const node = JSON.parse(JSON.stringify(unaryOperation)) - node.operator = '--' - t.notOk(common.isPlusPlusUnaryOperation(node), 'should not be unary ++') + t.plan(2) + t.ok(common.isPlusPlusUnaryOperation(unaryOperation), 'should be unary ++') + const node = JSON.parse(JSON.stringify(unaryOperation)) + node.operator = '--' + t.notOk(common.isPlusPlusUnaryOperation(node), 'should not be unary ++') }) test('staticAnalysisCommon.isMinusMinusUnaryOperation', function (t) { - t.plan(2) - unaryOperation.operator = '--' - t.ok(common.isMinusMinusUnaryOperation(unaryOperation), 'should be unary --') - unaryOperation.operator = '++' - t.notOk(common.isMinusMinusUnaryOperation(unaryOperation), 'should not be unary --') + t.plan(2) + unaryOperation.operator = '--' + t.ok(common.isMinusMinusUnaryOperation(unaryOperation), 'should be unary --') + unaryOperation.operator = '++' + t.notOk(common.isMinusMinusUnaryOperation(unaryOperation), 'should not be unary --') }) test('staticAnalysisCommon.isFullyImplementedContract', function (t) { - t.plan(2) - t.ok(common.isFullyImplementedContract(contractDefinition), 'should be fully implemented contract') - const node = JSON.parse(JSON.stringify(contractDefinition)) - node.fullyImplemented = false - t.notOk(common.isFullyImplementedContract(node), 'should not be fully implemented contract') + t.plan(2) + t.ok(common.isFullyImplementedContract(contractDefinition), 'should be fully implemented contract') + const node = JSON.parse(JSON.stringify(contractDefinition)) + node.fullyImplemented = false + t.notOk(common.isFullyImplementedContract(node), 'should not be fully implemented contract') }) test('staticAnalysisCommon.isCallToNonConstLocalFunction', function (t) { - t.plan(2) - t.ok(common.isCallToNonConstLocalFunction(localCall), 'should be call to non const Local func') - const node = JSON.parse(JSON.stringify(localCall)) - node.expression.typeDescriptions.typeString = 'function (struct Ballot.Voter storage pointer) view payable (uint256)' - t.notok(common.isCallToNonConstLocalFunction(node), 'should no longer be call to non const Local func') + t.plan(2) + t.ok(common.isCallToNonConstLocalFunction(localCall), 'should be call to non const Local func') + const node = JSON.parse(JSON.stringify(localCall)) + node.expression.typeDescriptions.typeString = 'function (struct Ballot.Voter storage pointer) view payable (uint256)' + t.notok(common.isCallToNonConstLocalFunction(node), 'should no longer be call to non const Local func') }) test('staticAnalysisCommon.isExternalDirectCall', function (t) { - t.plan(5) - t.notOk(common.isThisLocalCall(externalDirect), 'is this.local_method() used should not work') - t.notOk(common.isBlockTimestampAccess(externalDirect), 'is block.timestamp used should not work') - t.notOk(common.isNowAccess(externalDirect), 'is now used should not work') - t.ok(common.isExternalDirectCall(externalDirect), 'c.f() should be external direct call') - t.notOk(common.isExternalDirectCall(thisLocalCall.expression), 'this local call is not an external call') + t.plan(5) + t.notOk(common.isThisLocalCall(externalDirect), 'is this.local_method() used should not work') + t.notOk(common.isBlockTimestampAccess(externalDirect), 'is block.timestamp used should not work') + t.notOk(common.isNowAccess(externalDirect), 'is now used should not work') + t.ok(common.isExternalDirectCall(externalDirect), 'c.f() should be external direct call') + t.notOk(common.isExternalDirectCall(thisLocalCall.expression), 'this local call is not an external call') }) test('staticAnalysisCommon.isNowAccess', function (t) { - t.plan(1) - t.ok(common.isNowAccess(nowAst), 'is now used should work') + t.plan(1) + t.ok(common.isNowAccess(nowAst), 'is now used should work') }) test('staticAnalysisCommon.isBlockTimestampAccess', function (t) { - t.plan(3) - t.notOk(common.isThisLocalCall(blockTimestamp), 'is this.local_method() used should not work') - t.ok(common.isBlockTimestampAccess(blockTimestamp), 'is block.timestamp used should work') - t.notOk(common.isNowAccess(blockTimestamp), 'is now used should not work') + t.plan(3) + t.notOk(common.isThisLocalCall(blockTimestamp), 'is this.local_method() used should not work') + t.ok(common.isBlockTimestampAccess(blockTimestamp), 'is block.timestamp used should work') + t.notOk(common.isNowAccess(blockTimestamp), 'is now used should not work') }) test('staticAnalysisCommon.isBlockBlockhashAccess', function (t) { - t.plan(2) - t.ok(common.isBlockBlockHashAccess(blockHashAccess), 'blockhash should work') - t.notOk(common.isNowAccess(blockHashAccess.expression), 'is now used should not work') + t.plan(2) + t.ok(common.isBlockBlockHashAccess(blockHashAccess), 'blockhash should work') + t.notOk(common.isNowAccess(blockHashAccess.expression), 'is now used should not work') }) test('staticAnalysisCommon.isThisLocalCall', function (t) { - t.plan(2) - t.ok(common.isThisLocalCall(thisLocalCall.expression), 'is this.local_method() used should work') - t.notOk(common.isBlockTimestampAccess(thisLocalCall.expression), 'is block.timestamp used should not work') + t.plan(2) + t.ok(common.isThisLocalCall(thisLocalCall.expression), 'is this.local_method() used should work') + t.notOk(common.isBlockTimestampAccess(thisLocalCall.expression), 'is block.timestamp used should not work') }) test('staticAnalysisCommon.isSuperLocalCall', function (t) { - t.plan(3) - t.ok(common.isSuperLocalCall(superLocal.expression), 'is super.local_method() used should work') - t.notOk(common.isThisLocalCall(superLocal.expression), 'is this.local_method() used should not work') - t.notOk(common.isBlockTimestampAccess(superLocal.expression), 'is block.timestamp used should not work') + t.plan(3) + t.ok(common.isSuperLocalCall(superLocal.expression), 'is super.local_method() used should work') + t.notOk(common.isThisLocalCall(superLocal.expression), 'is this.local_method() used should not work') + t.notOk(common.isBlockTimestampAccess(superLocal.expression), 'is block.timestamp used should not work') }) test('staticAnalysisCommon.isLibraryCall', function (t) { - t.plan(4) - t.ok(common.isLibraryCall(libCall.expression), 'is lib call should not work') - t.notOk(common.isSuperLocalCall(libCall.expression), 'is super.local_method() used should not work') - t.notOk(common.isThisLocalCall(libCall.expression), 'is this.local_method() used should not work') - t.notOk(common.isBlockTimestampAccess(libCall.expression), 'is block.timestamp used should not work') + t.plan(4) + t.ok(common.isLibraryCall(libCall.expression), 'is lib call should not work') + t.notOk(common.isSuperLocalCall(libCall.expression), 'is super.local_method() used should not work') + t.notOk(common.isThisLocalCall(libCall.expression), 'is this.local_method() used should not work') + t.notOk(common.isBlockTimestampAccess(libCall.expression), 'is block.timestamp used should not work') }) test('staticAnalysisCommon.isLocalCall', function (t) { - t.plan(1) - t.ok(common.isLocalCall(localCall), 'isLocalCall') + t.plan(1) + t.ok(common.isLocalCall(localCall), 'isLocalCall') }) test('staticAnalysisCommon.isLowLevelCall', function (t) { - t.plan(3) - t.ok(common.isLLSend(lowlevelCall.sendAst.expression) && common.isLowLevelCall(lowlevelCall.sendAst.expression), 'send is llc should work') - t.ok(common.isLLCall(lowlevelCall.callAst.expression) && common.isLowLevelCall(lowlevelCall.callAst.expression), 'call is llc should work') - t.ok(common.isLLDelegatecall(lowlevelCall.delegatecallAst) && common.isLowLevelCall(lowlevelCall.delegatecallAst), 'delegatecall is llc should work') + t.plan(3) + t.ok(common.isLLSend(lowlevelCall.sendAst.expression) && common.isLowLevelCall(lowlevelCall.sendAst.expression), 'send is llc should work') + t.ok(common.isLLCall(lowlevelCall.callAst.expression) && common.isLowLevelCall(lowlevelCall.callAst.expression), 'call is llc should work') + t.ok(common.isLLDelegatecall(lowlevelCall.delegatecallAst) && common.isLowLevelCall(lowlevelCall.delegatecallAst), 'delegatecall is llc should work') }) test('staticAnalysisCommon: Call of parameter function', function (t) { - t.plan(3) - t.ok(common.isLocalCall(parameterFunction), 'is not LocalCall') - t.equals(common.getFunctionCallType(parameterFunction), 'function (uint256,uint256)', 'Extracts right type') - t.equals(common.getFunctionCallTypeParameterType(parameterFunction), 'uint256,uint256', 'Extracts param right type') + t.plan(3) + t.ok(common.isLocalCall(parameterFunction), 'is not LocalCall') + t.equals(common.getFunctionCallType(parameterFunction), 'function (uint256,uint256)', 'Extracts right type') + t.equals(common.getFunctionCallTypeParameterType(parameterFunction), 'uint256,uint256', 'Extracts param right type') }) test('staticAnalysisCommon: function call with of function with function parameter', function (t) { - t.plan(2) - t.equals(common.getFunctionCallType(parameterFunctionCall), 'function (function (uint256) pure returns (uint256)) pure returns (uint256)', 'Extracts right type') - t.equals(common.getFunctionCallTypeParameterType(parameterFunctionCall), 'function (uint256) pure returns (uint256)', 'Extracts param right type') + t.plan(2) + t.equals(common.getFunctionCallType(parameterFunctionCall), 'function (function (uint256) pure returns (uint256)) pure returns (uint256)', 'Extracts right type') + t.equals(common.getFunctionCallTypeParameterType(parameterFunctionCall), 'function (uint256) pure returns (uint256)', 'Extracts param right type') }) test('staticAnalysisCommon: require call', function (t) { - t.plan(3) - t.equals(common.isRequireCall(requireCall), true) - t.equals(common.getFunctionCallType(requireCall), 'function (bool) pure', 'Extracts right type') - t.equals(common.getFunctionCallTypeParameterType(requireCall), 'bool', 'Extracts param right type') + t.plan(3) + t.equals(common.isRequireCall(requireCall), true) + t.equals(common.getFunctionCallType(requireCall), 'function (bool) pure', 'Extracts right type') + t.equals(common.getFunctionCallTypeParameterType(requireCall), 'bool', 'Extracts param right type') }) test('staticAnalysisCommon: isDeleteOfDynamicArray', function (t) { - t.plan(2) - t.ok(common.isDeleteOfDynamicArray(dynamicDeleteUnaryOp), 'is dynamic array deletion') - t.ok(common.isDynamicArrayAccess(dynamicDeleteUnaryOp.subExpression), 'Extracts right type') + t.plan(2) + t.ok(common.isDeleteOfDynamicArray(dynamicDeleteUnaryOp), 'is dynamic array deletion') + t.ok(common.isDynamicArrayAccess(dynamicDeleteUnaryOp.subExpression), 'Extracts right type') }) test('staticAnalysisCommon: isAbiNamespaceCall', function (t) { - t.plan(8) - t.equals(common.isAbiNamespaceCall(abiNamespaceCallNodes.encode), true, 'encode abi') - t.equals(common.isAbiNamespaceCall(abiNamespaceCallNodes.encodePacked), true, 'encodePacked abi') - t.equals(common.isAbiNamespaceCall(abiNamespaceCallNodes.encodeWithSelector), true, 'encodeWithSelector abi') - t.equals(common.isAbiNamespaceCall(abiNamespaceCallNodes.encodeWithSignature), true, 'encodeWithSignature abi') - - t.equals(common.isBuiltinFunctionCall(abiNamespaceCallNodes.encode), true, 'encode Builtin') - t.equals(common.isBuiltinFunctionCall(abiNamespaceCallNodes.encodePacked), true, 'encodePacked Builtin') - t.equals(common.isBuiltinFunctionCall(abiNamespaceCallNodes.encodeWithSelector), true, 'encodeWithSelector Builtin') - t.equals(common.isBuiltinFunctionCall(abiNamespaceCallNodes.encodeWithSignature), true, 'encodeWithSignature Builtin') + t.plan(8) + t.equals(common.isAbiNamespaceCall(abiNamespaceCallNodes.encode), true, 'encode abi') + t.equals(common.isAbiNamespaceCall(abiNamespaceCallNodes.encodePacked), true, 'encodePacked abi') + t.equals(common.isAbiNamespaceCall(abiNamespaceCallNodes.encodeWithSelector), true, 'encodeWithSelector abi') + t.equals(common.isAbiNamespaceCall(abiNamespaceCallNodes.encodeWithSignature), true, 'encodeWithSignature abi') + + t.equals(common.isBuiltinFunctionCall(abiNamespaceCallNodes.encode), true, 'encode Builtin') + t.equals(common.isBuiltinFunctionCall(abiNamespaceCallNodes.encodePacked), true, 'encodePacked Builtin') + t.equals(common.isBuiltinFunctionCall(abiNamespaceCallNodes.encodeWithSelector), true, 'encodeWithSelector Builtin') + t.equals(common.isBuiltinFunctionCall(abiNamespaceCallNodes.encodeWithSignature), true, 'encodeWithSignature Builtin') }) diff --git a/libs/remix-analyzer/test/analysis/staticAnalysisIntegration-test-0.4.24.ts b/libs/remix-analyzer/test/analysis/staticAnalysisIntegration-test-0.4.24.ts index 305b53ae97..3d992d4a4c 100644 --- a/libs/remix-analyzer/test/analysis/staticAnalysisIntegration-test-0.4.24.ts +++ b/libs/remix-analyzer/test/analysis/staticAnalysisIntegration-test-0.4.24.ts @@ -10,815 +10,815 @@ const { compilerInput } = helpers.compiler const folder: string = 'solidity-v0.4.24' const testFiles: string[] = [ - 'KingOfTheEtherThrone.sol', - 'assembly.sol', - 'ballot.sol', - 'ballot_reentrant.sol', - 'ballot_withoutWarnings.sol', - 'cross_contract.sol', - 'inheritance.sol', - 'modifier1.sol', - 'modifier2.sol', - 'notReentrant.sol', - 'structReentrant.sol', - 'thisLocal.sol', - 'globals.sol', - 'library.sol', - 'transfer.sol', - 'ctor.sol', - 'forgottenReturn.sol', - 'selfdestruct.sol', - 'deleteDynamicArray.sol', - 'deleteFromDynamicArray.sol', - 'blockLevelCompare.sol', - 'intDivisionTruncate.sol', - 'ERC20.sol', - 'stringBytesLength.sol', - 'etherTransferInLoop.sol', - 'forLoopIteratesOverDynamicArray.sol' + 'KingOfTheEtherThrone.sol', + 'assembly.sol', + 'ballot.sol', + 'ballot_reentrant.sol', + 'ballot_withoutWarnings.sol', + 'cross_contract.sol', + 'inheritance.sol', + 'modifier1.sol', + 'modifier2.sol', + 'notReentrant.sol', + 'structReentrant.sol', + 'thisLocal.sol', + 'globals.sol', + 'library.sol', + 'transfer.sol', + 'ctor.sol', + 'forgottenReturn.sol', + 'selfdestruct.sol', + 'deleteDynamicArray.sol', + 'deleteFromDynamicArray.sol', + 'blockLevelCompare.sol', + 'intDivisionTruncate.sol', + 'ERC20.sol', + 'stringBytesLength.sol', + 'etherTransferInLoop.sol', + 'forLoopIteratesOverDynamicArray.sol' ] const compilationResults: Record = {} test('setup', function (t: test.Test) { - solcOrg.loadRemoteVersion('v0.4.24+commit.e67f0147', (error, compiler) => { - if (error) throw error + solcOrg.loadRemoteVersion('v0.4.24+commit.e67f0147', (error, compiler) => { + if (error) throw error - testFiles.forEach((fileName) => { - const content: string = readFileSync(join(__dirname, 'test-contracts/' + folder, fileName), 'utf8') - // Latest AST is available under 'compileStandardWrapper' under solc for, 0.4.12 <= version < 0.5.0 - compilationResults[fileName] = JSON.parse(compiler.compile(compilerInput(content))) - }) - - t.end() + testFiles.forEach((fileName) => { + const content: string = readFileSync(join(__dirname, 'test-contracts/' + folder, fileName), 'utf8') + // Latest AST is available under 'compileStandardWrapper' under solc for, 0.4.12 <= version < 0.5.0 + compilationResults[fileName] = JSON.parse(compiler.compile(compilerInput(content))) }) + + t.end() + }) }); test('Integration test thisLocal module', function (t: test.Test) { - t.plan(testFiles.length) - const module: any = modules.thisLocal - const lengthCheck: Record = { - 'KingOfTheEtherThrone.sol': 0, - 'assembly.sol': 0, - 'ballot.sol': 0, - 'ballot_reentrant.sol': 1, - 'ballot_withoutWarnings.sol': 0, - 'cross_contract.sol': 0, - 'inheritance.sol': 0, - 'modifier1.sol': 0, - 'modifier2.sol': 0, - 'notReentrant.sol': 0, - 'structReentrant.sol': 0, - 'thisLocal.sol': 1, - 'globals.sol': 0, - 'library.sol': 0, - 'transfer.sol': 0, - 'ctor.sol': 0, - 'forgottenReturn.sol': 0, - 'selfdestruct.sol': 0, - 'deleteDynamicArray.sol': 0, - 'deleteFromDynamicArray.sol': 0, - 'blockLevelCompare.sol': 0, - 'intDivisionTruncate.sol': 0, - 'ERC20.sol': 0, - 'stringBytesLength.sol': 0, - 'etherTransferInLoop.sol': 0, - 'forLoopIteratesOverDynamicArray.sol': 0 - } - runModuleOnFiles(module, t, (file: string, report: AnalysisReportObj[]) => { - t.equal(report.length, lengthCheck[file], `${file} has right amount of thisLocal warnings`) - }) + t.plan(testFiles.length) + const module: any = modules.thisLocal + const lengthCheck: Record = { + 'KingOfTheEtherThrone.sol': 0, + 'assembly.sol': 0, + 'ballot.sol': 0, + 'ballot_reentrant.sol': 1, + 'ballot_withoutWarnings.sol': 0, + 'cross_contract.sol': 0, + 'inheritance.sol': 0, + 'modifier1.sol': 0, + 'modifier2.sol': 0, + 'notReentrant.sol': 0, + 'structReentrant.sol': 0, + 'thisLocal.sol': 1, + 'globals.sol': 0, + 'library.sol': 0, + 'transfer.sol': 0, + 'ctor.sol': 0, + 'forgottenReturn.sol': 0, + 'selfdestruct.sol': 0, + 'deleteDynamicArray.sol': 0, + 'deleteFromDynamicArray.sol': 0, + 'blockLevelCompare.sol': 0, + 'intDivisionTruncate.sol': 0, + 'ERC20.sol': 0, + 'stringBytesLength.sol': 0, + 'etherTransferInLoop.sol': 0, + 'forLoopIteratesOverDynamicArray.sol': 0 + } + runModuleOnFiles(module, t, (file: string, report: AnalysisReportObj[]) => { + t.equal(report.length, lengthCheck[file], `${file} has right amount of thisLocal warnings`) + }) }) test('Integration test checksEffectsInteraction module', function (t: test.Test) { - t.plan(testFiles.length) - const module: any = modules.checksEffectsInteraction - const lengthCheck: Record = { - 'KingOfTheEtherThrone.sol': 1, - 'assembly.sol': 1, - 'ballot.sol': 0, - 'ballot_reentrant.sol': 1, - 'ballot_withoutWarnings.sol': 0, - 'cross_contract.sol': 0, - 'inheritance.sol': 1, - 'modifier1.sol': 0, - 'modifier2.sol': 0, - 'notReentrant.sol': 0, - 'structReentrant.sol': 1, - 'thisLocal.sol': 0, - 'globals.sol': 1, - 'library.sol': 1, - 'transfer.sol': 1, - 'ctor.sol': 0, - 'forgottenReturn.sol': 0, - 'selfdestruct.sol': 0, - 'deleteDynamicArray.sol': 0, - 'deleteFromDynamicArray.sol': 0, - 'blockLevelCompare.sol': 0, - 'intDivisionTruncate.sol': 0, - 'ERC20.sol': 0, - 'stringBytesLength.sol': 0, - 'etherTransferInLoop.sol': 0, - 'forLoopIteratesOverDynamicArray.sol': 0 - } - runModuleOnFiles(module, t, (file: string, report: AnalysisReportObj[]) => { - t.equal(report.length, lengthCheck[file], `${file} has right amount of checksEffectsInteraction warnings`) - }) + t.plan(testFiles.length) + const module: any = modules.checksEffectsInteraction + const lengthCheck: Record = { + 'KingOfTheEtherThrone.sol': 1, + 'assembly.sol': 1, + 'ballot.sol': 0, + 'ballot_reentrant.sol': 1, + 'ballot_withoutWarnings.sol': 0, + 'cross_contract.sol': 0, + 'inheritance.sol': 1, + 'modifier1.sol': 0, + 'modifier2.sol': 0, + 'notReentrant.sol': 0, + 'structReentrant.sol': 1, + 'thisLocal.sol': 0, + 'globals.sol': 1, + 'library.sol': 1, + 'transfer.sol': 1, + 'ctor.sol': 0, + 'forgottenReturn.sol': 0, + 'selfdestruct.sol': 0, + 'deleteDynamicArray.sol': 0, + 'deleteFromDynamicArray.sol': 0, + 'blockLevelCompare.sol': 0, + 'intDivisionTruncate.sol': 0, + 'ERC20.sol': 0, + 'stringBytesLength.sol': 0, + 'etherTransferInLoop.sol': 0, + 'forLoopIteratesOverDynamicArray.sol': 0 + } + runModuleOnFiles(module, t, (file: string, report: AnalysisReportObj[]) => { + t.equal(report.length, lengthCheck[file], `${file} has right amount of checksEffectsInteraction warnings`) + }) }) test('Integration test constantFunctions module', function (t: test.Test) { - t.plan(testFiles.length) - const module: any = modules.constantFunctions - const lengthCheck: Record = { - 'KingOfTheEtherThrone.sol': 0, - 'assembly.sol': 0, - 'ballot.sol': 0, - 'ballot_reentrant.sol': 0, - 'ballot_withoutWarnings.sol': 0, - 'cross_contract.sol': 1, - 'inheritance.sol': 0, - 'modifier1.sol': 1, - 'modifier2.sol': 0, - 'notReentrant.sol': 0, - 'structReentrant.sol': 1, - 'thisLocal.sol': 1, - 'globals.sol': 0, - 'library.sol': 3, - 'transfer.sol': 0, - 'ctor.sol': 0, - 'forgottenReturn.sol': 0, - 'selfdestruct.sol': 1, - 'deleteDynamicArray.sol': 0, - 'deleteFromDynamicArray.sol': 0, - 'blockLevelCompare.sol': 0, - 'intDivisionTruncate.sol': 0, - 'ERC20.sol': 0, - 'stringBytesLength.sol': 0, - 'etherTransferInLoop.sol': 0, - 'forLoopIteratesOverDynamicArray.sol': 0 - } - runModuleOnFiles(module, t, (file: string, report: AnalysisReportObj[]) => { - t.equal(report.length, lengthCheck[file], `${file} has right amount of constantFunctions warnings`) - }) + t.plan(testFiles.length) + const module: any = modules.constantFunctions + const lengthCheck: Record = { + 'KingOfTheEtherThrone.sol': 0, + 'assembly.sol': 0, + 'ballot.sol': 0, + 'ballot_reentrant.sol': 0, + 'ballot_withoutWarnings.sol': 0, + 'cross_contract.sol': 1, + 'inheritance.sol': 0, + 'modifier1.sol': 1, + 'modifier2.sol': 0, + 'notReentrant.sol': 0, + 'structReentrant.sol': 1, + 'thisLocal.sol': 1, + 'globals.sol': 0, + 'library.sol': 3, + 'transfer.sol': 0, + 'ctor.sol': 0, + 'forgottenReturn.sol': 0, + 'selfdestruct.sol': 1, + 'deleteDynamicArray.sol': 0, + 'deleteFromDynamicArray.sol': 0, + 'blockLevelCompare.sol': 0, + 'intDivisionTruncate.sol': 0, + 'ERC20.sol': 0, + 'stringBytesLength.sol': 0, + 'etherTransferInLoop.sol': 0, + 'forLoopIteratesOverDynamicArray.sol': 0 + } + runModuleOnFiles(module, t, (file: string, report: AnalysisReportObj[]) => { + t.equal(report.length, lengthCheck[file], `${file} has right amount of constantFunctions warnings`) + }) }) test('Integration test inlineAssembly module', function (t: test.Test) { - t.plan(testFiles.length) - const module: any = modules.inlineAssembly - const lengthCheck: Record = { - 'KingOfTheEtherThrone.sol': 0, - 'assembly.sol': 2, - 'ballot.sol': 0, - 'ballot_reentrant.sol': 0, - 'ballot_withoutWarnings.sol': 0, - 'cross_contract.sol': 0, - 'inheritance.sol': 0, - 'modifier1.sol': 0, - 'modifier2.sol': 0, - 'notReentrant.sol': 0, - 'structReentrant.sol': 0, - 'thisLocal.sol': 0, - 'globals.sol': 0, - 'library.sol': 0, - 'transfer.sol': 0, - 'ctor.sol': 0, - 'forgottenReturn.sol': 0, - 'selfdestruct.sol': 0, - 'deleteDynamicArray.sol': 0, - 'deleteFromDynamicArray.sol': 0, - 'blockLevelCompare.sol': 0, - 'intDivisionTruncate.sol': 0, - 'ERC20.sol': 0, - 'stringBytesLength.sol': 0, - 'etherTransferInLoop.sol': 0, - 'forLoopIteratesOverDynamicArray.sol': 0 - } - runModuleOnFiles(module, t, (file: string, report: AnalysisReportObj[]) => { - t.equal(report.length, lengthCheck[file], `${file} has right amount of inlineAssembly warnings`) - }) + t.plan(testFiles.length) + const module: any = modules.inlineAssembly + const lengthCheck: Record = { + 'KingOfTheEtherThrone.sol': 0, + 'assembly.sol': 2, + 'ballot.sol': 0, + 'ballot_reentrant.sol': 0, + 'ballot_withoutWarnings.sol': 0, + 'cross_contract.sol': 0, + 'inheritance.sol': 0, + 'modifier1.sol': 0, + 'modifier2.sol': 0, + 'notReentrant.sol': 0, + 'structReentrant.sol': 0, + 'thisLocal.sol': 0, + 'globals.sol': 0, + 'library.sol': 0, + 'transfer.sol': 0, + 'ctor.sol': 0, + 'forgottenReturn.sol': 0, + 'selfdestruct.sol': 0, + 'deleteDynamicArray.sol': 0, + 'deleteFromDynamicArray.sol': 0, + 'blockLevelCompare.sol': 0, + 'intDivisionTruncate.sol': 0, + 'ERC20.sol': 0, + 'stringBytesLength.sol': 0, + 'etherTransferInLoop.sol': 0, + 'forLoopIteratesOverDynamicArray.sol': 0 + } + runModuleOnFiles(module, t, (file: string, report: AnalysisReportObj[]) => { + t.equal(report.length, lengthCheck[file], `${file} has right amount of inlineAssembly warnings`) + }) }) test('Integration test txOrigin module', function (t: test.Test) { - t.plan(testFiles.length) - const module: any = modules.txOrigin - const lengthCheck: Record = { - 'KingOfTheEtherThrone.sol': 0, - 'assembly.sol': 1, - 'ballot.sol': 0, - 'ballot_reentrant.sol': 0, - 'ballot_withoutWarnings.sol': 0, - 'cross_contract.sol': 0, - 'inheritance.sol': 0, - 'modifier1.sol': 0, - 'modifier2.sol': 0, - 'notReentrant.sol': 0, - 'structReentrant.sol': 0, - 'thisLocal.sol': 0, - 'globals.sol': 1, - 'library.sol': 0, - 'transfer.sol': 0, - 'ctor.sol': 0, - 'forgottenReturn.sol': 0, - 'selfdestruct.sol': 0, - 'deleteDynamicArray.sol': 0, - 'deleteFromDynamicArray.sol': 0, - 'blockLevelCompare.sol': 0, - 'intDivisionTruncate.sol': 0, - 'ERC20.sol': 0, - 'stringBytesLength.sol': 0, - 'etherTransferInLoop.sol': 0, - 'forLoopIteratesOverDynamicArray.sol': 0 - } - runModuleOnFiles(module, t, (file: string, report: AnalysisReportObj[]) => { - t.equal(report.length, lengthCheck[file], `${file} has right amount of txOrigin warnings`) - }) + t.plan(testFiles.length) + const module: any = modules.txOrigin + const lengthCheck: Record = { + 'KingOfTheEtherThrone.sol': 0, + 'assembly.sol': 1, + 'ballot.sol': 0, + 'ballot_reentrant.sol': 0, + 'ballot_withoutWarnings.sol': 0, + 'cross_contract.sol': 0, + 'inheritance.sol': 0, + 'modifier1.sol': 0, + 'modifier2.sol': 0, + 'notReentrant.sol': 0, + 'structReentrant.sol': 0, + 'thisLocal.sol': 0, + 'globals.sol': 1, + 'library.sol': 0, + 'transfer.sol': 0, + 'ctor.sol': 0, + 'forgottenReturn.sol': 0, + 'selfdestruct.sol': 0, + 'deleteDynamicArray.sol': 0, + 'deleteFromDynamicArray.sol': 0, + 'blockLevelCompare.sol': 0, + 'intDivisionTruncate.sol': 0, + 'ERC20.sol': 0, + 'stringBytesLength.sol': 0, + 'etherTransferInLoop.sol': 0, + 'forLoopIteratesOverDynamicArray.sol': 0 + } + runModuleOnFiles(module, t, (file: string, report: AnalysisReportObj[]) => { + t.equal(report.length, lengthCheck[file], `${file} has right amount of txOrigin warnings`) + }) }) test('Integration test gasCosts module', function (t: test.Test) { - t.plan(testFiles.length) - const module: any = modules.gasCosts - const lengthCheck: Record = { - 'KingOfTheEtherThrone.sol': 2, - 'assembly.sol': 2, - 'ballot.sol': 3, - 'ballot_reentrant.sol': 2, - 'ballot_withoutWarnings.sol': 0, - 'cross_contract.sol': 1, - 'inheritance.sol': 1, - 'modifier1.sol': 0, - 'modifier2.sol': 1, - 'notReentrant.sol': 1, - 'structReentrant.sol': 1, - 'thisLocal.sol': 1, - 'globals.sol': 1, - 'library.sol': 1, - 'transfer.sol': 1, - 'ctor.sol': 0, - 'forgottenReturn.sol': 3, - 'selfdestruct.sol': 0, - 'deleteDynamicArray.sol': 2, - 'deleteFromDynamicArray.sol': 1, - 'blockLevelCompare.sol': 1, - 'intDivisionTruncate.sol': 1, - 'ERC20.sol': 2, - 'stringBytesLength.sol': 1, - 'etherTransferInLoop.sol': 3, - 'forLoopIteratesOverDynamicArray.sol': 2 - } - runModuleOnFiles(module, t, (file: string, report: AnalysisReportObj[]) => { - t.equal(report.length, lengthCheck[file], `${file} has right amount of gasCosts warnings`) - }) + t.plan(testFiles.length) + const module: any = modules.gasCosts + const lengthCheck: Record = { + 'KingOfTheEtherThrone.sol': 2, + 'assembly.sol': 2, + 'ballot.sol': 3, + 'ballot_reentrant.sol': 2, + 'ballot_withoutWarnings.sol': 0, + 'cross_contract.sol': 1, + 'inheritance.sol': 1, + 'modifier1.sol': 0, + 'modifier2.sol': 1, + 'notReentrant.sol': 1, + 'structReentrant.sol': 1, + 'thisLocal.sol': 1, + 'globals.sol': 1, + 'library.sol': 1, + 'transfer.sol': 1, + 'ctor.sol': 0, + 'forgottenReturn.sol': 3, + 'selfdestruct.sol': 0, + 'deleteDynamicArray.sol': 2, + 'deleteFromDynamicArray.sol': 1, + 'blockLevelCompare.sol': 1, + 'intDivisionTruncate.sol': 1, + 'ERC20.sol': 2, + 'stringBytesLength.sol': 1, + 'etherTransferInLoop.sol': 3, + 'forLoopIteratesOverDynamicArray.sol': 2 + } + runModuleOnFiles(module, t, (file: string, report: AnalysisReportObj[]) => { + t.equal(report.length, lengthCheck[file], `${file} has right amount of gasCosts warnings`) + }) }) test('Integration test similarVariableNames module', function (t: test.Test) { - t.plan(testFiles.length) - const module: any = modules.similarVariableNames - const lengthCheck: Record = { - 'KingOfTheEtherThrone.sol': 0, - 'assembly.sol': 0, - 'ballot.sol': 7, - 'ballot_reentrant.sol': 16, - 'ballot_withoutWarnings.sol': 0, - 'cross_contract.sol': 0, - 'inheritance.sol': 0, - 'modifier1.sol': 0, - 'modifier2.sol': 0, - 'notReentrant.sol': 4, - 'structReentrant.sol': 0, - 'thisLocal.sol': 0, - 'globals.sol': 0, - 'library.sol': 0, - 'transfer.sol': 0, - 'ctor.sol': 2, - 'forgottenReturn.sol': 0, - 'selfdestruct.sol': 0, - 'deleteDynamicArray.sol': 2, - 'deleteFromDynamicArray.sol': 0, - 'blockLevelCompare.sol': 0, - 'intDivisionTruncate.sol': 0, - 'ERC20.sol': 0, - 'stringBytesLength.sol': 0, - 'etherTransferInLoop.sol': 0, - 'forLoopIteratesOverDynamicArray.sol': 0 - } - runModuleOnFiles(module, t, (file: string, report: AnalysisReportObj[]) => { - t.equal(report.length, lengthCheck[file], `${file} has right amount of similarVariableNames warnings`) - }) + t.plan(testFiles.length) + const module: any = modules.similarVariableNames + const lengthCheck: Record = { + 'KingOfTheEtherThrone.sol': 0, + 'assembly.sol': 0, + 'ballot.sol': 7, + 'ballot_reentrant.sol': 16, + 'ballot_withoutWarnings.sol': 0, + 'cross_contract.sol': 0, + 'inheritance.sol': 0, + 'modifier1.sol': 0, + 'modifier2.sol': 0, + 'notReentrant.sol': 4, + 'structReentrant.sol': 0, + 'thisLocal.sol': 0, + 'globals.sol': 0, + 'library.sol': 0, + 'transfer.sol': 0, + 'ctor.sol': 2, + 'forgottenReturn.sol': 0, + 'selfdestruct.sol': 0, + 'deleteDynamicArray.sol': 2, + 'deleteFromDynamicArray.sol': 0, + 'blockLevelCompare.sol': 0, + 'intDivisionTruncate.sol': 0, + 'ERC20.sol': 0, + 'stringBytesLength.sol': 0, + 'etherTransferInLoop.sol': 0, + 'forLoopIteratesOverDynamicArray.sol': 0 + } + runModuleOnFiles(module, t, (file: string, report: AnalysisReportObj[]) => { + t.equal(report.length, lengthCheck[file], `${file} has right amount of similarVariableNames warnings`) + }) }) test('Integration test blockTimestamp module', function (t: test.Test) { - t.plan(testFiles.length) - const module: any = modules.blockTimestamp - const lengthCheck: Record = { - 'KingOfTheEtherThrone.sol': 1, - 'assembly.sol': 0, - 'ballot.sol': 0, - 'ballot_reentrant.sol': 3, - 'ballot_withoutWarnings.sol': 0, - 'cross_contract.sol': 0, - 'inheritance.sol': 0, - 'modifier1.sol': 0, - 'modifier2.sol': 0, - 'notReentrant.sol': 0, - 'structReentrant.sol': 0, - 'thisLocal.sol': 0, - 'globals.sol': 2, - 'library.sol': 0, - 'transfer.sol': 0, - 'ctor.sol': 0, - 'forgottenReturn.sol': 0, - 'selfdestruct.sol': 0, - 'deleteDynamicArray.sol': 0, - 'deleteFromDynamicArray.sol': 0, - 'blockLevelCompare.sol': 0, - 'intDivisionTruncate.sol': 0, - 'ERC20.sol': 0, - 'stringBytesLength.sol': 0, - 'etherTransferInLoop.sol': 0, - 'forLoopIteratesOverDynamicArray.sol': 0 - } - runModuleOnFiles(module, t, (file: string, report: AnalysisReportObj[]) => { - t.equal(report.length, lengthCheck[file], `${file} has right amount of blockTimestamp warnings`) - }) + t.plan(testFiles.length) + const module: any = modules.blockTimestamp + const lengthCheck: Record = { + 'KingOfTheEtherThrone.sol': 1, + 'assembly.sol': 0, + 'ballot.sol': 0, + 'ballot_reentrant.sol': 3, + 'ballot_withoutWarnings.sol': 0, + 'cross_contract.sol': 0, + 'inheritance.sol': 0, + 'modifier1.sol': 0, + 'modifier2.sol': 0, + 'notReentrant.sol': 0, + 'structReentrant.sol': 0, + 'thisLocal.sol': 0, + 'globals.sol': 2, + 'library.sol': 0, + 'transfer.sol': 0, + 'ctor.sol': 0, + 'forgottenReturn.sol': 0, + 'selfdestruct.sol': 0, + 'deleteDynamicArray.sol': 0, + 'deleteFromDynamicArray.sol': 0, + 'blockLevelCompare.sol': 0, + 'intDivisionTruncate.sol': 0, + 'ERC20.sol': 0, + 'stringBytesLength.sol': 0, + 'etherTransferInLoop.sol': 0, + 'forLoopIteratesOverDynamicArray.sol': 0 + } + runModuleOnFiles(module, t, (file: string, report: AnalysisReportObj[]) => { + t.equal(report.length, lengthCheck[file], `${file} has right amount of blockTimestamp warnings`) + }) }) test('Integration test lowLevelCalls module', function (t: test.Test) { - t.plan(testFiles.length) - const module: any = modules.lowLevelCalls - const lengthCheck: Record = { - 'KingOfTheEtherThrone.sol': 1, - 'assembly.sol': 1, - 'ballot.sol': 0, - 'ballot_reentrant.sol': 7, - 'ballot_withoutWarnings.sol': 0, - 'cross_contract.sol': 1, - 'inheritance.sol': 1, - 'modifier1.sol': 0, - 'modifier2.sol': 0, - 'notReentrant.sol': 1, - 'structReentrant.sol': 1, - 'thisLocal.sol': 2, - 'globals.sol': 1, - 'library.sol': 1, - 'transfer.sol': 0, - 'ctor.sol': 0, - 'forgottenReturn.sol': 0, - 'selfdestruct.sol': 0, - 'deleteDynamicArray.sol': 0, - 'deleteFromDynamicArray.sol': 0, - 'blockLevelCompare.sol': 0, - 'intDivisionTruncate.sol': 0, - 'ERC20.sol': 0, - 'stringBytesLength.sol': 0, - 'etherTransferInLoop.sol': 0, - 'forLoopIteratesOverDynamicArray.sol': 0 - } - runModuleOnFiles(module, t, (file: string, report: AnalysisReportObj[]) => { - t.equal(report.length, lengthCheck[file], `${file} has right amount of lowLevelCalls warnings`) - }) + t.plan(testFiles.length) + const module: any = modules.lowLevelCalls + const lengthCheck: Record = { + 'KingOfTheEtherThrone.sol': 1, + 'assembly.sol': 1, + 'ballot.sol': 0, + 'ballot_reentrant.sol': 7, + 'ballot_withoutWarnings.sol': 0, + 'cross_contract.sol': 1, + 'inheritance.sol': 1, + 'modifier1.sol': 0, + 'modifier2.sol': 0, + 'notReentrant.sol': 1, + 'structReentrant.sol': 1, + 'thisLocal.sol': 2, + 'globals.sol': 1, + 'library.sol': 1, + 'transfer.sol': 0, + 'ctor.sol': 0, + 'forgottenReturn.sol': 0, + 'selfdestruct.sol': 0, + 'deleteDynamicArray.sol': 0, + 'deleteFromDynamicArray.sol': 0, + 'blockLevelCompare.sol': 0, + 'intDivisionTruncate.sol': 0, + 'ERC20.sol': 0, + 'stringBytesLength.sol': 0, + 'etherTransferInLoop.sol': 0, + 'forLoopIteratesOverDynamicArray.sol': 0 + } + runModuleOnFiles(module, t, (file: string, report: AnalysisReportObj[]) => { + t.equal(report.length, lengthCheck[file], `${file} has right amount of lowLevelCalls warnings`) + }) }) test('Integration test blockBlockhash module', function (t: test.Test) { - t.plan(testFiles.length) - const module: any = modules.blockBlockhash - const lengthCheck: Record = { - 'KingOfTheEtherThrone.sol': 0, - 'assembly.sol': 0, - 'ballot.sol': 0, - 'ballot_reentrant.sol': 0, - 'ballot_withoutWarnings.sol': 0, - 'cross_contract.sol': 0, - 'inheritance.sol': 0, - 'modifier1.sol': 0, - 'modifier2.sol': 0, - 'notReentrant.sol': 0, - 'structReentrant.sol': 0, - 'thisLocal.sol': 0, - 'globals.sol': 1, - 'library.sol': 0, - 'transfer.sol': 0, - 'ctor.sol': 0, - 'forgottenReturn.sol': 0, - 'selfdestruct.sol': 0, - 'deleteDynamicArray.sol': 0, - 'deleteFromDynamicArray.sol': 0, - 'blockLevelCompare.sol': 0, - 'intDivisionTruncate.sol': 0, - 'ERC20.sol': 0, - 'stringBytesLength.sol': 0, - 'etherTransferInLoop.sol': 0, - 'forLoopIteratesOverDynamicArray.sol': 0 - } - runModuleOnFiles(module, t, (file: string, report: AnalysisReportObj[]) => { - t.equal(report.length, lengthCheck[file], `${file} has right amount of blockBlockhash warnings`) - }) + t.plan(testFiles.length) + const module: any = modules.blockBlockhash + const lengthCheck: Record = { + 'KingOfTheEtherThrone.sol': 0, + 'assembly.sol': 0, + 'ballot.sol': 0, + 'ballot_reentrant.sol': 0, + 'ballot_withoutWarnings.sol': 0, + 'cross_contract.sol': 0, + 'inheritance.sol': 0, + 'modifier1.sol': 0, + 'modifier2.sol': 0, + 'notReentrant.sol': 0, + 'structReentrant.sol': 0, + 'thisLocal.sol': 0, + 'globals.sol': 1, + 'library.sol': 0, + 'transfer.sol': 0, + 'ctor.sol': 0, + 'forgottenReturn.sol': 0, + 'selfdestruct.sol': 0, + 'deleteDynamicArray.sol': 0, + 'deleteFromDynamicArray.sol': 0, + 'blockLevelCompare.sol': 0, + 'intDivisionTruncate.sol': 0, + 'ERC20.sol': 0, + 'stringBytesLength.sol': 0, + 'etherTransferInLoop.sol': 0, + 'forLoopIteratesOverDynamicArray.sol': 0 + } + runModuleOnFiles(module, t, (file: string, report: AnalysisReportObj[]) => { + t.equal(report.length, lengthCheck[file], `${file} has right amount of blockBlockhash warnings`) + }) }) test('Integration test noReturn module', function (t: test.Test) { - t.plan(testFiles.length) - const module: any = modules.noReturn - const lengthCheck: Record = { - 'KingOfTheEtherThrone.sol': 0, - 'assembly.sol': 1, - 'ballot.sol': 0, - 'ballot_reentrant.sol': 0, - 'ballot_withoutWarnings.sol': 0, - 'cross_contract.sol': 0, - 'inheritance.sol': 0, - 'modifier1.sol': 1, - 'modifier2.sol': 0, - 'notReentrant.sol': 0, - 'structReentrant.sol': 0, - 'thisLocal.sol': 1, - 'globals.sol': 0, - 'library.sol': 0, - 'transfer.sol': 0, - 'ctor.sol': 0, - 'forgottenReturn.sol': 1, - 'selfdestruct.sol': 0, - 'deleteDynamicArray.sol': 0, - 'deleteFromDynamicArray.sol': 0, - 'blockLevelCompare.sol': 0, - 'intDivisionTruncate.sol': 0, - 'ERC20.sol': 0, - 'stringBytesLength.sol': 0, - 'etherTransferInLoop.sol': 0, - 'forLoopIteratesOverDynamicArray.sol': 0 - } - runModuleOnFiles(module, t, (file: string, report: AnalysisReportObj[]) => { - t.equal(report.length, lengthCheck[file], `${file} has right amount of noReturn warnings`) - }) + t.plan(testFiles.length) + const module: any = modules.noReturn + const lengthCheck: Record = { + 'KingOfTheEtherThrone.sol': 0, + 'assembly.sol': 1, + 'ballot.sol': 0, + 'ballot_reentrant.sol': 0, + 'ballot_withoutWarnings.sol': 0, + 'cross_contract.sol': 0, + 'inheritance.sol': 0, + 'modifier1.sol': 1, + 'modifier2.sol': 0, + 'notReentrant.sol': 0, + 'structReentrant.sol': 0, + 'thisLocal.sol': 1, + 'globals.sol': 0, + 'library.sol': 0, + 'transfer.sol': 0, + 'ctor.sol': 0, + 'forgottenReturn.sol': 1, + 'selfdestruct.sol': 0, + 'deleteDynamicArray.sol': 0, + 'deleteFromDynamicArray.sol': 0, + 'blockLevelCompare.sol': 0, + 'intDivisionTruncate.sol': 0, + 'ERC20.sol': 0, + 'stringBytesLength.sol': 0, + 'etherTransferInLoop.sol': 0, + 'forLoopIteratesOverDynamicArray.sol': 0 + } + runModuleOnFiles(module, t, (file: string, report: AnalysisReportObj[]) => { + t.equal(report.length, lengthCheck[file], `${file} has right amount of noReturn warnings`) + }) }) test('Integration test selfdestruct module', function (t: test.Test) { - t.plan(testFiles.length) - const module: any = modules.selfdestruct - const lengthCheck: Record = { - 'KingOfTheEtherThrone.sol': 0, - 'assembly.sol': 0, - 'ballot.sol': 0, - 'ballot_reentrant.sol': 0, - 'ballot_withoutWarnings.sol': 0, - 'cross_contract.sol': 0, - 'inheritance.sol': 0, - 'modifier1.sol': 0, - 'modifier2.sol': 0, - 'notReentrant.sol': 0, - 'structReentrant.sol': 0, - 'thisLocal.sol': 0, - 'globals.sol': 2, - 'library.sol': 0, - 'transfer.sol': 0, - 'ctor.sol': 0, - 'forgottenReturn.sol': 0, - 'selfdestruct.sol': 3, - 'deleteDynamicArray.sol': 0, - 'deleteFromDynamicArray.sol': 0, - 'blockLevelCompare.sol': 0, - 'ERC20.sol': 0, - 'intDivisionTruncate.sol': 5, - 'stringBytesLength.sol': 0, - 'etherTransferInLoop.sol': 0, - 'forLoopIteratesOverDynamicArray.sol': 0 - } - runModuleOnFiles(module, t, (file: string, report: AnalysisReportObj[]) => { - t.equal(report.length, lengthCheck[file], `${file} has right amount of selfdestruct warnings`) - }) + t.plan(testFiles.length) + const module: any = modules.selfdestruct + const lengthCheck: Record = { + 'KingOfTheEtherThrone.sol': 0, + 'assembly.sol': 0, + 'ballot.sol': 0, + 'ballot_reentrant.sol': 0, + 'ballot_withoutWarnings.sol': 0, + 'cross_contract.sol': 0, + 'inheritance.sol': 0, + 'modifier1.sol': 0, + 'modifier2.sol': 0, + 'notReentrant.sol': 0, + 'structReentrant.sol': 0, + 'thisLocal.sol': 0, + 'globals.sol': 2, + 'library.sol': 0, + 'transfer.sol': 0, + 'ctor.sol': 0, + 'forgottenReturn.sol': 0, + 'selfdestruct.sol': 3, + 'deleteDynamicArray.sol': 0, + 'deleteFromDynamicArray.sol': 0, + 'blockLevelCompare.sol': 0, + 'ERC20.sol': 0, + 'intDivisionTruncate.sol': 5, + 'stringBytesLength.sol': 0, + 'etherTransferInLoop.sol': 0, + 'forLoopIteratesOverDynamicArray.sol': 0 + } + runModuleOnFiles(module, t, (file: string, report: AnalysisReportObj[]) => { + t.equal(report.length, lengthCheck[file], `${file} has right amount of selfdestruct warnings`) + }) }) test('Integration test guardConditions module', function (t: test.Test) { - t.plan(testFiles.length) - const module: any = modules.guardConditions - const lengthCheck: Record = { - 'KingOfTheEtherThrone.sol': 0, - 'assembly.sol': 2, - 'ballot.sol': 0, - 'ballot_reentrant.sol': 0, - 'ballot_withoutWarnings.sol': 0, - 'cross_contract.sol': 0, - 'inheritance.sol': 0, - 'modifier1.sol': 0, - 'modifier2.sol': 0, - 'notReentrant.sol': 0, - 'structReentrant.sol': 0, - 'thisLocal.sol': 0, - 'globals.sol': 1, - 'library.sol': 0, - 'transfer.sol': 0, - 'ctor.sol': 0, - 'forgottenReturn.sol': 0, - 'selfdestruct.sol': 0, - 'deleteDynamicArray.sol': 1, - 'deleteFromDynamicArray.sol': 0, - 'blockLevelCompare.sol': 0, - 'intDivisionTruncate.sol': 1, - 'ERC20.sol': 0, - 'stringBytesLength.sol': 0, - 'etherTransferInLoop.sol': 0, - 'forLoopIteratesOverDynamicArray.sol': 0 - } - runModuleOnFiles(module, t, (file: string, report: AnalysisReportObj[]) => { - t.equal(report.length, lengthCheck[file], `${file} has right amount of guardConditions warnings`) - }) + t.plan(testFiles.length) + const module: any = modules.guardConditions + const lengthCheck: Record = { + 'KingOfTheEtherThrone.sol': 0, + 'assembly.sol': 2, + 'ballot.sol': 0, + 'ballot_reentrant.sol': 0, + 'ballot_withoutWarnings.sol': 0, + 'cross_contract.sol': 0, + 'inheritance.sol': 0, + 'modifier1.sol': 0, + 'modifier2.sol': 0, + 'notReentrant.sol': 0, + 'structReentrant.sol': 0, + 'thisLocal.sol': 0, + 'globals.sol': 1, + 'library.sol': 0, + 'transfer.sol': 0, + 'ctor.sol': 0, + 'forgottenReturn.sol': 0, + 'selfdestruct.sol': 0, + 'deleteDynamicArray.sol': 1, + 'deleteFromDynamicArray.sol': 0, + 'blockLevelCompare.sol': 0, + 'intDivisionTruncate.sol': 1, + 'ERC20.sol': 0, + 'stringBytesLength.sol': 0, + 'etherTransferInLoop.sol': 0, + 'forLoopIteratesOverDynamicArray.sol': 0 + } + runModuleOnFiles(module, t, (file: string, report: AnalysisReportObj[]) => { + t.equal(report.length, lengthCheck[file], `${file} has right amount of guardConditions warnings`) + }) }) test('Integration test deleteDynamicArrays module', function (t: test.Test) { - t.plan(testFiles.length) - const module: any = modules.deleteDynamicArrays - const lengthCheck: Record = { - 'KingOfTheEtherThrone.sol': 0, - 'assembly.sol': 0, - 'ballot.sol': 0, - 'ballot_reentrant.sol': 0, - 'ballot_withoutWarnings.sol': 0, - 'cross_contract.sol': 0, - 'inheritance.sol': 0, - 'modifier1.sol': 0, - 'modifier2.sol': 0, - 'notReentrant.sol': 0, - 'structReentrant.sol': 0, - 'thisLocal.sol': 0, - 'globals.sol': 0, - 'library.sol': 0, - 'transfer.sol': 0, - 'ctor.sol': 0, - 'forgottenReturn.sol': 0, - 'selfdestruct.sol': 0, - 'deleteDynamicArray.sol': 2, - 'deleteFromDynamicArray.sol': 0, - 'blockLevelCompare.sol': 0, - 'intDivisionTruncate.sol': 0, - 'ERC20.sol': 0, - 'stringBytesLength.sol': 0, - 'etherTransferInLoop.sol': 0, - 'forLoopIteratesOverDynamicArray.sol': 0 - } - runModuleOnFiles(module, t, (file: string, report: AnalysisReportObj[]) => { - t.equal(report.length, lengthCheck[file], `${file} has right amount of deleteDynamicArrays warnings`) - }) + t.plan(testFiles.length) + const module: any = modules.deleteDynamicArrays + const lengthCheck: Record = { + 'KingOfTheEtherThrone.sol': 0, + 'assembly.sol': 0, + 'ballot.sol': 0, + 'ballot_reentrant.sol': 0, + 'ballot_withoutWarnings.sol': 0, + 'cross_contract.sol': 0, + 'inheritance.sol': 0, + 'modifier1.sol': 0, + 'modifier2.sol': 0, + 'notReentrant.sol': 0, + 'structReentrant.sol': 0, + 'thisLocal.sol': 0, + 'globals.sol': 0, + 'library.sol': 0, + 'transfer.sol': 0, + 'ctor.sol': 0, + 'forgottenReturn.sol': 0, + 'selfdestruct.sol': 0, + 'deleteDynamicArray.sol': 2, + 'deleteFromDynamicArray.sol': 0, + 'blockLevelCompare.sol': 0, + 'intDivisionTruncate.sol': 0, + 'ERC20.sol': 0, + 'stringBytesLength.sol': 0, + 'etherTransferInLoop.sol': 0, + 'forLoopIteratesOverDynamicArray.sol': 0 + } + runModuleOnFiles(module, t, (file: string, report: AnalysisReportObj[]) => { + t.equal(report.length, lengthCheck[file], `${file} has right amount of deleteDynamicArrays warnings`) + }) }) test('Integration test deleteFromDynamicArray module', function (t) { - t.plan(testFiles.length) - const module: any = modules.deleteFromDynamicArray - const lengthCheck: Record = { - 'KingOfTheEtherThrone.sol': 0, - 'assembly.sol': 0, - 'ballot.sol': 0, - 'ballot_reentrant.sol': 0, - 'ballot_withoutWarnings.sol': 0, - 'cross_contract.sol': 0, - 'inheritance.sol': 0, - 'modifier1.sol': 0, - 'modifier2.sol': 0, - 'notReentrant.sol': 0, - 'structReentrant.sol': 0, - 'thisLocal.sol': 0, - 'globals.sol': 0, - 'library.sol': 0, - 'transfer.sol': 0, - 'ctor.sol': 0, - 'forgottenReturn.sol': 0, - 'selfdestruct.sol': 0, - 'deleteDynamicArray.sol': 0, - 'deleteFromDynamicArray.sol': 1, - 'blockLevelCompare.sol': 0, - 'intDivisionTruncate.sol': 0, - 'ERC20.sol': 0, - 'stringBytesLength.sol': 0, - 'etherTransferInLoop.sol': 0, - 'forLoopIteratesOverDynamicArray.sol': 0 - } - runModuleOnFiles(module, t, (file: string, report: AnalysisReportObj[]) => { - t.equal(report.length, lengthCheck[file], `${file} has right amount of deleteFromDynamicArray warnings`) - }) + t.plan(testFiles.length) + const module: any = modules.deleteFromDynamicArray + const lengthCheck: Record = { + 'KingOfTheEtherThrone.sol': 0, + 'assembly.sol': 0, + 'ballot.sol': 0, + 'ballot_reentrant.sol': 0, + 'ballot_withoutWarnings.sol': 0, + 'cross_contract.sol': 0, + 'inheritance.sol': 0, + 'modifier1.sol': 0, + 'modifier2.sol': 0, + 'notReentrant.sol': 0, + 'structReentrant.sol': 0, + 'thisLocal.sol': 0, + 'globals.sol': 0, + 'library.sol': 0, + 'transfer.sol': 0, + 'ctor.sol': 0, + 'forgottenReturn.sol': 0, + 'selfdestruct.sol': 0, + 'deleteDynamicArray.sol': 0, + 'deleteFromDynamicArray.sol': 1, + 'blockLevelCompare.sol': 0, + 'intDivisionTruncate.sol': 0, + 'ERC20.sol': 0, + 'stringBytesLength.sol': 0, + 'etherTransferInLoop.sol': 0, + 'forLoopIteratesOverDynamicArray.sol': 0 + } + runModuleOnFiles(module, t, (file: string, report: AnalysisReportObj[]) => { + t.equal(report.length, lengthCheck[file], `${file} has right amount of deleteFromDynamicArray warnings`) + }) }) test('Integration test assignAndCompare module', function (t: test.Test) { - t.plan(testFiles.length) - const module: any = modules.assignAndCompare - const lengthCheck: Record = { - 'KingOfTheEtherThrone.sol': 0, - 'assembly.sol': 0, - 'ballot.sol': 0, - 'ballot_reentrant.sol': 0, - 'ballot_withoutWarnings.sol': 0, - 'cross_contract.sol': 0, - 'inheritance.sol': 0, - 'modifier1.sol': 0, - 'modifier2.sol': 0, - 'notReentrant.sol': 0, - 'structReentrant.sol': 0, - 'thisLocal.sol': 0, - 'globals.sol': 0, - 'library.sol': 0, - 'transfer.sol': 0, - 'ctor.sol': 0, - 'forgottenReturn.sol': 0, - 'selfdestruct.sol': 0, - 'deleteDynamicArray.sol': 0, - 'deleteFromDynamicArray.sol': 0, - 'blockLevelCompare.sol': 8, - 'intDivisionTruncate.sol': 0, - 'ERC20.sol': 0, - 'stringBytesLength.sol': 0, - 'etherTransferInLoop.sol': 0, - 'forLoopIteratesOverDynamicArray.sol': 0 - } - runModuleOnFiles(module, t, (file: string, report: AnalysisReportObj[]) => { - t.equal(report.length, lengthCheck[file], `${file} has right amount of assignAndCompare warnings`) - }) + t.plan(testFiles.length) + const module: any = modules.assignAndCompare + const lengthCheck: Record = { + 'KingOfTheEtherThrone.sol': 0, + 'assembly.sol': 0, + 'ballot.sol': 0, + 'ballot_reentrant.sol': 0, + 'ballot_withoutWarnings.sol': 0, + 'cross_contract.sol': 0, + 'inheritance.sol': 0, + 'modifier1.sol': 0, + 'modifier2.sol': 0, + 'notReentrant.sol': 0, + 'structReentrant.sol': 0, + 'thisLocal.sol': 0, + 'globals.sol': 0, + 'library.sol': 0, + 'transfer.sol': 0, + 'ctor.sol': 0, + 'forgottenReturn.sol': 0, + 'selfdestruct.sol': 0, + 'deleteDynamicArray.sol': 0, + 'deleteFromDynamicArray.sol': 0, + 'blockLevelCompare.sol': 8, + 'intDivisionTruncate.sol': 0, + 'ERC20.sol': 0, + 'stringBytesLength.sol': 0, + 'etherTransferInLoop.sol': 0, + 'forLoopIteratesOverDynamicArray.sol': 0 + } + runModuleOnFiles(module, t, (file: string, report: AnalysisReportObj[]) => { + t.equal(report.length, lengthCheck[file], `${file} has right amount of assignAndCompare warnings`) + }) }) test('Integration test intDivisionTruncate module', function (t: test.Test) { - t.plan(testFiles.length) - const module: any = modules.intDivisionTruncate - const lengthCheck: Record = { - 'KingOfTheEtherThrone.sol': 0, - 'assembly.sol': 0, - 'ballot.sol': 0, - 'ballot_reentrant.sol': 0, - 'ballot_withoutWarnings.sol': 0, - 'cross_contract.sol': 0, - 'inheritance.sol': 0, - 'modifier1.sol': 0, - 'modifier2.sol': 0, - 'notReentrant.sol': 0, - 'structReentrant.sol': 0, - 'thisLocal.sol': 0, - 'globals.sol': 0, - 'library.sol': 0, - 'transfer.sol': 0, - 'ctor.sol': 0, - 'forgottenReturn.sol': 0, - 'selfdestruct.sol': 0, - 'deleteDynamicArray.sol': 0, - 'deleteFromDynamicArray.sol': 0, - 'blockLevelCompare.sol': 0, - 'intDivisionTruncate.sol': 2, - 'ERC20.sol': 0, - 'stringBytesLength.sol': 0, - 'etherTransferInLoop.sol': 0, - 'forLoopIteratesOverDynamicArray.sol': 0 - } - runModuleOnFiles(module, t, (file: string, report: AnalysisReportObj[]) => { - t.equal(report.length, lengthCheck[file], `${file} has right amount of intDivisionTruncate warnings`) - }) + t.plan(testFiles.length) + const module: any = modules.intDivisionTruncate + const lengthCheck: Record = { + 'KingOfTheEtherThrone.sol': 0, + 'assembly.sol': 0, + 'ballot.sol': 0, + 'ballot_reentrant.sol': 0, + 'ballot_withoutWarnings.sol': 0, + 'cross_contract.sol': 0, + 'inheritance.sol': 0, + 'modifier1.sol': 0, + 'modifier2.sol': 0, + 'notReentrant.sol': 0, + 'structReentrant.sol': 0, + 'thisLocal.sol': 0, + 'globals.sol': 0, + 'library.sol': 0, + 'transfer.sol': 0, + 'ctor.sol': 0, + 'forgottenReturn.sol': 0, + 'selfdestruct.sol': 0, + 'deleteDynamicArray.sol': 0, + 'deleteFromDynamicArray.sol': 0, + 'blockLevelCompare.sol': 0, + 'intDivisionTruncate.sol': 2, + 'ERC20.sol': 0, + 'stringBytesLength.sol': 0, + 'etherTransferInLoop.sol': 0, + 'forLoopIteratesOverDynamicArray.sol': 0 + } + runModuleOnFiles(module, t, (file: string, report: AnalysisReportObj[]) => { + t.equal(report.length, lengthCheck[file], `${file} has right amount of intDivisionTruncate warnings`) + }) }) test('Integration test erc20Decimal module', function (t: test.Test) { - t.plan(testFiles.length) - const module: any = modules.erc20Decimals - const lengthCheck: Record = { - 'KingOfTheEtherThrone.sol': 0, - 'assembly.sol': 0, - 'ballot.sol': 0, - 'ballot_reentrant.sol': 0, - 'ballot_withoutWarnings.sol': 0, - 'cross_contract.sol': 0, - 'inheritance.sol': 0, - 'modifier1.sol': 0, - 'modifier2.sol': 0, - 'notReentrant.sol': 0, - 'structReentrant.sol': 0, - 'thisLocal.sol': 0, - 'globals.sol': 0, - 'library.sol': 0, - 'transfer.sol': 0, - 'ctor.sol': 0, - 'forgottenReturn.sol': 0, - 'selfdestruct.sol': 0, - 'deleteDynamicArray.sol': 0, - 'deleteFromDynamicArray.sol': 0, - 'blockLevelCompare.sol': 0, - 'intDivisionTruncate.sol': 0, - 'ERC20.sol': 1, - 'stringBytesLength.sol': 0, - 'etherTransferInLoop.sol': 0, - 'forLoopIteratesOverDynamicArray.sol': 0 - } - runModuleOnFiles(module, t, (file: string, report: AnalysisReportObj[]) => { - t.equal(report.length, lengthCheck[file], `${file} has right amount of erc20Decimals warnings`) - }) + t.plan(testFiles.length) + const module: any = modules.erc20Decimals + const lengthCheck: Record = { + 'KingOfTheEtherThrone.sol': 0, + 'assembly.sol': 0, + 'ballot.sol': 0, + 'ballot_reentrant.sol': 0, + 'ballot_withoutWarnings.sol': 0, + 'cross_contract.sol': 0, + 'inheritance.sol': 0, + 'modifier1.sol': 0, + 'modifier2.sol': 0, + 'notReentrant.sol': 0, + 'structReentrant.sol': 0, + 'thisLocal.sol': 0, + 'globals.sol': 0, + 'library.sol': 0, + 'transfer.sol': 0, + 'ctor.sol': 0, + 'forgottenReturn.sol': 0, + 'selfdestruct.sol': 0, + 'deleteDynamicArray.sol': 0, + 'deleteFromDynamicArray.sol': 0, + 'blockLevelCompare.sol': 0, + 'intDivisionTruncate.sol': 0, + 'ERC20.sol': 1, + 'stringBytesLength.sol': 0, + 'etherTransferInLoop.sol': 0, + 'forLoopIteratesOverDynamicArray.sol': 0 + } + runModuleOnFiles(module, t, (file: string, report: AnalysisReportObj[]) => { + t.equal(report.length, lengthCheck[file], `${file} has right amount of erc20Decimals warnings`) + }) }) test('Integration test stringBytesLength module', function (t: test.Test) { - t.plan(testFiles.length) - const module: any = modules.stringBytesLength - const lengthCheck: Record = { - 'KingOfTheEtherThrone.sol': 0, - 'assembly.sol': 0, - 'ballot.sol': 0, - 'ballot_reentrant.sol': 0, - 'ballot_withoutWarnings.sol': 0, - 'cross_contract.sol': 0, - 'inheritance.sol': 0, - 'modifier1.sol': 0, - 'modifier2.sol': 0, - 'notReentrant.sol': 0, - 'structReentrant.sol': 0, - 'thisLocal.sol': 0, - 'globals.sol': 0, - 'library.sol': 0, - 'transfer.sol': 0, - 'ctor.sol': 0, - 'forgottenReturn.sol': 0, - 'selfdestruct.sol': 0, - 'deleteDynamicArray.sol': 0, - 'deleteFromDynamicArray.sol': 0, - 'blockLevelCompare.sol': 0, - 'intDivisionTruncate.sol': 0, - 'ERC20.sol': 0, - 'stringBytesLength.sol': 1, - 'etherTransferInLoop.sol': 0, - 'forLoopIteratesOverDynamicArray.sol': 0 - } - runModuleOnFiles(module, t, (file: string, report: AnalysisReportObj[]) => { - t.equal(report.length, lengthCheck[file], `${file} has right amount of stringBytesLength warnings`) - }) + t.plan(testFiles.length) + const module: any = modules.stringBytesLength + const lengthCheck: Record = { + 'KingOfTheEtherThrone.sol': 0, + 'assembly.sol': 0, + 'ballot.sol': 0, + 'ballot_reentrant.sol': 0, + 'ballot_withoutWarnings.sol': 0, + 'cross_contract.sol': 0, + 'inheritance.sol': 0, + 'modifier1.sol': 0, + 'modifier2.sol': 0, + 'notReentrant.sol': 0, + 'structReentrant.sol': 0, + 'thisLocal.sol': 0, + 'globals.sol': 0, + 'library.sol': 0, + 'transfer.sol': 0, + 'ctor.sol': 0, + 'forgottenReturn.sol': 0, + 'selfdestruct.sol': 0, + 'deleteDynamicArray.sol': 0, + 'deleteFromDynamicArray.sol': 0, + 'blockLevelCompare.sol': 0, + 'intDivisionTruncate.sol': 0, + 'ERC20.sol': 0, + 'stringBytesLength.sol': 1, + 'etherTransferInLoop.sol': 0, + 'forLoopIteratesOverDynamicArray.sol': 0 + } + runModuleOnFiles(module, t, (file: string, report: AnalysisReportObj[]) => { + t.equal(report.length, lengthCheck[file], `${file} has right amount of stringBytesLength warnings`) + }) }) test('Integration test etherTransferInLoop module', function (t: test.Test) { - t.plan(testFiles.length) - const module: any = modules.etherTransferInLoop - const lengthCheck: Record = { - 'KingOfTheEtherThrone.sol': 0, - 'assembly.sol': 0, - 'ballot.sol': 0, - 'ballot_reentrant.sol': 0, - 'ballot_withoutWarnings.sol': 0, - 'cross_contract.sol': 0, - 'inheritance.sol': 0, - 'modifier1.sol': 0, - 'modifier2.sol': 0, - 'notReentrant.sol': 0, - 'structReentrant.sol': 0, - 'thisLocal.sol': 0, - 'globals.sol': 0, - 'library.sol': 0, - 'transfer.sol': 0, - 'ctor.sol': 0, - 'forgottenReturn.sol': 0, - 'selfdestruct.sol': 0, - 'deleteDynamicArray.sol': 0, - 'deleteFromDynamicArray.sol': 0, - 'blockLevelCompare.sol': 0, - 'intDivisionTruncate.sol': 0, - 'ERC20.sol': 0, - 'stringBytesLength.sol': 0, - 'etherTransferInLoop.sol': 3, - 'forLoopIteratesOverDynamicArray.sol': 0 - } - runModuleOnFiles(module, t, (file: string, report: AnalysisReportObj[]) => { - t.equal(report.length, lengthCheck[file], `${file} has right amount of etherTransferInLoop warnings`) - }) + t.plan(testFiles.length) + const module: any = modules.etherTransferInLoop + const lengthCheck: Record = { + 'KingOfTheEtherThrone.sol': 0, + 'assembly.sol': 0, + 'ballot.sol': 0, + 'ballot_reentrant.sol': 0, + 'ballot_withoutWarnings.sol': 0, + 'cross_contract.sol': 0, + 'inheritance.sol': 0, + 'modifier1.sol': 0, + 'modifier2.sol': 0, + 'notReentrant.sol': 0, + 'structReentrant.sol': 0, + 'thisLocal.sol': 0, + 'globals.sol': 0, + 'library.sol': 0, + 'transfer.sol': 0, + 'ctor.sol': 0, + 'forgottenReturn.sol': 0, + 'selfdestruct.sol': 0, + 'deleteDynamicArray.sol': 0, + 'deleteFromDynamicArray.sol': 0, + 'blockLevelCompare.sol': 0, + 'intDivisionTruncate.sol': 0, + 'ERC20.sol': 0, + 'stringBytesLength.sol': 0, + 'etherTransferInLoop.sol': 3, + 'forLoopIteratesOverDynamicArray.sol': 0 + } + runModuleOnFiles(module, t, (file: string, report: AnalysisReportObj[]) => { + t.equal(report.length, lengthCheck[file], `${file} has right amount of etherTransferInLoop warnings`) + }) }) test('Integration test forLoopIteratesOverDynamicArray module', function (t: test.Test) { - t.plan(testFiles.length) - const module: any = modules.forLoopIteratesOverDynamicArray - const lengthCheck: Record = { - 'KingOfTheEtherThrone.sol': 0, - 'assembly.sol': 0, - 'ballot.sol': 2, - 'ballot_reentrant.sol': 1, - 'ballot_withoutWarnings.sol': 0, - 'cross_contract.sol': 0, - 'inheritance.sol': 0, - 'modifier1.sol': 0, - 'modifier2.sol': 0, - 'notReentrant.sol': 0, - 'structReentrant.sol': 0, - 'thisLocal.sol': 0, - 'globals.sol': 0, - 'library.sol': 0, - 'transfer.sol': 0, - 'ctor.sol': 0, - 'forgottenReturn.sol': 0, - 'selfdestruct.sol': 0, - 'deleteDynamicArray.sol': 0, - 'deleteFromDynamicArray.sol': 0, - 'blockLevelCompare.sol': 0, - 'intDivisionTruncate.sol': 0, - 'ERC20.sol': 0, - 'stringBytesLength.sol': 0, - 'etherTransferInLoop.sol': 0, - 'forLoopIteratesOverDynamicArray.sol': 2 - } - runModuleOnFiles(module, t, (file: string, report: AnalysisReportObj[]) => { - t.equal(report.length, lengthCheck[file], `${file} has right amount of forLoopIteratesOverDynamicArray warnings`) - }) + t.plan(testFiles.length) + const module: any = modules.forLoopIteratesOverDynamicArray + const lengthCheck: Record = { + 'KingOfTheEtherThrone.sol': 0, + 'assembly.sol': 0, + 'ballot.sol': 2, + 'ballot_reentrant.sol': 1, + 'ballot_withoutWarnings.sol': 0, + 'cross_contract.sol': 0, + 'inheritance.sol': 0, + 'modifier1.sol': 0, + 'modifier2.sol': 0, + 'notReentrant.sol': 0, + 'structReentrant.sol': 0, + 'thisLocal.sol': 0, + 'globals.sol': 0, + 'library.sol': 0, + 'transfer.sol': 0, + 'ctor.sol': 0, + 'forgottenReturn.sol': 0, + 'selfdestruct.sol': 0, + 'deleteDynamicArray.sol': 0, + 'deleteFromDynamicArray.sol': 0, + 'blockLevelCompare.sol': 0, + 'intDivisionTruncate.sol': 0, + 'ERC20.sol': 0, + 'stringBytesLength.sol': 0, + 'etherTransferInLoop.sol': 0, + 'forLoopIteratesOverDynamicArray.sol': 2 + } + runModuleOnFiles(module, t, (file: string, report: AnalysisReportObj[]) => { + t.equal(report.length, lengthCheck[file], `${file} has right amount of forLoopIteratesOverDynamicArray warnings`) + }) }) // #################### Helpers function runModuleOnFiles (Module: any, t: test.Test, cb: ((fname: string, report: AnalysisReportObj[]) => void)): void { - const statRunner: StatRunner = new StatRunner() - testFiles.forEach((fileName: string) => { - const reports = statRunner.runWithModuleList(compilationResults[fileName], [{ name: new Module().name, mod: new Module() }]) - const report: AnalysisReportObj[] = reports[0].report - if (report.some((x: AnalysisReportObj) => x.warning.includes('INTERNAL ERROR'))) { - t.comment('Error while executing Module: ' + JSON.stringify(report)) - } - cb(fileName, report) - }) + const statRunner: StatRunner = new StatRunner() + testFiles.forEach((fileName: string) => { + const reports = statRunner.runWithModuleList(compilationResults[fileName], [{ name: new Module().name, mod: new Module() }]) + const report: AnalysisReportObj[] = reports[0].report + if (report.some((x: AnalysisReportObj) => x.warning.includes('INTERNAL ERROR'))) { + t.comment('Error while executing Module: ' + JSON.stringify(report)) + } + cb(fileName, report) + }) } diff --git a/libs/remix-analyzer/test/analysis/staticAnalysisIntegration-test-0.5.0.ts b/libs/remix-analyzer/test/analysis/staticAnalysisIntegration-test-0.5.0.ts index 18881e20ef..bc1d018d51 100644 --- a/libs/remix-analyzer/test/analysis/staticAnalysisIntegration-test-0.5.0.ts +++ b/libs/remix-analyzer/test/analysis/staticAnalysisIntegration-test-0.5.0.ts @@ -10,817 +10,817 @@ const { compilerInput } = helpers.compiler const folder: string = 'solidity-v0.5' const testFiles: string[] = [ - 'KingOfTheEtherThrone.sol', - 'assembly.sol', - 'ballot.sol', - 'ballot_reentrant.sol', - 'ballot_withoutWarnings.sol', - 'cross_contract.sol', - 'inheritance.sol', - 'modifier1.sol', - 'modifier2.sol', - 'notReentrant.sol', - 'structReentrant.sol', - 'thisLocal.sol', - 'globals.sol', - 'library.sol', - 'transfer.sol', - 'ctor.sol', - 'forgottenReturn.sol', - 'selfdestruct.sol', - 'deleteDynamicArray.sol', - 'deleteFromDynamicArray.sol', - 'blockLevelCompare.sol', - 'intDivisionTruncate.sol', - 'ERC20.sol', - 'stringBytesLength.sol', - 'etherTransferInLoop.sol', - 'forLoopIteratesOverDynamicArray.sol' + 'KingOfTheEtherThrone.sol', + 'assembly.sol', + 'ballot.sol', + 'ballot_reentrant.sol', + 'ballot_withoutWarnings.sol', + 'cross_contract.sol', + 'inheritance.sol', + 'modifier1.sol', + 'modifier2.sol', + 'notReentrant.sol', + 'structReentrant.sol', + 'thisLocal.sol', + 'globals.sol', + 'library.sol', + 'transfer.sol', + 'ctor.sol', + 'forgottenReturn.sol', + 'selfdestruct.sol', + 'deleteDynamicArray.sol', + 'deleteFromDynamicArray.sol', + 'blockLevelCompare.sol', + 'intDivisionTruncate.sol', + 'ERC20.sol', + 'stringBytesLength.sol', + 'etherTransferInLoop.sol', + 'forLoopIteratesOverDynamicArray.sol' ] const compilationResults: Record = {} test('setup', function (t) { - solc.loadRemoteVersion('v0.5.0+commit.1d4f565a', (error, compiler) => { - if (error) throw error + solc.loadRemoteVersion('v0.5.0+commit.1d4f565a', (error, compiler) => { + if (error) throw error - testFiles.forEach((fileName) => { - const content = readFileSync(join(__dirname, 'test-contracts/' + folder, fileName), 'utf8') - compilationResults[fileName] = JSON.parse(compiler.compile(compilerInput(content))) - }) - - t.end() + testFiles.forEach((fileName) => { + const content = readFileSync(join(__dirname, 'test-contracts/' + folder, fileName), 'utf8') + compilationResults[fileName] = JSON.parse(compiler.compile(compilerInput(content))) }) + + t.end() + }) }); test('Integration test thisLocal module', function (t: test.Test) { - t.plan(testFiles.length) - const module: any = modules.thisLocal - const lengthCheck: Record = { - 'KingOfTheEtherThrone.sol': 0, - 'assembly.sol': 0, - 'ballot.sol': 0, - 'ballot_reentrant.sol': 1, - 'ballot_withoutWarnings.sol': 0, - 'cross_contract.sol': 0, - 'inheritance.sol': 0, - 'modifier1.sol': 0, - 'modifier2.sol': 0, - 'notReentrant.sol': 0, - 'structReentrant.sol': 0, - 'thisLocal.sol': 1, - 'globals.sol': 0, - 'library.sol': 0, - 'transfer.sol': 0, - 'ctor.sol': 0, - 'forgottenReturn.sol': 0, - 'selfdestruct.sol': 0, - 'deleteDynamicArray.sol': 0, - 'deleteFromDynamicArray.sol': 0, - 'blockLevelCompare.sol': 0, - 'intDivisionTruncate.sol': 0, - 'ERC20.sol': 0, - 'stringBytesLength.sol': 0, - 'etherTransferInLoop.sol': 0, - 'forLoopIteratesOverDynamicArray.sol': 0 - } - runModuleOnFiles(module, t, (file: string, report: AnalysisReportObj[]) => { - t.equal(report.length, lengthCheck[file], `${file} has right amount of thisLocal warnings`) - }) + t.plan(testFiles.length) + const module: any = modules.thisLocal + const lengthCheck: Record = { + 'KingOfTheEtherThrone.sol': 0, + 'assembly.sol': 0, + 'ballot.sol': 0, + 'ballot_reentrant.sol': 1, + 'ballot_withoutWarnings.sol': 0, + 'cross_contract.sol': 0, + 'inheritance.sol': 0, + 'modifier1.sol': 0, + 'modifier2.sol': 0, + 'notReentrant.sol': 0, + 'structReentrant.sol': 0, + 'thisLocal.sol': 1, + 'globals.sol': 0, + 'library.sol': 0, + 'transfer.sol': 0, + 'ctor.sol': 0, + 'forgottenReturn.sol': 0, + 'selfdestruct.sol': 0, + 'deleteDynamicArray.sol': 0, + 'deleteFromDynamicArray.sol': 0, + 'blockLevelCompare.sol': 0, + 'intDivisionTruncate.sol': 0, + 'ERC20.sol': 0, + 'stringBytesLength.sol': 0, + 'etherTransferInLoop.sol': 0, + 'forLoopIteratesOverDynamicArray.sol': 0 + } + runModuleOnFiles(module, t, (file: string, report: AnalysisReportObj[]) => { + t.equal(report.length, lengthCheck[file], `${file} has right amount of thisLocal warnings`) + }) }) test('Integration test checksEffectsInteraction module', function (t: test.Test) { - t.plan(testFiles.length) - const module: any = modules.checksEffectsInteraction - const lengthCheck: Record = { - 'KingOfTheEtherThrone.sol': 1, - 'assembly.sol': 1, - 'ballot.sol': 0, - 'ballot_reentrant.sol': 1, - 'ballot_withoutWarnings.sol': 0, - 'cross_contract.sol': 0, - 'inheritance.sol': 1, - 'modifier1.sol': 0, - 'modifier2.sol': 0, - 'notReentrant.sol': 0, - 'structReentrant.sol': 1, - 'thisLocal.sol': 0, - 'globals.sol': 1, - 'library.sol': 1, - 'transfer.sol': 1, - 'ctor.sol': 0, - 'forgottenReturn.sol': 0, - 'selfdestruct.sol': 0, - 'deleteDynamicArray.sol': 0, - 'deleteFromDynamicArray.sol': 0, - 'blockLevelCompare.sol': 0, - 'intDivisionTruncate.sol': 0, - 'ERC20.sol': 0, - 'stringBytesLength.sol': 0, - 'etherTransferInLoop.sol': 0, - 'forLoopIteratesOverDynamicArray.sol': 0 - } - runModuleOnFiles(module, t, (file: string, report: AnalysisReportObj[]) => { - t.equal(report.length, lengthCheck[file], `${file} has right amount of checksEffectsInteraction warnings`) - }) + t.plan(testFiles.length) + const module: any = modules.checksEffectsInteraction + const lengthCheck: Record = { + 'KingOfTheEtherThrone.sol': 1, + 'assembly.sol': 1, + 'ballot.sol': 0, + 'ballot_reentrant.sol': 1, + 'ballot_withoutWarnings.sol': 0, + 'cross_contract.sol': 0, + 'inheritance.sol': 1, + 'modifier1.sol': 0, + 'modifier2.sol': 0, + 'notReentrant.sol': 0, + 'structReentrant.sol': 1, + 'thisLocal.sol': 0, + 'globals.sol': 1, + 'library.sol': 1, + 'transfer.sol': 1, + 'ctor.sol': 0, + 'forgottenReturn.sol': 0, + 'selfdestruct.sol': 0, + 'deleteDynamicArray.sol': 0, + 'deleteFromDynamicArray.sol': 0, + 'blockLevelCompare.sol': 0, + 'intDivisionTruncate.sol': 0, + 'ERC20.sol': 0, + 'stringBytesLength.sol': 0, + 'etherTransferInLoop.sol': 0, + 'forLoopIteratesOverDynamicArray.sol': 0 + } + runModuleOnFiles(module, t, (file: string, report: AnalysisReportObj[]) => { + t.equal(report.length, lengthCheck[file], `${file} has right amount of checksEffectsInteraction warnings`) + }) }) test('Integration test constantFunctions module', function (t: test.Test) { - t.plan(testFiles.length) - const module: any = modules.constantFunctions - const lengthCheck: Record = { - 'KingOfTheEtherThrone.sol': 0, - 'assembly.sol': 0, - 'ballot.sol': 1, - 'ballot_reentrant.sol': 0, - 'ballot_withoutWarnings.sol': 0, - 'cross_contract.sol': 0, - 'inheritance.sol': 0, - 'modifier1.sol': 1, - 'modifier2.sol': 0, - 'notReentrant.sol': 0, - 'structReentrant.sol': 1, - 'thisLocal.sol': 1, - 'globals.sol': 0, - 'library.sol': 3, - 'transfer.sol': 0, - 'ctor.sol': 0, - 'forgottenReturn.sol': 0, - 'selfdestruct.sol': 0, - 'deleteDynamicArray.sol': 0, - 'deleteFromDynamicArray.sol': 0, - 'blockLevelCompare.sol': 0, - 'intDivisionTruncate.sol': 0, - 'ERC20.sol': 0, - 'stringBytesLength.sol': 0, - 'etherTransferInLoop.sol': 0, - 'forLoopIteratesOverDynamicArray.sol': 0 - } - runModuleOnFiles(module, t, (file: string, report: AnalysisReportObj[]) => { - t.equal(report.length, lengthCheck[file], `${file} has right amount of constantFunctions warnings`) - }) + t.plan(testFiles.length) + const module: any = modules.constantFunctions + const lengthCheck: Record = { + 'KingOfTheEtherThrone.sol': 0, + 'assembly.sol': 0, + 'ballot.sol': 1, + 'ballot_reentrant.sol': 0, + 'ballot_withoutWarnings.sol': 0, + 'cross_contract.sol': 0, + 'inheritance.sol': 0, + 'modifier1.sol': 1, + 'modifier2.sol': 0, + 'notReentrant.sol': 0, + 'structReentrant.sol': 1, + 'thisLocal.sol': 1, + 'globals.sol': 0, + 'library.sol': 3, + 'transfer.sol': 0, + 'ctor.sol': 0, + 'forgottenReturn.sol': 0, + 'selfdestruct.sol': 0, + 'deleteDynamicArray.sol': 0, + 'deleteFromDynamicArray.sol': 0, + 'blockLevelCompare.sol': 0, + 'intDivisionTruncate.sol': 0, + 'ERC20.sol': 0, + 'stringBytesLength.sol': 0, + 'etherTransferInLoop.sol': 0, + 'forLoopIteratesOverDynamicArray.sol': 0 + } + runModuleOnFiles(module, t, (file: string, report: AnalysisReportObj[]) => { + t.equal(report.length, lengthCheck[file], `${file} has right amount of constantFunctions warnings`) + }) }) test('Integration test inlineAssembly module', function (t: test.Test) { - t.plan(testFiles.length) - const module: any = modules.inlineAssembly - const lengthCheck: Record = { - 'KingOfTheEtherThrone.sol': 0, - 'assembly.sol': 2, - 'ballot.sol': 0, - 'ballot_reentrant.sol': 0, - 'ballot_withoutWarnings.sol': 0, - 'cross_contract.sol': 0, - 'inheritance.sol': 0, - 'modifier1.sol': 0, - 'modifier2.sol': 0, - 'notReentrant.sol': 0, - 'structReentrant.sol': 0, - 'thisLocal.sol': 0, - 'globals.sol': 0, - 'library.sol': 0, - 'transfer.sol': 0, - 'ctor.sol': 0, - 'forgottenReturn.sol': 0, - 'selfdestruct.sol': 0, - 'deleteDynamicArray.sol': 0, - 'deleteFromDynamicArray.sol': 0, - 'blockLevelCompare.sol': 0, - 'intDivisionTruncate.sol': 0, - 'ERC20.sol': 0, - 'stringBytesLength.sol': 0, - 'etherTransferInLoop.sol': 0, - 'forLoopIteratesOverDynamicArray.sol': 0 - } - runModuleOnFiles(module, t, (file: string, report: AnalysisReportObj[]) => { - t.equal(report.length, lengthCheck[file], `${file} has right amount of inlineAssembly warnings`) - }) + t.plan(testFiles.length) + const module: any = modules.inlineAssembly + const lengthCheck: Record = { + 'KingOfTheEtherThrone.sol': 0, + 'assembly.sol': 2, + 'ballot.sol': 0, + 'ballot_reentrant.sol': 0, + 'ballot_withoutWarnings.sol': 0, + 'cross_contract.sol': 0, + 'inheritance.sol': 0, + 'modifier1.sol': 0, + 'modifier2.sol': 0, + 'notReentrant.sol': 0, + 'structReentrant.sol': 0, + 'thisLocal.sol': 0, + 'globals.sol': 0, + 'library.sol': 0, + 'transfer.sol': 0, + 'ctor.sol': 0, + 'forgottenReturn.sol': 0, + 'selfdestruct.sol': 0, + 'deleteDynamicArray.sol': 0, + 'deleteFromDynamicArray.sol': 0, + 'blockLevelCompare.sol': 0, + 'intDivisionTruncate.sol': 0, + 'ERC20.sol': 0, + 'stringBytesLength.sol': 0, + 'etherTransferInLoop.sol': 0, + 'forLoopIteratesOverDynamicArray.sol': 0 + } + runModuleOnFiles(module, t, (file: string, report: AnalysisReportObj[]) => { + t.equal(report.length, lengthCheck[file], `${file} has right amount of inlineAssembly warnings`) + }) }) test('Integration test txOrigin module', function (t: test.Test) { - t.plan(testFiles.length) - const module: any = modules.txOrigin - const lengthCheck: Record = { - 'KingOfTheEtherThrone.sol': 0, - 'assembly.sol': 1, - 'ballot.sol': 0, - 'ballot_reentrant.sol': 0, - 'ballot_withoutWarnings.sol': 0, - 'cross_contract.sol': 0, - 'inheritance.sol': 0, - 'modifier1.sol': 0, - 'modifier2.sol': 0, - 'notReentrant.sol': 0, - 'structReentrant.sol': 0, - 'thisLocal.sol': 0, - 'globals.sol': 1, - 'library.sol': 0, - 'transfer.sol': 0, - 'ctor.sol': 0, - 'forgottenReturn.sol': 0, - 'selfdestruct.sol': 0, - 'deleteDynamicArray.sol': 0, - 'deleteFromDynamicArray.sol': 0, - 'blockLevelCompare.sol': 0, - 'intDivisionTruncate.sol': 0, - 'ERC20.sol': 0, - 'stringBytesLength.sol': 0, - 'etherTransferInLoop.sol': 0, - 'forLoopIteratesOverDynamicArray.sol': 0 - } - runModuleOnFiles(module, t, (file: string, report: AnalysisReportObj[]) => { - t.equal(report.length, lengthCheck[file], `${file} has right amount of txOrigin warnings`) - }) + t.plan(testFiles.length) + const module: any = modules.txOrigin + const lengthCheck: Record = { + 'KingOfTheEtherThrone.sol': 0, + 'assembly.sol': 1, + 'ballot.sol': 0, + 'ballot_reentrant.sol': 0, + 'ballot_withoutWarnings.sol': 0, + 'cross_contract.sol': 0, + 'inheritance.sol': 0, + 'modifier1.sol': 0, + 'modifier2.sol': 0, + 'notReentrant.sol': 0, + 'structReentrant.sol': 0, + 'thisLocal.sol': 0, + 'globals.sol': 1, + 'library.sol': 0, + 'transfer.sol': 0, + 'ctor.sol': 0, + 'forgottenReturn.sol': 0, + 'selfdestruct.sol': 0, + 'deleteDynamicArray.sol': 0, + 'deleteFromDynamicArray.sol': 0, + 'blockLevelCompare.sol': 0, + 'intDivisionTruncate.sol': 0, + 'ERC20.sol': 0, + 'stringBytesLength.sol': 0, + 'etherTransferInLoop.sol': 0, + 'forLoopIteratesOverDynamicArray.sol': 0 + } + runModuleOnFiles(module, t, (file: string, report: AnalysisReportObj[]) => { + t.equal(report.length, lengthCheck[file], `${file} has right amount of txOrigin warnings`) + }) }) test('Integration test gasCosts module', function (t: test.Test) { - t.plan(testFiles.length) - const module: any = modules.gasCosts - const lengthCheck: Record = { - 'KingOfTheEtherThrone.sol': 2, - 'assembly.sol': 2, - 'ballot.sol': 4, - 'ballot_reentrant.sol': 2, - 'ballot_withoutWarnings.sol': 0, - 'cross_contract.sol': 1, - 'inheritance.sol': 1, - 'modifier1.sol': 0, - 'modifier2.sol': 1, - 'notReentrant.sol': 1, - 'structReentrant.sol': 1, - 'thisLocal.sol': 1, - 'globals.sol': 1, - 'library.sol': 1, - 'transfer.sol': 1, - 'ctor.sol': 0, - 'forgottenReturn.sol': 3, - 'selfdestruct.sol': 0, - 'deleteDynamicArray.sol': 2, - 'deleteFromDynamicArray.sol': 1, - 'blockLevelCompare.sol': 1, - 'intDivisionTruncate.sol': 1, - 'ERC20.sol': 2, - 'stringBytesLength.sol': 1, - 'etherTransferInLoop.sol': 3, - 'forLoopIteratesOverDynamicArray.sol': 2 - } - runModuleOnFiles(module, t, (file: string, report: AnalysisReportObj[]) => { - t.equal(report.length, lengthCheck[file], `${file} has right amount of gasCosts warnings`) - }) + t.plan(testFiles.length) + const module: any = modules.gasCosts + const lengthCheck: Record = { + 'KingOfTheEtherThrone.sol': 2, + 'assembly.sol': 2, + 'ballot.sol': 4, + 'ballot_reentrant.sol': 2, + 'ballot_withoutWarnings.sol': 0, + 'cross_contract.sol': 1, + 'inheritance.sol': 1, + 'modifier1.sol': 0, + 'modifier2.sol': 1, + 'notReentrant.sol': 1, + 'structReentrant.sol': 1, + 'thisLocal.sol': 1, + 'globals.sol': 1, + 'library.sol': 1, + 'transfer.sol': 1, + 'ctor.sol': 0, + 'forgottenReturn.sol': 3, + 'selfdestruct.sol': 0, + 'deleteDynamicArray.sol': 2, + 'deleteFromDynamicArray.sol': 1, + 'blockLevelCompare.sol': 1, + 'intDivisionTruncate.sol': 1, + 'ERC20.sol': 2, + 'stringBytesLength.sol': 1, + 'etherTransferInLoop.sol': 3, + 'forLoopIteratesOverDynamicArray.sol': 2 + } + runModuleOnFiles(module, t, (file: string, report: AnalysisReportObj[]) => { + t.equal(report.length, lengthCheck[file], `${file} has right amount of gasCosts warnings`) + }) }) test('Integration test similarVariableNames module', function (t: test.Test) { - t.plan(testFiles.length) - const module: any = modules.similarVariableNames - const lengthCheck: Record = { - 'KingOfTheEtherThrone.sol': 0, - 'assembly.sol': 0, - 'ballot.sol': 7, - 'ballot_reentrant.sol': 40, - 'ballot_withoutWarnings.sol': 0, - 'cross_contract.sol': 0, - 'inheritance.sol': 0, - 'modifier1.sol': 0, - 'modifier2.sol': 0, - 'notReentrant.sol': 4, - 'structReentrant.sol': 0, - 'thisLocal.sol': 0, - 'globals.sol': 0, - 'library.sol': 0, - 'transfer.sol': 0, - 'ctor.sol': 2, - 'forgottenReturn.sol': 0, - 'selfdestruct.sol': 0, - 'deleteDynamicArray.sol': 2, - 'deleteFromDynamicArray.sol': 0, - 'blockLevelCompare.sol': 0, - 'intDivisionTruncate.sol': 0, - 'ERC20.sol': 0, - 'stringBytesLength.sol': 0, - 'etherTransferInLoop.sol': 0, - 'forLoopIteratesOverDynamicArray.sol': 0 - } - runModuleOnFiles(module, t, (file: string, report: AnalysisReportObj[]) => { - t.equal(report.length, lengthCheck[file], `${file} has right amount of similarVariableNames warnings`) - }) + t.plan(testFiles.length) + const module: any = modules.similarVariableNames + const lengthCheck: Record = { + 'KingOfTheEtherThrone.sol': 0, + 'assembly.sol': 0, + 'ballot.sol': 7, + 'ballot_reentrant.sol': 40, + 'ballot_withoutWarnings.sol': 0, + 'cross_contract.sol': 0, + 'inheritance.sol': 0, + 'modifier1.sol': 0, + 'modifier2.sol': 0, + 'notReentrant.sol': 4, + 'structReentrant.sol': 0, + 'thisLocal.sol': 0, + 'globals.sol': 0, + 'library.sol': 0, + 'transfer.sol': 0, + 'ctor.sol': 2, + 'forgottenReturn.sol': 0, + 'selfdestruct.sol': 0, + 'deleteDynamicArray.sol': 2, + 'deleteFromDynamicArray.sol': 0, + 'blockLevelCompare.sol': 0, + 'intDivisionTruncate.sol': 0, + 'ERC20.sol': 0, + 'stringBytesLength.sol': 0, + 'etherTransferInLoop.sol': 0, + 'forLoopIteratesOverDynamicArray.sol': 0 + } + runModuleOnFiles(module, t, (file: string, report: AnalysisReportObj[]) => { + t.equal(report.length, lengthCheck[file], `${file} has right amount of similarVariableNames warnings`) + }) }) test('Integration test blockTimestamp module', function (t: test.Test) { - t.plan(testFiles.length) - const module: any = modules.blockTimestamp - const lengthCheck: Record = { - 'KingOfTheEtherThrone.sol': 1, - 'assembly.sol': 0, - 'ballot.sol': 0, - 'ballot_reentrant.sol': 3, - 'ballot_withoutWarnings.sol': 0, - 'cross_contract.sol': 0, - 'inheritance.sol': 0, - 'modifier1.sol': 0, - 'modifier2.sol': 0, - 'notReentrant.sol': 0, - 'structReentrant.sol': 0, - 'thisLocal.sol': 0, - 'globals.sol': 2, - 'library.sol': 0, - 'transfer.sol': 0, - 'ctor.sol': 0, - 'forgottenReturn.sol': 0, - 'selfdestruct.sol': 0, - 'deleteDynamicArray.sol': 0, - 'deleteFromDynamicArray.sol': 0, - 'blockLevelCompare.sol': 0, - 'intDivisionTruncate.sol': 0, - 'ERC20.sol': 0, - 'stringBytesLength.sol': 0, - 'etherTransferInLoop.sol': 0, - 'forLoopIteratesOverDynamicArray.sol': 0 - } - runModuleOnFiles(module, t, (file: string, report: AnalysisReportObj[]) => { - t.equal(report.length, lengthCheck[file], `${file} has right amount of blockTimestamp warnings`) - }) + t.plan(testFiles.length) + const module: any = modules.blockTimestamp + const lengthCheck: Record = { + 'KingOfTheEtherThrone.sol': 1, + 'assembly.sol': 0, + 'ballot.sol': 0, + 'ballot_reentrant.sol': 3, + 'ballot_withoutWarnings.sol': 0, + 'cross_contract.sol': 0, + 'inheritance.sol': 0, + 'modifier1.sol': 0, + 'modifier2.sol': 0, + 'notReentrant.sol': 0, + 'structReentrant.sol': 0, + 'thisLocal.sol': 0, + 'globals.sol': 2, + 'library.sol': 0, + 'transfer.sol': 0, + 'ctor.sol': 0, + 'forgottenReturn.sol': 0, + 'selfdestruct.sol': 0, + 'deleteDynamicArray.sol': 0, + 'deleteFromDynamicArray.sol': 0, + 'blockLevelCompare.sol': 0, + 'intDivisionTruncate.sol': 0, + 'ERC20.sol': 0, + 'stringBytesLength.sol': 0, + 'etherTransferInLoop.sol': 0, + 'forLoopIteratesOverDynamicArray.sol': 0 + } + runModuleOnFiles(module, t, (file: string, report: AnalysisReportObj[]) => { + t.equal(report.length, lengthCheck[file], `${file} has right amount of blockTimestamp warnings`) + }) }) test('Integration test lowLevelCalls module', function (t: test.Test) { - t.plan(testFiles.length) - const module: any = modules.lowLevelCalls - const lengthCheck: Record = { - 'KingOfTheEtherThrone.sol': 1, - 'assembly.sol': 1, - 'ballot.sol': 0, - 'ballot_reentrant.sol': 7, - 'ballot_withoutWarnings.sol': 0, - 'cross_contract.sol': 1, - 'inheritance.sol': 1, - 'modifier1.sol': 0, - 'modifier2.sol': 0, - 'notReentrant.sol': 1, - 'structReentrant.sol': 1, - 'thisLocal.sol': 2, - 'globals.sol': 1, - 'library.sol': 1, - 'transfer.sol': 0, - 'ctor.sol': 0, - 'forgottenReturn.sol': 0, - 'selfdestruct.sol': 0, - 'deleteDynamicArray.sol': 0, - 'deleteFromDynamicArray.sol': 0, - 'blockLevelCompare.sol': 0, - 'intDivisionTruncate.sol': 0, - 'ERC20.sol': 0, - 'stringBytesLength.sol': 0, - 'etherTransferInLoop.sol': 0, - 'forLoopIteratesOverDynamicArray.sol': 0 - } - runModuleOnFiles(module, t, (file: string, report: AnalysisReportObj[]) => { - t.equal(report.length, lengthCheck[file], `${file} has right amount of lowLevelCalls warnings`) - }) + t.plan(testFiles.length) + const module: any = modules.lowLevelCalls + const lengthCheck: Record = { + 'KingOfTheEtherThrone.sol': 1, + 'assembly.sol': 1, + 'ballot.sol': 0, + 'ballot_reentrant.sol': 7, + 'ballot_withoutWarnings.sol': 0, + 'cross_contract.sol': 1, + 'inheritance.sol': 1, + 'modifier1.sol': 0, + 'modifier2.sol': 0, + 'notReentrant.sol': 1, + 'structReentrant.sol': 1, + 'thisLocal.sol': 2, + 'globals.sol': 1, + 'library.sol': 1, + 'transfer.sol': 0, + 'ctor.sol': 0, + 'forgottenReturn.sol': 0, + 'selfdestruct.sol': 0, + 'deleteDynamicArray.sol': 0, + 'deleteFromDynamicArray.sol': 0, + 'blockLevelCompare.sol': 0, + 'intDivisionTruncate.sol': 0, + 'ERC20.sol': 0, + 'stringBytesLength.sol': 0, + 'etherTransferInLoop.sol': 0, + 'forLoopIteratesOverDynamicArray.sol': 0 + } + runModuleOnFiles(module, t, (file: string, report: AnalysisReportObj[]) => { + t.equal(report.length, lengthCheck[file], `${file} has right amount of lowLevelCalls warnings`) + }) }) test('Integration test blockBlockhash module', function (t: test.Test) { - t.plan(testFiles.length) - const module: any = modules.blockBlockhash - const lengthCheck: Record = { - 'KingOfTheEtherThrone.sol': 0, - 'assembly.sol': 0, - 'ballot.sol': 0, - 'ballot_reentrant.sol': 0, - 'ballot_withoutWarnings.sol': 0, - 'cross_contract.sol': 0, - 'inheritance.sol': 0, - 'modifier1.sol': 0, - 'modifier2.sol': 0, - 'notReentrant.sol': 0, - 'structReentrant.sol': 0, - 'thisLocal.sol': 0, - 'globals.sol': 1, - 'library.sol': 0, - 'transfer.sol': 0, - 'ctor.sol': 0, - 'forgottenReturn.sol': 0, - 'selfdestruct.sol': 0, - 'deleteDynamicArray.sol': 0, - 'deleteFromDynamicArray.sol': 0, - 'blockLevelCompare.sol': 0, - 'intDivisionTruncate.sol': 0, - 'ERC20.sol': 0, - 'stringBytesLength.sol': 0, - 'etherTransferInLoop.sol': 0, - 'forLoopIteratesOverDynamicArray.sol': 0 - } - runModuleOnFiles(module, t, (file: string, report: AnalysisReportObj[]) => { - t.equal(report.length, lengthCheck[file], `${file} has right amount of blockBlockhash warnings`) - }) + t.plan(testFiles.length) + const module: any = modules.blockBlockhash + const lengthCheck: Record = { + 'KingOfTheEtherThrone.sol': 0, + 'assembly.sol': 0, + 'ballot.sol': 0, + 'ballot_reentrant.sol': 0, + 'ballot_withoutWarnings.sol': 0, + 'cross_contract.sol': 0, + 'inheritance.sol': 0, + 'modifier1.sol': 0, + 'modifier2.sol': 0, + 'notReentrant.sol': 0, + 'structReentrant.sol': 0, + 'thisLocal.sol': 0, + 'globals.sol': 1, + 'library.sol': 0, + 'transfer.sol': 0, + 'ctor.sol': 0, + 'forgottenReturn.sol': 0, + 'selfdestruct.sol': 0, + 'deleteDynamicArray.sol': 0, + 'deleteFromDynamicArray.sol': 0, + 'blockLevelCompare.sol': 0, + 'intDivisionTruncate.sol': 0, + 'ERC20.sol': 0, + 'stringBytesLength.sol': 0, + 'etherTransferInLoop.sol': 0, + 'forLoopIteratesOverDynamicArray.sol': 0 + } + runModuleOnFiles(module, t, (file: string, report: AnalysisReportObj[]) => { + t.equal(report.length, lengthCheck[file], `${file} has right amount of blockBlockhash warnings`) + }) }) test('Integration test noReturn module', function (t: test.Test) { - t.plan(testFiles.length) - const module = modules.noReturn - const lengthCheck = { - 'KingOfTheEtherThrone.sol': 0, - 'assembly.sol': 1, - 'ballot.sol': 0, - 'ballot_reentrant.sol': 0, - 'ballot_withoutWarnings.sol': 0, - 'cross_contract.sol': 0, - 'inheritance.sol': 0, - 'modifier1.sol': 0, - 'modifier2.sol': 0, - 'notReentrant.sol': 0, - 'structReentrant.sol': 0, - 'thisLocal.sol': 1, - 'globals.sol': 0, - 'library.sol': 0, - 'transfer.sol': 0, - 'ctor.sol': 0, - 'forgottenReturn.sol': 1, - 'selfdestruct.sol': 0, - 'deleteDynamicArray.sol': 0, - 'deleteFromDynamicArray.sol': 0, - 'blockLevelCompare.sol': 0, - 'intDivisionTruncate.sol': 0, - 'ERC20.sol': 0, - 'stringBytesLength.sol': 0, - 'etherTransferInLoop.sol': 0, - 'forLoopIteratesOverDynamicArray.sol': 0 - } - runModuleOnFiles(module, t, (file: string, report: AnalysisReportObj[]) => { - t.equal(report.length, lengthCheck[file], `${file} has right amount of noReturn warnings`) - }) + t.plan(testFiles.length) + const module = modules.noReturn + const lengthCheck = { + 'KingOfTheEtherThrone.sol': 0, + 'assembly.sol': 1, + 'ballot.sol': 0, + 'ballot_reentrant.sol': 0, + 'ballot_withoutWarnings.sol': 0, + 'cross_contract.sol': 0, + 'inheritance.sol': 0, + 'modifier1.sol': 0, + 'modifier2.sol': 0, + 'notReentrant.sol': 0, + 'structReentrant.sol': 0, + 'thisLocal.sol': 1, + 'globals.sol': 0, + 'library.sol': 0, + 'transfer.sol': 0, + 'ctor.sol': 0, + 'forgottenReturn.sol': 1, + 'selfdestruct.sol': 0, + 'deleteDynamicArray.sol': 0, + 'deleteFromDynamicArray.sol': 0, + 'blockLevelCompare.sol': 0, + 'intDivisionTruncate.sol': 0, + 'ERC20.sol': 0, + 'stringBytesLength.sol': 0, + 'etherTransferInLoop.sol': 0, + 'forLoopIteratesOverDynamicArray.sol': 0 + } + runModuleOnFiles(module, t, (file: string, report: AnalysisReportObj[]) => { + t.equal(report.length, lengthCheck[file], `${file} has right amount of noReturn warnings`) + }) }) test('Integration test selfdestruct module', function (t: test.Test) { - t.plan(testFiles.length) - const module: any = modules.selfdestruct - const lengthCheck: Record = { - 'KingOfTheEtherThrone.sol': 0, - 'assembly.sol': 0, - 'ballot.sol': 0, - 'ballot_reentrant.sol': 0, - 'ballot_withoutWarnings.sol': 0, - 'cross_contract.sol': 0, - 'inheritance.sol': 0, - 'modifier1.sol': 0, - 'modifier2.sol': 0, - 'notReentrant.sol': 0, - 'structReentrant.sol': 0, - 'thisLocal.sol': 0, - 'globals.sol': 2, - 'library.sol': 0, - 'transfer.sol': 0, - 'ctor.sol': 0, - 'forgottenReturn.sol': 0, - 'selfdestruct.sol': 3, - 'deleteDynamicArray.sol': 0, - 'deleteFromDynamicArray.sol': 0, - 'blockLevelCompare.sol': 0, - 'ERC20.sol': 0, - 'intDivisionTruncate.sol': 5, - 'stringBytesLength.sol': 0, - 'etherTransferInLoop.sol': 0, - 'forLoopIteratesOverDynamicArray.sol': 0 - } - runModuleOnFiles(module, t, (file: string, report: AnalysisReportObj[]) => { - t.equal(report.length, lengthCheck[file], `${file} has right amount of selfdestruct warnings`) - }) + t.plan(testFiles.length) + const module: any = modules.selfdestruct + const lengthCheck: Record = { + 'KingOfTheEtherThrone.sol': 0, + 'assembly.sol': 0, + 'ballot.sol': 0, + 'ballot_reentrant.sol': 0, + 'ballot_withoutWarnings.sol': 0, + 'cross_contract.sol': 0, + 'inheritance.sol': 0, + 'modifier1.sol': 0, + 'modifier2.sol': 0, + 'notReentrant.sol': 0, + 'structReentrant.sol': 0, + 'thisLocal.sol': 0, + 'globals.sol': 2, + 'library.sol': 0, + 'transfer.sol': 0, + 'ctor.sol': 0, + 'forgottenReturn.sol': 0, + 'selfdestruct.sol': 3, + 'deleteDynamicArray.sol': 0, + 'deleteFromDynamicArray.sol': 0, + 'blockLevelCompare.sol': 0, + 'ERC20.sol': 0, + 'intDivisionTruncate.sol': 5, + 'stringBytesLength.sol': 0, + 'etherTransferInLoop.sol': 0, + 'forLoopIteratesOverDynamicArray.sol': 0 + } + runModuleOnFiles(module, t, (file: string, report: AnalysisReportObj[]) => { + t.equal(report.length, lengthCheck[file], `${file} has right amount of selfdestruct warnings`) + }) }) test('Integration test guardConditions module', function (t: test.Test) { - t.plan(testFiles.length) - const module: any = modules.guardConditions - const lengthCheck: Record = { - 'KingOfTheEtherThrone.sol': 0, - 'assembly.sol': 2, - 'ballot.sol': 0, - 'ballot_reentrant.sol': 0, - 'ballot_withoutWarnings.sol': 0, - 'cross_contract.sol': 0, - 'inheritance.sol': 0, - 'modifier1.sol': 0, - 'modifier2.sol': 0, - 'notReentrant.sol': 0, - 'structReentrant.sol': 0, - 'thisLocal.sol': 0, - 'globals.sol': 1, - 'library.sol': 0, - 'transfer.sol': 0, - 'ctor.sol': 0, - 'forgottenReturn.sol': 0, - 'selfdestruct.sol': 0, - 'deleteDynamicArray.sol': 1, - 'deleteFromDynamicArray.sol': 0, - 'blockLevelCompare.sol': 0, - 'intDivisionTruncate.sol': 1, - 'ERC20.sol': 0, - 'stringBytesLength.sol': 0, - 'etherTransferInLoop.sol': 0, - 'forLoopIteratesOverDynamicArray.sol': 0 - } - runModuleOnFiles(module, t, (file: string, report: AnalysisReportObj[]) => { - t.equal(report.length, lengthCheck[file], `${file} has right amount of guardConditions warnings`) - }) + t.plan(testFiles.length) + const module: any = modules.guardConditions + const lengthCheck: Record = { + 'KingOfTheEtherThrone.sol': 0, + 'assembly.sol': 2, + 'ballot.sol': 0, + 'ballot_reentrant.sol': 0, + 'ballot_withoutWarnings.sol': 0, + 'cross_contract.sol': 0, + 'inheritance.sol': 0, + 'modifier1.sol': 0, + 'modifier2.sol': 0, + 'notReentrant.sol': 0, + 'structReentrant.sol': 0, + 'thisLocal.sol': 0, + 'globals.sol': 1, + 'library.sol': 0, + 'transfer.sol': 0, + 'ctor.sol': 0, + 'forgottenReturn.sol': 0, + 'selfdestruct.sol': 0, + 'deleteDynamicArray.sol': 1, + 'deleteFromDynamicArray.sol': 0, + 'blockLevelCompare.sol': 0, + 'intDivisionTruncate.sol': 1, + 'ERC20.sol': 0, + 'stringBytesLength.sol': 0, + 'etherTransferInLoop.sol': 0, + 'forLoopIteratesOverDynamicArray.sol': 0 + } + runModuleOnFiles(module, t, (file: string, report: AnalysisReportObj[]) => { + t.equal(report.length, lengthCheck[file], `${file} has right amount of guardConditions warnings`) + }) }) test('Integration test deleteDynamicArrays module', function (t: test.Test) { - t.plan(testFiles.length) - const module: any = modules.deleteDynamicArrays - const lengthCheck: Record = { - 'KingOfTheEtherThrone.sol': 0, - 'assembly.sol': 0, - 'ballot.sol': 0, - 'ballot_reentrant.sol': 0, - 'ballot_withoutWarnings.sol': 0, - 'cross_contract.sol': 0, - 'inheritance.sol': 0, - 'modifier1.sol': 0, - 'modifier2.sol': 0, - 'notReentrant.sol': 0, - 'structReentrant.sol': 0, - 'thisLocal.sol': 0, - 'globals.sol': 0, - 'library.sol': 0, - 'transfer.sol': 0, - 'ctor.sol': 0, - 'forgottenReturn.sol': 0, - 'selfdestruct.sol': 0, - 'deleteDynamicArray.sol': 2, - 'deleteFromDynamicArray.sol': 0, - 'blockLevelCompare.sol': 0, - 'intDivisionTruncate.sol': 0, - 'ERC20.sol': 0, - 'stringBytesLength.sol': 0, - 'etherTransferInLoop.sol': 0, - 'forLoopIteratesOverDynamicArray.sol': 0 - } - runModuleOnFiles(module, t, (file: string, report: AnalysisReportObj[]) => { - t.equal(report.length, lengthCheck[file], `${file} has right amount of deleteDynamicArrays warnings`) - }) + t.plan(testFiles.length) + const module: any = modules.deleteDynamicArrays + const lengthCheck: Record = { + 'KingOfTheEtherThrone.sol': 0, + 'assembly.sol': 0, + 'ballot.sol': 0, + 'ballot_reentrant.sol': 0, + 'ballot_withoutWarnings.sol': 0, + 'cross_contract.sol': 0, + 'inheritance.sol': 0, + 'modifier1.sol': 0, + 'modifier2.sol': 0, + 'notReentrant.sol': 0, + 'structReentrant.sol': 0, + 'thisLocal.sol': 0, + 'globals.sol': 0, + 'library.sol': 0, + 'transfer.sol': 0, + 'ctor.sol': 0, + 'forgottenReturn.sol': 0, + 'selfdestruct.sol': 0, + 'deleteDynamicArray.sol': 2, + 'deleteFromDynamicArray.sol': 0, + 'blockLevelCompare.sol': 0, + 'intDivisionTruncate.sol': 0, + 'ERC20.sol': 0, + 'stringBytesLength.sol': 0, + 'etherTransferInLoop.sol': 0, + 'forLoopIteratesOverDynamicArray.sol': 0 + } + runModuleOnFiles(module, t, (file: string, report: AnalysisReportObj[]) => { + t.equal(report.length, lengthCheck[file], `${file} has right amount of deleteDynamicArrays warnings`) + }) }) test('Integration test deleteFromDynamicArray module', function (t: test.Test) { - t.plan(testFiles.length) - const module: any = modules.deleteFromDynamicArray - const lengthCheck: Record = { - 'KingOfTheEtherThrone.sol': 0, - 'assembly.sol': 0, - 'ballot.sol': 0, - 'ballot_reentrant.sol': 0, - 'ballot_withoutWarnings.sol': 0, - 'cross_contract.sol': 0, - 'inheritance.sol': 0, - 'modifier1.sol': 0, - 'modifier2.sol': 0, - 'notReentrant.sol': 0, - 'structReentrant.sol': 0, - 'thisLocal.sol': 0, - 'globals.sol': 0, - 'library.sol': 0, - 'transfer.sol': 0, - 'ctor.sol': 0, - 'forgottenReturn.sol': 0, - 'selfdestruct.sol': 0, - 'deleteDynamicArray.sol': 0, - 'deleteFromDynamicArray.sol': 1, - 'blockLevelCompare.sol': 0, - 'intDivisionTruncate.sol': 0, - 'ERC20.sol': 0, - 'stringBytesLength.sol': 0, - 'etherTransferInLoop.sol': 0, - 'forLoopIteratesOverDynamicArray.sol': 0 - } - runModuleOnFiles(module, t, (file: string, report: AnalysisReportObj[]) => { - t.equal(report.length, lengthCheck[file], `${file} has right amount of deleteFromDynamicArray warnings`) - }) + t.plan(testFiles.length) + const module: any = modules.deleteFromDynamicArray + const lengthCheck: Record = { + 'KingOfTheEtherThrone.sol': 0, + 'assembly.sol': 0, + 'ballot.sol': 0, + 'ballot_reentrant.sol': 0, + 'ballot_withoutWarnings.sol': 0, + 'cross_contract.sol': 0, + 'inheritance.sol': 0, + 'modifier1.sol': 0, + 'modifier2.sol': 0, + 'notReentrant.sol': 0, + 'structReentrant.sol': 0, + 'thisLocal.sol': 0, + 'globals.sol': 0, + 'library.sol': 0, + 'transfer.sol': 0, + 'ctor.sol': 0, + 'forgottenReturn.sol': 0, + 'selfdestruct.sol': 0, + 'deleteDynamicArray.sol': 0, + 'deleteFromDynamicArray.sol': 1, + 'blockLevelCompare.sol': 0, + 'intDivisionTruncate.sol': 0, + 'ERC20.sol': 0, + 'stringBytesLength.sol': 0, + 'etherTransferInLoop.sol': 0, + 'forLoopIteratesOverDynamicArray.sol': 0 + } + runModuleOnFiles(module, t, (file: string, report: AnalysisReportObj[]) => { + t.equal(report.length, lengthCheck[file], `${file} has right amount of deleteFromDynamicArray warnings`) + }) }) test('Integration test assignAndCompare module', function (t: test.Test) { - t.plan(testFiles.length) - const module: any = modules.assignAndCompare - const lengthCheck: Record = { - 'KingOfTheEtherThrone.sol': 0, - 'assembly.sol': 0, - 'ballot.sol': 0, - 'ballot_reentrant.sol': 0, - 'ballot_withoutWarnings.sol': 0, - 'cross_contract.sol': 0, - 'inheritance.sol': 0, - 'modifier1.sol': 0, - 'modifier2.sol': 0, - 'notReentrant.sol': 0, - 'structReentrant.sol': 0, - 'thisLocal.sol': 0, - 'globals.sol': 0, - 'library.sol': 0, - 'transfer.sol': 0, - 'ctor.sol': 0, - 'forgottenReturn.sol': 0, - 'selfdestruct.sol': 0, - 'deleteDynamicArray.sol': 0, - 'deleteFromDynamicArray.sol': 0, - 'blockLevelCompare.sol': 8, - 'intDivisionTruncate.sol': 0, - 'ERC20.sol': 0, - 'stringBytesLength.sol': 0, - 'etherTransferInLoop.sol': 0, - 'forLoopIteratesOverDynamicArray.sol': 0 - } - runModuleOnFiles(module, t, (file: string, report: AnalysisReportObj[]) => { - t.equal(report.length, lengthCheck[file], `${file} has right amount of assignAndCompare warnings`) - }) + t.plan(testFiles.length) + const module: any = modules.assignAndCompare + const lengthCheck: Record = { + 'KingOfTheEtherThrone.sol': 0, + 'assembly.sol': 0, + 'ballot.sol': 0, + 'ballot_reentrant.sol': 0, + 'ballot_withoutWarnings.sol': 0, + 'cross_contract.sol': 0, + 'inheritance.sol': 0, + 'modifier1.sol': 0, + 'modifier2.sol': 0, + 'notReentrant.sol': 0, + 'structReentrant.sol': 0, + 'thisLocal.sol': 0, + 'globals.sol': 0, + 'library.sol': 0, + 'transfer.sol': 0, + 'ctor.sol': 0, + 'forgottenReturn.sol': 0, + 'selfdestruct.sol': 0, + 'deleteDynamicArray.sol': 0, + 'deleteFromDynamicArray.sol': 0, + 'blockLevelCompare.sol': 8, + 'intDivisionTruncate.sol': 0, + 'ERC20.sol': 0, + 'stringBytesLength.sol': 0, + 'etherTransferInLoop.sol': 0, + 'forLoopIteratesOverDynamicArray.sol': 0 + } + runModuleOnFiles(module, t, (file: string, report: AnalysisReportObj[]) => { + t.equal(report.length, lengthCheck[file], `${file} has right amount of assignAndCompare warnings`) + }) }) test('Integration test intDivisionTruncate module', function (t: test.Test) { - t.plan(testFiles.length) - const module: any = modules.intDivisionTruncate - const lengthCheck: Record = { - 'KingOfTheEtherThrone.sol': 0, - 'assembly.sol': 0, - 'ballot.sol': 0, - 'ballot_reentrant.sol': 0, - 'ballot_withoutWarnings.sol': 0, - 'cross_contract.sol': 0, - 'inheritance.sol': 0, - 'modifier1.sol': 0, - 'modifier2.sol': 0, - 'notReentrant.sol': 0, - 'structReentrant.sol': 0, - 'thisLocal.sol': 0, - 'globals.sol': 0, - 'library.sol': 0, - 'transfer.sol': 0, - 'ctor.sol': 0, - 'forgottenReturn.sol': 0, - 'selfdestruct.sol': 0, - 'deleteDynamicArray.sol': 0, - 'deleteFromDynamicArray.sol': 0, - 'blockLevelCompare.sol': 0, - 'intDivisionTruncate.sol': 2, - 'ERC20.sol': 0, - 'stringBytesLength.sol': 0, - 'etherTransferInLoop.sol': 0, - 'forLoopIteratesOverDynamicArray.sol': 0 - } - runModuleOnFiles(module, t, (file: string, report: AnalysisReportObj[]) => { - t.equal(report.length, lengthCheck[file], `${file} has right amount of intDivisionTruncate warnings`) - }) + t.plan(testFiles.length) + const module: any = modules.intDivisionTruncate + const lengthCheck: Record = { + 'KingOfTheEtherThrone.sol': 0, + 'assembly.sol': 0, + 'ballot.sol': 0, + 'ballot_reentrant.sol': 0, + 'ballot_withoutWarnings.sol': 0, + 'cross_contract.sol': 0, + 'inheritance.sol': 0, + 'modifier1.sol': 0, + 'modifier2.sol': 0, + 'notReentrant.sol': 0, + 'structReentrant.sol': 0, + 'thisLocal.sol': 0, + 'globals.sol': 0, + 'library.sol': 0, + 'transfer.sol': 0, + 'ctor.sol': 0, + 'forgottenReturn.sol': 0, + 'selfdestruct.sol': 0, + 'deleteDynamicArray.sol': 0, + 'deleteFromDynamicArray.sol': 0, + 'blockLevelCompare.sol': 0, + 'intDivisionTruncate.sol': 2, + 'ERC20.sol': 0, + 'stringBytesLength.sol': 0, + 'etherTransferInLoop.sol': 0, + 'forLoopIteratesOverDynamicArray.sol': 0 + } + runModuleOnFiles(module, t, (file: string, report: AnalysisReportObj[]) => { + t.equal(report.length, lengthCheck[file], `${file} has right amount of intDivisionTruncate warnings`) + }) }) test('Integration test erc20Decimal module', function (t: test.Test) { - t.plan(testFiles.length) - const module: any = modules.erc20Decimals - const lengthCheck: Record = { - 'KingOfTheEtherThrone.sol': 0, - 'assembly.sol': 0, - 'ballot.sol': 0, - 'ballot_reentrant.sol': 0, - 'ballot_withoutWarnings.sol': 0, - 'cross_contract.sol': 0, - 'inheritance.sol': 0, - 'modifier1.sol': 0, - 'modifier2.sol': 0, - 'notReentrant.sol': 0, - 'structReentrant.sol': 0, - 'thisLocal.sol': 0, - 'globals.sol': 0, - 'library.sol': 0, - 'transfer.sol': 0, - 'ctor.sol': 0, - 'forgottenReturn.sol': 0, - 'selfdestruct.sol': 0, - 'deleteDynamicArray.sol': 0, - 'deleteFromDynamicArray.sol': 0, - 'blockLevelCompare.sol': 0, - 'intDivisionTruncate.sol': 0, - 'ERC20.sol': 1, - 'stringBytesLength.sol': 0, - 'etherTransferInLoop.sol': 0, - 'forLoopIteratesOverDynamicArray.sol': 0 - } - runModuleOnFiles(module, t, (file: string, report: AnalysisReportObj[]) => { - t.equal(report.length, lengthCheck[file], `${file} has right amount of erc20Decimals warnings`) - }) + t.plan(testFiles.length) + const module: any = modules.erc20Decimals + const lengthCheck: Record = { + 'KingOfTheEtherThrone.sol': 0, + 'assembly.sol': 0, + 'ballot.sol': 0, + 'ballot_reentrant.sol': 0, + 'ballot_withoutWarnings.sol': 0, + 'cross_contract.sol': 0, + 'inheritance.sol': 0, + 'modifier1.sol': 0, + 'modifier2.sol': 0, + 'notReentrant.sol': 0, + 'structReentrant.sol': 0, + 'thisLocal.sol': 0, + 'globals.sol': 0, + 'library.sol': 0, + 'transfer.sol': 0, + 'ctor.sol': 0, + 'forgottenReturn.sol': 0, + 'selfdestruct.sol': 0, + 'deleteDynamicArray.sol': 0, + 'deleteFromDynamicArray.sol': 0, + 'blockLevelCompare.sol': 0, + 'intDivisionTruncate.sol': 0, + 'ERC20.sol': 1, + 'stringBytesLength.sol': 0, + 'etherTransferInLoop.sol': 0, + 'forLoopIteratesOverDynamicArray.sol': 0 + } + runModuleOnFiles(module, t, (file: string, report: AnalysisReportObj[]) => { + t.equal(report.length, lengthCheck[file], `${file} has right amount of erc20Decimals warnings`) + }) }) test('Integration test stringBytesLength module', function (t: test.Test) { - t.plan(testFiles.length) - const module: any = modules.stringBytesLength - const lengthCheck: Record = { - 'KingOfTheEtherThrone.sol': 0, - 'assembly.sol': 0, - 'ballot.sol': 0, - 'ballot_reentrant.sol': 0, - 'ballot_withoutWarnings.sol': 0, - 'cross_contract.sol': 0, - 'inheritance.sol': 0, - 'modifier1.sol': 0, - 'modifier2.sol': 0, - 'notReentrant.sol': 0, - 'structReentrant.sol': 0, - 'thisLocal.sol': 0, - 'globals.sol': 0, - 'library.sol': 0, - 'transfer.sol': 0, - 'ctor.sol': 0, - 'forgottenReturn.sol': 0, - 'selfdestruct.sol': 0, - 'deleteDynamicArray.sol': 0, - 'deleteFromDynamicArray.sol': 0, - 'blockLevelCompare.sol': 0, - 'intDivisionTruncate.sol': 0, - 'ERC20.sol': 0, - 'stringBytesLength.sol': 1, - 'etherTransferInLoop.sol': 0, - 'forLoopIteratesOverDynamicArray.sol': 0 - } - runModuleOnFiles(module, t, (file: string, report: AnalysisReportObj[]) => { - t.equal(report.length, lengthCheck[file], `${file} has right amount of stringBytesLength warnings`) - }) + t.plan(testFiles.length) + const module: any = modules.stringBytesLength + const lengthCheck: Record = { + 'KingOfTheEtherThrone.sol': 0, + 'assembly.sol': 0, + 'ballot.sol': 0, + 'ballot_reentrant.sol': 0, + 'ballot_withoutWarnings.sol': 0, + 'cross_contract.sol': 0, + 'inheritance.sol': 0, + 'modifier1.sol': 0, + 'modifier2.sol': 0, + 'notReentrant.sol': 0, + 'structReentrant.sol': 0, + 'thisLocal.sol': 0, + 'globals.sol': 0, + 'library.sol': 0, + 'transfer.sol': 0, + 'ctor.sol': 0, + 'forgottenReturn.sol': 0, + 'selfdestruct.sol': 0, + 'deleteDynamicArray.sol': 0, + 'deleteFromDynamicArray.sol': 0, + 'blockLevelCompare.sol': 0, + 'intDivisionTruncate.sol': 0, + 'ERC20.sol': 0, + 'stringBytesLength.sol': 1, + 'etherTransferInLoop.sol': 0, + 'forLoopIteratesOverDynamicArray.sol': 0 + } + runModuleOnFiles(module, t, (file: string, report: AnalysisReportObj[]) => { + t.equal(report.length, lengthCheck[file], `${file} has right amount of stringBytesLength warnings`) + }) }) test('Integration test etherTransferInLoop module', function (t: test.Test) { - t.plan(testFiles.length) - const module: any = modules.etherTransferInLoop - const lengthCheck: Record = { - 'KingOfTheEtherThrone.sol': 0, - 'assembly.sol': 0, - 'ballot.sol': 0, - 'ballot_reentrant.sol': 0, - 'ballot_withoutWarnings.sol': 0, - 'cross_contract.sol': 0, - 'inheritance.sol': 0, - 'modifier1.sol': 0, - 'modifier2.sol': 0, - 'notReentrant.sol': 0, - 'structReentrant.sol': 0, - 'thisLocal.sol': 0, - 'globals.sol': 0, - 'library.sol': 0, - 'transfer.sol': 0, - 'ctor.sol': 0, - 'forgottenReturn.sol': 0, - 'selfdestruct.sol': 0, - 'deleteDynamicArray.sol': 0, - 'deleteFromDynamicArray.sol': 0, - 'blockLevelCompare.sol': 0, - 'intDivisionTruncate.sol': 0, - 'ERC20.sol': 0, - 'stringBytesLength.sol': 0, - 'etherTransferInLoop.sol': 3, - 'forLoopIteratesOverDynamicArray.sol': 0 - } - runModuleOnFiles(module, t, (file: string, report: AnalysisReportObj[]) => { - t.equal(report.length, lengthCheck[file], `${file} has right amount of etherTransferInLoop warnings`) - }) + t.plan(testFiles.length) + const module: any = modules.etherTransferInLoop + const lengthCheck: Record = { + 'KingOfTheEtherThrone.sol': 0, + 'assembly.sol': 0, + 'ballot.sol': 0, + 'ballot_reentrant.sol': 0, + 'ballot_withoutWarnings.sol': 0, + 'cross_contract.sol': 0, + 'inheritance.sol': 0, + 'modifier1.sol': 0, + 'modifier2.sol': 0, + 'notReentrant.sol': 0, + 'structReentrant.sol': 0, + 'thisLocal.sol': 0, + 'globals.sol': 0, + 'library.sol': 0, + 'transfer.sol': 0, + 'ctor.sol': 0, + 'forgottenReturn.sol': 0, + 'selfdestruct.sol': 0, + 'deleteDynamicArray.sol': 0, + 'deleteFromDynamicArray.sol': 0, + 'blockLevelCompare.sol': 0, + 'intDivisionTruncate.sol': 0, + 'ERC20.sol': 0, + 'stringBytesLength.sol': 0, + 'etherTransferInLoop.sol': 3, + 'forLoopIteratesOverDynamicArray.sol': 0 + } + runModuleOnFiles(module, t, (file: string, report: AnalysisReportObj[]) => { + t.equal(report.length, lengthCheck[file], `${file} has right amount of etherTransferInLoop warnings`) + }) }) test('Integration test forLoopIteratesOverDynamicArray module', function (t: test.Test) { - t.plan(testFiles.length) - const module = modules.forLoopIteratesOverDynamicArray - const lengthCheck: Record = { - 'KingOfTheEtherThrone.sol': 0, - 'assembly.sol': 0, - 'ballot.sol': 2, - 'ballot_reentrant.sol': 1, - 'ballot_withoutWarnings.sol': 0, - 'cross_contract.sol': 0, - 'inheritance.sol': 0, - 'modifier1.sol': 0, - 'modifier2.sol': 0, - 'notReentrant.sol': 0, - 'structReentrant.sol': 0, - 'thisLocal.sol': 0, - 'globals.sol': 0, - 'library.sol': 0, - 'transfer.sol': 0, - 'ctor.sol': 0, - 'forgottenReturn.sol': 0, - 'selfdestruct.sol': 0, - 'deleteDynamicArray.sol': 0, - 'deleteFromDynamicArray.sol': 0, - 'blockLevelCompare.sol': 0, - 'intDivisionTruncate.sol': 0, - 'ERC20.sol': 0, - 'stringBytesLength.sol': 0, - 'etherTransferInLoop.sol': 0, - 'forLoopIteratesOverDynamicArray.sol': 2 - } - runModuleOnFiles(module, t, (file: string, report: AnalysisReportObj[]) => { - t.equal(report.length, lengthCheck[file], `${file} has right amount of forLoopIteratesOverDynamicArray warnings`) - }) + t.plan(testFiles.length) + const module = modules.forLoopIteratesOverDynamicArray + const lengthCheck: Record = { + 'KingOfTheEtherThrone.sol': 0, + 'assembly.sol': 0, + 'ballot.sol': 2, + 'ballot_reentrant.sol': 1, + 'ballot_withoutWarnings.sol': 0, + 'cross_contract.sol': 0, + 'inheritance.sol': 0, + 'modifier1.sol': 0, + 'modifier2.sol': 0, + 'notReentrant.sol': 0, + 'structReentrant.sol': 0, + 'thisLocal.sol': 0, + 'globals.sol': 0, + 'library.sol': 0, + 'transfer.sol': 0, + 'ctor.sol': 0, + 'forgottenReturn.sol': 0, + 'selfdestruct.sol': 0, + 'deleteDynamicArray.sol': 0, + 'deleteFromDynamicArray.sol': 0, + 'blockLevelCompare.sol': 0, + 'intDivisionTruncate.sol': 0, + 'ERC20.sol': 0, + 'stringBytesLength.sol': 0, + 'etherTransferInLoop.sol': 0, + 'forLoopIteratesOverDynamicArray.sol': 2 + } + runModuleOnFiles(module, t, (file: string, report: AnalysisReportObj[]) => { + t.equal(report.length, lengthCheck[file], `${file} has right amount of forLoopIteratesOverDynamicArray warnings`) + }) }) // #################### Helpers function runModuleOnFiles (Module: any, t: test.Test, cb: ((fname: string, report: AnalysisReportObj[]) => void)): void { - const statRunner: StatRunner = new StatRunner() - testFiles.forEach((fileName: string) => { - const reports = statRunner.runWithModuleList(compilationResults[fileName], [{ name: new Module().name, mod: new Module() }]) - const report: AnalysisReportObj[] = reports[0].report - if (report.some((x: AnalysisReportObj) => x['warning'].includes('INTERNAL ERROR'))) { - t.comment('Error while executing Module: ' + JSON.stringify(report)) - } - cb(fileName, report) - }) + const statRunner: StatRunner = new StatRunner() + testFiles.forEach((fileName: string) => { + const reports = statRunner.runWithModuleList(compilationResults[fileName], [{ name: new Module().name, mod: new Module() }]) + const report: AnalysisReportObj[] = reports[0].report + if (report.some((x: AnalysisReportObj) => x['warning'].includes('INTERNAL ERROR'))) { + t.comment('Error while executing Module: ' + JSON.stringify(report)) + } + cb(fileName, report) + }) } diff --git a/libs/remix-analyzer/test/analysis/staticAnalysisIssues-test-0.4.24.ts b/libs/remix-analyzer/test/analysis/staticAnalysisIssues-test-0.4.24.ts index 2a6091dc32..e7c738d210 100644 --- a/libs/remix-analyzer/test/analysis/staticAnalysisIssues-test-0.4.24.ts +++ b/libs/remix-analyzer/test/analysis/staticAnalysisIssues-test-0.4.24.ts @@ -11,29 +11,29 @@ const folder: string = 'solidity-v0.4.24' let compiler test('setup', function (t) { - solc.loadRemoteVersion('v0.4.24+commit.e67f0147', (error, solcVersion) => { - if (error) throw error - compiler = solcVersion - t.end() - }) + solc.loadRemoteVersion('v0.4.24+commit.e67f0147', (error, solcVersion) => { + if (error) throw error + compiler = solcVersion + t.end() + }) }); function compile (fileName: string): CompilationResult { - const content: string = readFileSync(join(__dirname, 'test-contracts/' + folder, fileName), 'utf8') - return JSON.parse(compiler.compile(compilerInput(content))) + const content: string = readFileSync(join(__dirname, 'test-contracts/' + folder, fileName), 'utf8') + return JSON.parse(compiler.compile(compilerInput(content))) } test('staticAnalysisIssues.functionParameterPassingError', function (t) { - // https://github.com/ethereum/remix-ide/issues/889#issuecomment-351746474 - t.plan(2) - const res: CompilationResult = compile('functionParameters.sol') - const Module: any = checksEffectsInteraction - const statRunner: StatRunner = new StatRunner() + // https://github.com/ethereum/remix-ide/issues/889#issuecomment-351746474 + t.plan(2) + const res: CompilationResult = compile('functionParameters.sol') + const Module: any = checksEffectsInteraction + const statRunner: StatRunner = new StatRunner() - t.doesNotThrow(() => { - statRunner.runWithModuleList(res, [{ name: new Module().name, mod: new Module()}]) - }, 'Analysis should not throw') + t.doesNotThrow(() => { + statRunner.runWithModuleList(res, [{ name: new Module().name, mod: new Module()}]) + }, 'Analysis should not throw') - const reports = statRunner.runWithModuleList(res, [{ name: new Module().name, mod: new Module()}]) - t.ok(!reports.some((mod: AnalysisReport) => mod.report.some((rep: AnalysisReportObj) => rep.warning.includes('INTERNAL ERROR')), 'Should not have internal errors')) + const reports = statRunner.runWithModuleList(res, [{ name: new Module().name, mod: new Module()}]) + t.ok(!reports.some((mod: AnalysisReport) => mod.report.some((rep: AnalysisReportObj) => rep.warning.includes('INTERNAL ERROR')), 'Should not have internal errors')) }) diff --git a/libs/remix-analyzer/test/analysis/staticAnalysisIssues-test-0.5.0.ts b/libs/remix-analyzer/test/analysis/staticAnalysisIssues-test-0.5.0.ts index 43234e4168..caac55106f 100644 --- a/libs/remix-analyzer/test/analysis/staticAnalysisIssues-test-0.5.0.ts +++ b/libs/remix-analyzer/test/analysis/staticAnalysisIssues-test-0.5.0.ts @@ -11,29 +11,29 @@ const folder: string = 'solidity-v0.5' let compiler test('setup', function (t) { - solc.loadRemoteVersion('v0.5.0+commit.1d4f565a', (error, solcVersion) => { - if (error) throw error - compiler = solcVersion - t.end() - }) + solc.loadRemoteVersion('v0.5.0+commit.1d4f565a', (error, solcVersion) => { + if (error) throw error + compiler = solcVersion + t.end() + }) }); function compile (fileName): CompilationResult { - const content: string = readFileSync(join(__dirname, 'test-contracts/' + folder, fileName), 'utf8') - return JSON.parse(compiler.compile(compilerInput(content))) + const content: string = readFileSync(join(__dirname, 'test-contracts/' + folder, fileName), 'utf8') + return JSON.parse(compiler.compile(compilerInput(content))) } test('staticAnalysisIssues.functionParameterPassingError', function (t) { - // https://github.com/ethereum/remix-ide/issues/889#issuecomment-351746474 - t.plan(2) - const res: CompilationResult = compile('functionParameters.sol') - const Module: any = checksEffectsInteraction - const statRunner: StatRunner = new StatRunner() + // https://github.com/ethereum/remix-ide/issues/889#issuecomment-351746474 + t.plan(2) + const res: CompilationResult = compile('functionParameters.sol') + const Module: any = checksEffectsInteraction + const statRunner: StatRunner = new StatRunner() - t.doesNotThrow(() => { - statRunner.runWithModuleList(res, [{ name: new Module().name, mod: new Module() }]) - }, 'Analysis should not throw') + t.doesNotThrow(() => { + statRunner.runWithModuleList(res, [{ name: new Module().name, mod: new Module() }]) + }, 'Analysis should not throw') - const reports = statRunner.runWithModuleList(res, [{ name: new Module().name, mod: new Module() }]) - t.ok(!reports.some((mod: AnalysisReport) => mod.report.some((rep: AnalysisReportObj) => rep.warning.includes('INTERNAL ERROR')), 'Should not have internal errors')) + const reports = statRunner.runWithModuleList(res, [{ name: new Module().name, mod: new Module() }]) + t.ok(!reports.some((mod: AnalysisReport) => mod.report.some((rep: AnalysisReportObj) => rep.warning.includes('INTERNAL ERROR')), 'Should not have internal errors')) }) diff --git a/libs/remix-astwalker/src/astWalker.ts b/libs/remix-astwalker/src/astWalker.ts index 853ff647b5..439d8ff094 100644 --- a/libs/remix-astwalker/src/astWalker.ts +++ b/libs/remix-astwalker/src/astWalker.ts @@ -6,24 +6,24 @@ export declare interface AstWalker { } const isObject = function (obj: any): boolean { - return obj != null && obj.constructor.name === 'Object' + return obj != null && obj.constructor.name === 'Object' } export function isAstNode (node: Record): boolean { - return ( - isObject(node) && + return ( + isObject(node) && 'id' in node && 'nodeType' in node && 'src' in node - ) + ) } export function isYulAstNode (node: Record): boolean { - return ( - isObject(node) && + return ( + isObject(node) && 'nodeType' in node && 'src' in node - ) + ) } /** @@ -41,54 +41,54 @@ export function isYulAstNode (node: Record): boolean { */ // eslint-disable-next-line no-redeclare export class AstWalker extends EventEmitter { - manageCallback ( - node: AstNode, - callback: Record | Function // eslint-disable-line @typescript-eslint/ban-types - ): any { - // FIXME: we shouldn't be doing this callback determination type on each AST node, - // since the callback function is set once per walk. - // Better would be to store the right one as a variable and - // return that. - if (node) { - if ((node).name in callback) { - return callback[(node).name](node) - } else { - return callback['*'](node) - } - } - if (node) { - if ((node).nodeType in callback) { - /* istanbul ignore next */ - return callback[(node).nodeType](node) - } else { - /* istanbul ignore next */ - return callback['*'](node) - } - } + manageCallback ( + node: AstNode, + callback: Record | Function // eslint-disable-line @typescript-eslint/ban-types + ): any { + // FIXME: we shouldn't be doing this callback determination type on each AST node, + // since the callback function is set once per walk. + // Better would be to store the right one as a variable and + // return that. + if (node) { + if ((node).name in callback) { + return callback[(node).name](node) + } else { + return callback['*'](node) + } } - - normalizeNodes (nodes: AstNode[]): AstNode[] { - // Remove null, undefined and empty elements if any - nodes = nodes.filter(e => e) - - // If any element in nodes is array, extract its members - const objNodes = [] - nodes.forEach(x => { - if (Array.isArray(x)) objNodes.push(...x) - else objNodes.push(x) - }) - - // Filter duplicate nodes using id field - const normalizedNodes = [] - objNodes.forEach((element) => { - const firstIndex = normalizedNodes.findIndex(e => e.id === element.id) - if (firstIndex === -1) normalizedNodes.push(element) - }) - return normalizedNodes + if (node) { + if ((node).nodeType in callback) { + /* istanbul ignore next */ + return callback[(node).nodeType](node) + } else { + /* istanbul ignore next */ + return callback['*'](node) + } } - - getASTNodeChildren (ast: AstNode): AstNode[] { - let nodes = ast.nodes || // for ContractDefinition + } + + normalizeNodes (nodes: AstNode[]): AstNode[] { + // Remove null, undefined and empty elements if any + nodes = nodes.filter(e => e) + + // If any element in nodes is array, extract its members + const objNodes = [] + nodes.forEach(x => { + if (Array.isArray(x)) objNodes.push(...x) + else objNodes.push(x) + }) + + // Filter duplicate nodes using id field + const normalizedNodes = [] + objNodes.forEach((element) => { + const firstIndex = normalizedNodes.findIndex(e => e.id === element.id) + if (firstIndex === -1) normalizedNodes.push(element) + }) + return normalizedNodes + } + + getASTNodeChildren (ast: AstNode): AstNode[] { + let nodes = ast.nodes || // for ContractDefinition ast.body || // for FunctionDefinition, ModifierDefinition, WhileStatement, DoWhileStatement, ForStatement ast.statements || // for Block, YulBlock ast.members || // for StructDefinition, EnumDefinition @@ -101,149 +101,149 @@ export class AstWalker extends EventEmitter { ast.eventCall || // for EmitStatement [] - // If 'nodes' is not an array, convert it into one, for example: ast.body - if (nodes && !Array.isArray(nodes)) { - const tempArr = [] - tempArr.push(nodes) - nodes = tempArr - } - - // To break object referencing - nodes = [...nodes] - - if (ast.nodes && ast.baseContracts?.length) { // for ContractDefinition - nodes.push(...ast.baseContracts) - } else if (ast.body && ast.overrides && ast.parameters && ast.returnParameters && ast.modifiers) { // for FunctionDefinition - nodes.push(ast.overrides) - nodes.push(ast.parameters) - nodes.push(ast.returnParameters) - nodes.push(ast.modifiers) - } else if (ast.typeName) { // for VariableDeclaration, NewExpression, ElementaryTypeNameExpression - nodes.push(ast.typeName) - } else if (ast.body && ast.overrides && ast.parameters) { // for ModifierDefinition - nodes.push(ast.overrides) - nodes.push(ast.parameters) - } else if (ast.modifierName && ast.arguments) { // for ModifierInvocation - nodes.push(ast.modifierName) - nodes.push(ast.arguments) - } else if (ast.parameterTypes && ast.returnParameterTypes) { // for ModifierInvocation - nodes.push(ast.parameterTypes) - nodes.push(ast.returnParameterTypes) - } else if (ast.keyType && ast.valueType) { // for Mapping - nodes.push(ast.keyType) - nodes.push(ast.valueType) - } else if (ast.baseType && ast.length) { // for ArrayTypeName - nodes.push(ast.baseType) - nodes.push(ast.length) - } else if (ast.AST) { // for InlineAssembly - nodes.push(ast.AST) - } else if (ast.condition && (ast.trueBody || ast.falseBody || ast.body)) { // for IfStatement, WhileStatement, DoWhileStatement - nodes.push(ast.condition) - nodes.push(ast.trueBody) - nodes.push(ast.falseBody) - } else if (ast.parameters && ast.block) { // for TryCatchClause - nodes.push(ast.block) - } else if (ast.externalCall && ast.clauses) { // for TryStatement - nodes.push(ast.externalCall) - nodes.push(ast.clauses) - } else if (ast.body && ast.condition && ast.initializationExpression && ast.loopExpression) { // for ForStatement - nodes.push(ast.condition) - nodes.push(ast.initializationExpression) - nodes.push(ast.loopExpression) - } else if (ast.declarations && ast.initialValue) { // for VariableDeclarationStatement - nodes.push(ast.initialValue) - } else if (ast.condition && (ast.trueExpression || ast.falseExpression)) { // for Conditional - nodes.push(ast.condition) - nodes.push(ast.trueExpression) - nodes.push(ast.falseExpression) - } else if (ast.leftHandSide && ast.rightHandSide) { // for Assignment - nodes.push(ast.leftHandSide) - nodes.push(ast.rightHandSide) - } else if (ast.leftExpression && ast.rightExpression) { // for BinaryOperation - nodes.push(ast.leftExpression) - nodes.push(ast.rightExpression) - } else if (ast.expression && (ast.arguments || ast.options)) { // for FunctionCall, FunctionCallOptions - nodes.push(ast.arguments ? ast.arguments : ast.options) - } else if (ast.baseExpression && (ast.indexExpression || (ast.startExpression && ast.endExpression))) { // for IndexAccess, IndexRangeAccess - nodes.push(ast.baseExpression) - if (ast.indexExpression) nodes.push(ast.indexExpression) - else { - nodes.push(ast.startExpression) - nodes.push(ast.endExpression) - } - } - return this.normalizeNodes(nodes) + // If 'nodes' is not an array, convert it into one, for example: ast.body + if (nodes && !Array.isArray(nodes)) { + const tempArr = [] + tempArr.push(nodes) + nodes = tempArr } - // eslint-disable-next-line @typescript-eslint/ban-types, @typescript-eslint/explicit-module-boundary-types - walk (ast: AstNode, callback?: Function | Record) { - if (ast) { - const children: AstNode[] = this.getASTNodeChildren(ast) - if (callback) { - if (callback instanceof Function) { - callback = Object({ '*': callback }) - } - if (!('*' in callback)) { - callback['*'] = function () { - return true - } - } - if (this.manageCallback(ast, callback) && children?.length) { - for (const k in children) { - const child = children[k] - this.walk(child, callback) - } - } - } else { - if (children?.length) { - for (const k in children) { - const child = children[k] - this.emit('node', child) - this.walk(child) - } - } - } - } + // To break object referencing + nodes = [...nodes] + + if (ast.nodes && ast.baseContracts?.length) { // for ContractDefinition + nodes.push(...ast.baseContracts) + } else if (ast.body && ast.overrides && ast.parameters && ast.returnParameters && ast.modifiers) { // for FunctionDefinition + nodes.push(ast.overrides) + nodes.push(ast.parameters) + nodes.push(ast.returnParameters) + nodes.push(ast.modifiers) + } else if (ast.typeName) { // for VariableDeclaration, NewExpression, ElementaryTypeNameExpression + nodes.push(ast.typeName) + } else if (ast.body && ast.overrides && ast.parameters) { // for ModifierDefinition + nodes.push(ast.overrides) + nodes.push(ast.parameters) + } else if (ast.modifierName && ast.arguments) { // for ModifierInvocation + nodes.push(ast.modifierName) + nodes.push(ast.arguments) + } else if (ast.parameterTypes && ast.returnParameterTypes) { // for ModifierInvocation + nodes.push(ast.parameterTypes) + nodes.push(ast.returnParameterTypes) + } else if (ast.keyType && ast.valueType) { // for Mapping + nodes.push(ast.keyType) + nodes.push(ast.valueType) + } else if (ast.baseType && ast.length) { // for ArrayTypeName + nodes.push(ast.baseType) + nodes.push(ast.length) + } else if (ast.AST) { // for InlineAssembly + nodes.push(ast.AST) + } else if (ast.condition && (ast.trueBody || ast.falseBody || ast.body)) { // for IfStatement, WhileStatement, DoWhileStatement + nodes.push(ast.condition) + nodes.push(ast.trueBody) + nodes.push(ast.falseBody) + } else if (ast.parameters && ast.block) { // for TryCatchClause + nodes.push(ast.block) + } else if (ast.externalCall && ast.clauses) { // for TryStatement + nodes.push(ast.externalCall) + nodes.push(ast.clauses) + } else if (ast.body && ast.condition && ast.initializationExpression && ast.loopExpression) { // for ForStatement + nodes.push(ast.condition) + nodes.push(ast.initializationExpression) + nodes.push(ast.loopExpression) + } else if (ast.declarations && ast.initialValue) { // for VariableDeclarationStatement + nodes.push(ast.initialValue) + } else if (ast.condition && (ast.trueExpression || ast.falseExpression)) { // for Conditional + nodes.push(ast.condition) + nodes.push(ast.trueExpression) + nodes.push(ast.falseExpression) + } else if (ast.leftHandSide && ast.rightHandSide) { // for Assignment + nodes.push(ast.leftHandSide) + nodes.push(ast.rightHandSide) + } else if (ast.leftExpression && ast.rightExpression) { // for BinaryOperation + nodes.push(ast.leftExpression) + nodes.push(ast.rightExpression) + } else if (ast.expression && (ast.arguments || ast.options)) { // for FunctionCall, FunctionCallOptions + nodes.push(ast.arguments ? ast.arguments : ast.options) + } else if (ast.baseExpression && (ast.indexExpression || (ast.startExpression && ast.endExpression))) { // for IndexAccess, IndexRangeAccess + nodes.push(ast.baseExpression) + if (ast.indexExpression) nodes.push(ast.indexExpression) + else { + nodes.push(ast.startExpression) + nodes.push(ast.endExpression) + } } - - // eslint-disable-next-line @typescript-eslint/ban-types, @typescript-eslint/explicit-module-boundary-types - walkFullInternal (ast: AstNode, callback: Function) { - if (isAstNode(ast) || isYulAstNode(ast)) { - // console.log(`XXX id ${ast.id}, nodeType: ${ast.nodeType}, src: ${ast.src}`); - callback(ast) - for (const k of Object.keys(ast)) { - // Possible optimization: - // if (k in ['id', 'src', 'nodeType']) continue; - const astItem = ast[k] - if (Array.isArray(astItem)) { - for (const child of astItem) { - if (child) { - this.walkFullInternal(child, callback) - } - } - } else { - this.walkFullInternal(astItem, callback) - } - } + return this.normalizeNodes(nodes) + } + + // eslint-disable-next-line @typescript-eslint/ban-types, @typescript-eslint/explicit-module-boundary-types + walk (ast: AstNode, callback?: Function | Record) { + if (ast) { + const children: AstNode[] = this.getASTNodeChildren(ast) + if (callback) { + if (callback instanceof Function) { + callback = Object({ '*': callback }) } + if (!('*' in callback)) { + callback['*'] = function () { + return true + } + } + if (this.manageCallback(ast, callback) && children?.length) { + for (const k in children) { + const child = children[k] + this.walk(child, callback) + } + } + } else { + if (children?.length) { + for (const k in children) { + const child = children[k] + this.emit('node', child) + this.walk(child) + } + } + } } - - // Normalizes parameter callback and calls walkFullInternal - // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types - walkFull (ast: AstNode, callback: any) { - if (isAstNode(ast) || isYulAstNode(ast)) return this.walkFullInternal(ast, callback) - } - - // eslint-disable-next-line @typescript-eslint/ban-types, @typescript-eslint/explicit-module-boundary-types - walkAstList (sourcesList: Node, cb?: Function) { - if (cb) { - if (sourcesList.ast) { - this.walk(sourcesList.ast, cb) + } + + // eslint-disable-next-line @typescript-eslint/ban-types, @typescript-eslint/explicit-module-boundary-types + walkFullInternal (ast: AstNode, callback: Function) { + if (isAstNode(ast) || isYulAstNode(ast)) { + // console.log(`XXX id ${ast.id}, nodeType: ${ast.nodeType}, src: ${ast.src}`); + callback(ast) + for (const k of Object.keys(ast)) { + // Possible optimization: + // if (k in ['id', 'src', 'nodeType']) continue; + const astItem = ast[k] + if (Array.isArray(astItem)) { + for (const child of astItem) { + if (child) { + this.walkFullInternal(child, callback) } + } } else { - if (sourcesList.ast) { - this.walk(sourcesList.ast) - } + this.walkFullInternal(astItem, callback) } + } + } + } + + // Normalizes parameter callback and calls walkFullInternal + // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types + walkFull (ast: AstNode, callback: any) { + if (isAstNode(ast) || isYulAstNode(ast)) return this.walkFullInternal(ast, callback) + } + + // eslint-disable-next-line @typescript-eslint/ban-types, @typescript-eslint/explicit-module-boundary-types + walkAstList (sourcesList: Node, cb?: Function) { + if (cb) { + if (sourcesList.ast) { + this.walk(sourcesList.ast, cb) + } + } else { + if (sourcesList.ast) { + this.walk(sourcesList.ast) + } } + } } diff --git a/libs/remix-astwalker/src/sourceMappings.ts b/libs/remix-astwalker/src/sourceMappings.ts index 869f495946..2886b2caf6 100644 --- a/libs/remix-astwalker/src/sourceMappings.ts +++ b/libs/remix-astwalker/src/sourceMappings.ts @@ -13,15 +13,15 @@ export declare interface SourceMappings { * @param offset The character offset to convert. */ export function lineColPositionFromOffset (offset: number, lineBreaks: Array): LineColPosition { - let line: number = util.findLowerBound(offset, lineBreaks) - if (lineBreaks[line] !== offset) { - line += 1 - } - const beginColumn = line === 0 ? 0 : (lineBreaks[line - 1] + 1) - return { - line: line + 1, - character: (offset - beginColumn) + 1 - } + let line: number = util.findLowerBound(offset, lineBreaks) + if (lineBreaks[line] !== offset) { + line += 1 + } + const beginColumn = line === 0 ? 0 : (lineBreaks[line - 1] + 1) + return { + line: line + 1, + character: (offset - beginColumn) + 1 + } } /** @@ -31,10 +31,10 @@ export function lineColPositionFromOffset (offset: number, lineBreaks: Array{ - start: parseInt(split[0], 10), - length: parseInt(split[1], 10), - file: parseInt(split[2], 10) - } + const split = src.split(':') + return { + start: parseInt(split[0], 10), + length: parseInt(split[1], 10), + file: parseInt(split[2], 10) + } } /** @@ -60,90 +60,90 @@ export function sourceLocationFromSrc (src: string): Location { */ // eslint-disable-next-line no-redeclare export class SourceMappings { - readonly source: string; - readonly lineBreaks: Array; + readonly source: string; + readonly lineBreaks: Array; - constructor (source: string) { - this.source = source + constructor (source: string) { + this.source = source - // Create a list of line offsets which will be used to map between - // character offset and line/column positions. - const lineBreaks: Array = [] - for (let pos = source.indexOf('\n'); pos >= 0; pos = source.indexOf('\n', pos + 1)) { - lineBreaks.push(pos) - } - this.lineBreaks = lineBreaks + // Create a list of line offsets which will be used to map between + // character offset and line/column positions. + const lineBreaks: Array = [] + for (let pos = source.indexOf('\n'); pos >= 0; pos = source.indexOf('\n', pos + 1)) { + lineBreaks.push(pos) } + this.lineBreaks = lineBreaks + } - /** + /** * Get a list of nodes that are at the given "position". * * @param astNodeType Type of node to return or null. * @param position Character offset where AST node should be located. */ - nodesAtPosition (astNodeType: string | null, position: Location, ast: AstNode): Array { - const astWalker = new AstWalker() - const found: Array = [] + nodesAtPosition (astNodeType: string | null, position: Location, ast: AstNode): Array { + const astWalker = new AstWalker() + const found: Array = [] - const callback = function (node: AstNode): boolean { - const nodeLocation = sourceLocationFromAstNode(node) - if (nodeLocation && + const callback = function (node: AstNode): boolean { + const nodeLocation = sourceLocationFromAstNode(node) + if (nodeLocation && nodeLocation.start === position.start && nodeLocation.length === position.length) { - if (!astNodeType || astNodeType === node.nodeType) { - found.push(node) - } - } - return true + if (!astNodeType || astNodeType === node.nodeType) { + found.push(node) } - astWalker.walkFull(ast, callback) - return found + } + return true } + astWalker.walkFull(ast, callback) + return found + } - /** + /** * Retrieve the first "astNodeType" that includes the source map at arg instIndex, or "null" if none found. * * @param astNodeType nodeType that a found ASTNode must be. Use "null" if any ASTNode can match. * @param sourceLocation "src" location that the AST node must match. */ - findNodeAtSourceLocation (astNodeType: string | undefined, sourceLocation: Location, ast: AstNode | null): AstNode | null { - const astWalker = new AstWalker() - let found = null - /* FIXME: Looking at AST walker code, + findNodeAtSourceLocation (astNodeType: string | undefined, sourceLocation: Location, ast: AstNode | null): AstNode | null { + const astWalker = new AstWalker() + let found = null + /* FIXME: Looking at AST walker code, I don't understand a need to return a boolean. */ - const callback = function (node: AstNode) { - const nodeLocation = sourceLocationFromAstNode(node) - if (nodeLocation && + const callback = function (node: AstNode) { + const nodeLocation = sourceLocationFromAstNode(node) + if (nodeLocation && nodeLocation.start === sourceLocation.start && nodeLocation.length === sourceLocation.length) { - if (astNodeType === undefined || astNodeType === node.nodeType) { - found = node - } - } - return true + if (astNodeType === undefined || astNodeType === node.nodeType) { + found = node } - - astWalker.walkFull(ast, callback) - return found + } + return true } - /** + astWalker.walkFull(ast, callback) + return found + } + + /** * Retrieve the line/column range position for the given source-mapping string. * * @param src Solc "src" object containing attributes {source} and {length}. */ - srcToLineColumnRange (src: string): LineColRange { - const sourceLocation = sourceLocationFromSrc(src) - if (sourceLocation.start >= 0 && sourceLocation.length >= 0) { - return { - start: lineColPositionFromOffset(sourceLocation.start, this.lineBreaks), - end: lineColPositionFromOffset(sourceLocation.start + sourceLocation.length, this.lineBreaks) - } - } else { - return { - start: null, - end: null - } - } + srcToLineColumnRange (src: string): LineColRange { + const sourceLocation = sourceLocationFromSrc(src) + if (sourceLocation.start >= 0 && sourceLocation.length >= 0) { + return { + start: lineColPositionFromOffset(sourceLocation.start, this.lineBreaks), + end: lineColPositionFromOffset(sourceLocation.start + sourceLocation.length, this.lineBreaks) + } + } else { + return { + start: null, + end: null + } } + } } diff --git a/libs/remix-astwalker/tests/newTests.ts b/libs/remix-astwalker/tests/newTests.ts index 3db5320202..b3521bcb8e 100644 --- a/libs/remix-astwalker/tests/newTests.ts +++ b/libs/remix-astwalker/tests/newTests.ts @@ -4,157 +4,157 @@ import node from "./resources/newAST"; import legacyNode from "./resources/legacyAST"; tape("New ASTWalker", (t: tape.Test) => { - // New Ast Object - const astWalker = new AstWalker(); - const latestASTNode = JSON.parse(JSON.stringify(node.ast)) - t.test("ASTWalker.walk && .walkastList", (st: tape.Test) => { - const latestAST = JSON.parse(JSON.stringify(latestASTNode)) - st.plan(24); - // EventListener - astWalker.on("node", node => { - if (node.nodeType === "ContractDefinition") { - checkContract(st, node); - } - - if (node.nodeType === "PragmaDirective") { - checkProgramDirective(st, node); - } - }); - - // Callback pattern - astWalker.walk(latestAST, (node: AstNode) => { - if (node.nodeType === "ContractDefinition") { - checkContract(st, node); - } - - if (node.nodeType === "PragmaDirective") { - checkProgramDirective(st, node); - } - }); - - // Callback Object - const callback: any = {}; - callback.FunctionDefinition = function(node: AstNode): boolean { - st.equal(node.name, "FunctionDefinition"); - - return true; - }; - // Calling walk function with cb - astWalker.walk(latestAST, callback); - - // Calling walk function without cb - astWalker.walk(latestAST); - - // Calling WALKASTLIST function - astWalker.walkAstList(node); - - // Calling WALKASTLIST function with cb - astWalker.walkAstList(node, node => { - return true; - }); - st.end(); + // New Ast Object + const astWalker = new AstWalker(); + const latestASTNode = JSON.parse(JSON.stringify(node.ast)) + t.test("ASTWalker.walk && .walkastList", (st: tape.Test) => { + const latestAST = JSON.parse(JSON.stringify(latestASTNode)) + st.plan(24); + // EventListener + astWalker.on("node", node => { + if (node.nodeType === "ContractDefinition") { + checkContract(st, node); + } + + if (node.nodeType === "PragmaDirective") { + checkProgramDirective(st, node); + } }); - t.test("ASTWalker.getASTNodeChildren", (st: tape.Test) => { - const latestAST = JSON.parse(JSON.stringify(latestASTNode)) - st.plan(26); - st.equal(latestAST.nodeType, 'SourceUnit') + // Callback pattern + astWalker.walk(latestAST, (node: AstNode) => { + if (node.nodeType === "ContractDefinition") { + checkContract(st, node); + } - const subNodes1 = astWalker.getASTNodeChildren(latestAST) + if (node.nodeType === "PragmaDirective") { + checkProgramDirective(st, node); + } + }); - st.equal(subNodes1.length, 3) - st.equal(subNodes1[0].nodeType, 'PragmaDirective') - st.equal(subNodes1[1].nodeType, 'ImportDirective') - st.equal(subNodes1[2].nodeType, 'ContractDefinition') + // Callback Object + const callback: any = {}; + callback.FunctionDefinition = function(node: AstNode): boolean { + st.equal(node.name, "FunctionDefinition"); - let subNodes2 = astWalker.getASTNodeChildren(subNodes1[0]) - st.equal(subNodes2.length, 0) + return true; + }; + // Calling walk function with cb + astWalker.walk(latestAST, callback); - subNodes2 = astWalker.getASTNodeChildren(subNodes1[1]) - st.equal(subNodes2.length, 0) + // Calling walk function without cb + astWalker.walk(latestAST); - subNodes2 = astWalker.getASTNodeChildren(subNodes1[2]) - st.equal(subNodes2.length, 4) - st.equal(subNodes2[0].nodeType, 'VariableDeclaration') - st.equal(subNodes2[1].nodeType, 'FunctionDefinition') - st.equal(subNodes2[2].nodeType, 'FunctionDefinition') - st.equal(subNodes2[3].nodeType, 'InheritanceSpecifier') + // Calling WALKASTLIST function + astWalker.walkAstList(node); - let subNodes3 = astWalker.getASTNodeChildren(subNodes2[0]) - st.equal(subNodes3.length, 1) - st.equal(subNodes3[0].nodeType, 'ElementaryTypeName') + // Calling WALKASTLIST function with cb + astWalker.walkAstList(node, node => { + return true; + }); + st.end(); + }); - let subNodes4 = astWalker.getASTNodeChildren(subNodes3[0]) - st.equal(subNodes4.length, 0) + t.test("ASTWalker.getASTNodeChildren", (st: tape.Test) => { + const latestAST = JSON.parse(JSON.stringify(latestASTNode)) + st.plan(26); + st.equal(latestAST.nodeType, 'SourceUnit') - subNodes3 = astWalker.getASTNodeChildren(subNodes2[1]) - st.equal(subNodes3.length, 1) - st.equal(subNodes3[0].nodeType, 'Block') + const subNodes1 = astWalker.getASTNodeChildren(latestAST) - subNodes4 = astWalker.getASTNodeChildren(subNodes3[0]) - st.equal(subNodes4.length, 1) - st.equal(subNodes4[0].nodeType, 'ExpressionStatement') + st.equal(subNodes1.length, 3) + st.equal(subNodes1[0].nodeType, 'PragmaDirective') + st.equal(subNodes1[1].nodeType, 'ImportDirective') + st.equal(subNodes1[2].nodeType, 'ContractDefinition') - const subNodes5 = astWalker.getASTNodeChildren(subNodes4[0]) - st.equal(subNodes5.length, 1) - st.equal(subNodes5[0].nodeType, 'Assignment') + let subNodes2 = astWalker.getASTNodeChildren(subNodes1[0]) + st.equal(subNodes2.length, 0) - const subNodes6 = astWalker.getASTNodeChildren(subNodes5[0]) + subNodes2 = astWalker.getASTNodeChildren(subNodes1[1]) + st.equal(subNodes2.length, 0) - st.equal(subNodes6.length, 2) - st.equal(subNodes6[0].nodeType, 'Identifier') - st.equal(subNodes6[1].nodeType, 'Identifier') + subNodes2 = astWalker.getASTNodeChildren(subNodes1[2]) + st.equal(subNodes2.length, 4) + st.equal(subNodes2[0].nodeType, 'VariableDeclaration') + st.equal(subNodes2[1].nodeType, 'FunctionDefinition') + st.equal(subNodes2[2].nodeType, 'FunctionDefinition') + st.equal(subNodes2[3].nodeType, 'InheritanceSpecifier') - let subNodes7 = astWalker.getASTNodeChildren(subNodes6[0]) - st.equal(subNodes7.length, 0) + let subNodes3 = astWalker.getASTNodeChildren(subNodes2[0]) + st.equal(subNodes3.length, 1) + st.equal(subNodes3[0].nodeType, 'ElementaryTypeName') - subNodes7 = astWalker.getASTNodeChildren(subNodes6[1]) - st.equal(subNodes7.length, 0) + let subNodes4 = astWalker.getASTNodeChildren(subNodes3[0]) + st.equal(subNodes4.length, 0) - st.end(); - }); + subNodes3 = astWalker.getASTNodeChildren(subNodes2[1]) + st.equal(subNodes3.length, 1) + st.equal(subNodes3[0].nodeType, 'Block') + + subNodes4 = astWalker.getASTNodeChildren(subNodes3[0]) + st.equal(subNodes4.length, 1) + st.equal(subNodes4[0].nodeType, 'ExpressionStatement') + + const subNodes5 = astWalker.getASTNodeChildren(subNodes4[0]) + st.equal(subNodes5.length, 1) + st.equal(subNodes5[0].nodeType, 'Assignment') + + const subNodes6 = astWalker.getASTNodeChildren(subNodes5[0]) - t.test("ASTWalkFull", (st: tape.Test) => { - const latestAST = JSON.parse(JSON.stringify(latestASTNode)) - const astNodeCount = 26; - st.plan(2 + astNodeCount); - let count: number = 0; - astWalker.walkFull(latestAST, (node: AstNode) => { - st.ok(isAstNode(node), "passed an ast node"); - count += 1; - }); - st.equal(count, astNodeCount, "traverses all AST nodes"); - count = 0; - const badCall = function() { - /* Typescript will keep us from calling walkFull with a legacyAST. + st.equal(subNodes6.length, 2) + st.equal(subNodes6[0].nodeType, 'Identifier') + st.equal(subNodes6[1].nodeType, 'Identifier') + + let subNodes7 = astWalker.getASTNodeChildren(subNodes6[0]) + st.equal(subNodes7.length, 0) + + subNodes7 = astWalker.getASTNodeChildren(subNodes6[1]) + st.equal(subNodes7.length, 0) + + st.end(); + }); + + t.test("ASTWalkFull", (st: tape.Test) => { + const latestAST = JSON.parse(JSON.stringify(latestASTNode)) + const astNodeCount = 26; + st.plan(2 + astNodeCount); + let count: number = 0; + astWalker.walkFull(latestAST, (node: AstNode) => { + st.ok(isAstNode(node), "passed an ast node"); + count += 1; + }); + st.equal(count, astNodeCount, "traverses all AST nodes"); + count = 0; + const badCall = function() { + /* Typescript will keep us from calling walkFull with a legacyAST. However, for non-typescript uses, we add this test which casts to an AST to check that there is a run-time check in walkFull. */ - astWalker.walkFull(legacyNode, (node: AstNode) => { - count += 1; - }); - } - // t.throws(badCall, /first argument should be an ast/, - // "passing legacyAST fails"); - st.equal(count, 0, "traverses no AST nodes"); - st.end(); - }); + astWalker.walkFull(legacyNode, (node: AstNode) => { + count += 1; + }); + } + // t.throws(badCall, /first argument should be an ast/, + // "passing legacyAST fails"); + st.equal(count, 0, "traverses no AST nodes"); + st.end(); + }); }); function checkProgramDirective(st: tape.Test, node: AstNode) { - st.equal(node.id, 1); - st.equal(node.literals.length, 7); + st.equal(node.id, 1); + st.equal(node.literals.length, 7); } function checkContract(st: tape.Test, node: AstNode) { - st.equal(node.name, "Greeter"); - st.equal(node.nodes[0].name, "greeting"); - st.equal(node.nodes[0].nodeType, "VariableDeclaration"); - st.equal(node.nodes[0].name, "greeting"); - st.equal(node.nodes[0].typeName.name, "string"); - st.equal(node.nodes[1].nodeType, "FunctionDefinition"); - st.equal(node.nodes[1].name, ""); - st.equal(node.nodes[1].scope, 25); - st.equal(node.nodes[2].nodeType, "FunctionDefinition"); - st.equal(node.nodes[2].name, "greet"); + st.equal(node.name, "Greeter"); + st.equal(node.nodes[0].name, "greeting"); + st.equal(node.nodes[0].nodeType, "VariableDeclaration"); + st.equal(node.nodes[0].name, "greeting"); + st.equal(node.nodes[0].typeName.name, "string"); + st.equal(node.nodes[1].nodeType, "FunctionDefinition"); + st.equal(node.nodes[1].name, ""); + st.equal(node.nodes[1].scope, 25); + st.equal(node.nodes[2].nodeType, "FunctionDefinition"); + st.equal(node.nodes[2].name, "greet"); } diff --git a/libs/remix-astwalker/tests/resources/ast.ts b/libs/remix-astwalker/tests/resources/ast.ts index d20b63e7bf..55ddc9cb24 100644 --- a/libs/remix-astwalker/tests/resources/ast.ts +++ b/libs/remix-astwalker/tests/resources/ast.ts @@ -1,7 +1,7 @@ import { Node } from '../../src/' const node = { - ast: { "legacyAST": { "children": [{ "attributes": { "fullyImplemented": true, "isLibrary": false, "linearizedBaseContracts": [5640396], "name": "test" }, "children": [{ "attributes": { "name": "x", "type": "int256" }, "children": [{ "attributes": { "name": "int" }, "id": 5657860, "name": "ElementaryTypeName", "src": "21:3:11" }], "id": 5658100, "name": "VariableDeclaration", "src": "21:5:11" }, { "attributes": { "name": "y", "type": "int256" }, "children": [{ "attributes": { "name": "int" }, "id": 5658180, "name": "ElementaryTypeName", "src": "38:3:11" }], "id": 5658268, "name": "VariableDeclaration", "src": "38:5:11" }, { "attributes": { "constant": false, "name": "set", "public": true }, "children": [{ "children": [{ "attributes": { "name": "_x", "type": "int256" }, "children": [{ "attributes": { "name": "int" }, "id": 5658404, "name": "ElementaryTypeName", "src": "68:3:11" }], "id": 5658492, "name": "VariableDeclaration", "src": "68:6:11" }], "id": 5658572, "name": "ParameterList", "src": "67:8:11" }, { "children": [{ "attributes": { "name": "_r", "type": "int256" }, "children": [{ "attributes": { "name": "int" }, "id": 5658628, "name": "ElementaryTypeName", "src": "85:3:11" }], "id": 5658716, "name": "VariableDeclaration", "src": "85:6:11" }], "id": 5658796, "name": "ParameterList", "src": "84:8:11" }, { "children": [{ "children": [{ "attributes": { "operator": "=", "type": "int256" }, "children": [{ "attributes": { "type": "int256", "value": "x" }, "id": 5658900, "name": "Identifier", "src": "108:1:11" }, { "attributes": { "type": "int256", "value": "_x" }, "id": 5658980, "name": "Identifier", "src": "112:2:11" }], "id": 5657492, "name": "Assignment", "src": "108:6:11" }], "id": 5659028, "name": "ExpressionStatement", "src": "108:6:11" }, { "children": [{ "attributes": { "operator": "=", "type": "int256" }, "children": [{ "attributes": { "type": "int256", "value": "y" }, "id": 5659116, "name": "Identifier", "src": "125:1:11" }, { "attributes": { "string": null, "type": "int_const 10", "value": "10" }, "id": 5659196, "name": "Literal", "src": "129:2:11" }], "id": 5659252, "name": "Assignment", "src": "125:6:11" }], "id": 5659316, "name": "ExpressionStatement", "src": "125:6:11" }, { "children": [{ "attributes": { "operator": "=", "type": "int256" }, "children": [{ "attributes": { "type": "int256", "value": "_r" }, "id": 5659428, "name": "Identifier", "src": "141:2:11" }, { "attributes": { "type": "int256", "value": "x" }, "id": 5639308, "name": "Identifier", "src": "146:1:11" }], "id": 5639356, "name": "Assignment", "src": "141:6:11" }], "id": 5639420, "name": "ExpressionStatement", "src": "141:6:11" }], "id": 5639516, "name": "Block", "src": "97:57:11" }], "id": 5639612, "name": "FunctionDefinition", "src": "55:99:11" }, { "attributes": { "constant": false, "name": "get", "public": true }, "children": [{ "children": [], "id": 5639764, "name": "ParameterList", "src": "179:2:11" }, { "children": [{ "attributes": { "name": "x", "type": "uint256" }, "children": [{ "attributes": { "name": "uint" }, "id": 5639820, "name": "ElementaryTypeName", "src": "191:4:11" }], "id": 5639908, "name": "VariableDeclaration", "src": "191:6:11" }, { "attributes": { "name": "y", "type": "uint256" }, "children": [{ "attributes": { "name": "uint" }, "id": 5639988, "name": "ElementaryTypeName", "src": "199:4:11" }], "id": 5640076, "name": "VariableDeclaration", "src": "199:6:11" }], "id": 5640156, "name": "ParameterList", "src": "190:16:11" }, { "children": [], "id": 5640212, "name": "Block", "src": "212:17:11" }], "id": 5640276, "name": "FunctionDefinition", "src": "167:62:11" }], "id": 5640396, "name": "ContractDefinition", "src": "0:231:11" }], "name": "SourceUnit" } }, - source: `contract test { + ast: { "legacyAST": { "children": [{ "attributes": { "fullyImplemented": true, "isLibrary": false, "linearizedBaseContracts": [5640396], "name": "test" }, "children": [{ "attributes": { "name": "x", "type": "int256" }, "children": [{ "attributes": { "name": "int" }, "id": 5657860, "name": "ElementaryTypeName", "src": "21:3:11" }], "id": 5658100, "name": "VariableDeclaration", "src": "21:5:11" }, { "attributes": { "name": "y", "type": "int256" }, "children": [{ "attributes": { "name": "int" }, "id": 5658180, "name": "ElementaryTypeName", "src": "38:3:11" }], "id": 5658268, "name": "VariableDeclaration", "src": "38:5:11" }, { "attributes": { "constant": false, "name": "set", "public": true }, "children": [{ "children": [{ "attributes": { "name": "_x", "type": "int256" }, "children": [{ "attributes": { "name": "int" }, "id": 5658404, "name": "ElementaryTypeName", "src": "68:3:11" }], "id": 5658492, "name": "VariableDeclaration", "src": "68:6:11" }], "id": 5658572, "name": "ParameterList", "src": "67:8:11" }, { "children": [{ "attributes": { "name": "_r", "type": "int256" }, "children": [{ "attributes": { "name": "int" }, "id": 5658628, "name": "ElementaryTypeName", "src": "85:3:11" }], "id": 5658716, "name": "VariableDeclaration", "src": "85:6:11" }], "id": 5658796, "name": "ParameterList", "src": "84:8:11" }, { "children": [{ "children": [{ "attributes": { "operator": "=", "type": "int256" }, "children": [{ "attributes": { "type": "int256", "value": "x" }, "id": 5658900, "name": "Identifier", "src": "108:1:11" }, { "attributes": { "type": "int256", "value": "_x" }, "id": 5658980, "name": "Identifier", "src": "112:2:11" }], "id": 5657492, "name": "Assignment", "src": "108:6:11" }], "id": 5659028, "name": "ExpressionStatement", "src": "108:6:11" }, { "children": [{ "attributes": { "operator": "=", "type": "int256" }, "children": [{ "attributes": { "type": "int256", "value": "y" }, "id": 5659116, "name": "Identifier", "src": "125:1:11" }, { "attributes": { "string": null, "type": "int_const 10", "value": "10" }, "id": 5659196, "name": "Literal", "src": "129:2:11" }], "id": 5659252, "name": "Assignment", "src": "125:6:11" }], "id": 5659316, "name": "ExpressionStatement", "src": "125:6:11" }, { "children": [{ "attributes": { "operator": "=", "type": "int256" }, "children": [{ "attributes": { "type": "int256", "value": "_r" }, "id": 5659428, "name": "Identifier", "src": "141:2:11" }, { "attributes": { "type": "int256", "value": "x" }, "id": 5639308, "name": "Identifier", "src": "146:1:11" }], "id": 5639356, "name": "Assignment", "src": "141:6:11" }], "id": 5639420, "name": "ExpressionStatement", "src": "141:6:11" }], "id": 5639516, "name": "Block", "src": "97:57:11" }], "id": 5639612, "name": "FunctionDefinition", "src": "55:99:11" }, { "attributes": { "constant": false, "name": "get", "public": true }, "children": [{ "children": [], "id": 5639764, "name": "ParameterList", "src": "179:2:11" }, { "children": [{ "attributes": { "name": "x", "type": "uint256" }, "children": [{ "attributes": { "name": "uint" }, "id": 5639820, "name": "ElementaryTypeName", "src": "191:4:11" }], "id": 5639908, "name": "VariableDeclaration", "src": "191:6:11" }, { "attributes": { "name": "y", "type": "uint256" }, "children": [{ "attributes": { "name": "uint" }, "id": 5639988, "name": "ElementaryTypeName", "src": "199:4:11" }], "id": 5640076, "name": "VariableDeclaration", "src": "199:6:11" }], "id": 5640156, "name": "ParameterList", "src": "190:16:11" }, { "children": [], "id": 5640212, "name": "Block", "src": "212:17:11" }], "id": 5640276, "name": "FunctionDefinition", "src": "167:62:11" }], "id": 5640396, "name": "ContractDefinition", "src": "0:231:11" }], "name": "SourceUnit" } }, + source: `contract test { int x; int y; diff --git a/libs/remix-astwalker/tests/resources/newAST.ts b/libs/remix-astwalker/tests/resources/newAST.ts index cda95ae756..c2aaf08c69 100644 --- a/libs/remix-astwalker/tests/resources/newAST.ts +++ b/libs/remix-astwalker/tests/resources/newAST.ts @@ -1,293 +1,293 @@ import { Node } from '../../src/' const node: Node = { - "ast": + "ast": { - "absolutePath": "greeter.sol", - "exportedSymbols": { - "Greeter": [ - 25 - ] + "absolutePath": "greeter.sol", + "exportedSymbols": { + "Greeter": [ + 25 + ] + }, + "id": 26, + "nodeType": "SourceUnit", + "nodes": [ + { + "id": 1, + "literals": [ + "solidity", + ">=", + "0.5", + ".0", + "<", + "0.6", + ".0" + ], + "nodeType": "PragmaDirective", + "src": "0:31:0" + }, + { + "absolutePath": "mortal.sol", + "file": "mortal.sol", + "id": 2, + "nodeType": "ImportDirective", + "scope": 26, + "sourceUnit": 53, + "src": "32:20:0", + "symbolAliases": [], + "unitAlias": "" }, - "id": 26, - "nodeType": "SourceUnit", - "nodes": [ + { + "baseContracts": [ { - "id": 1, - "literals": [ - "solidity", - ">=", - "0.5", - ".0", - "<", - "0.6", - ".0" - ], - "nodeType": "PragmaDirective", - "src": "0:31:0" - }, + "arguments": null, + "baseName": { + "contractScope": null, + "id": 3, + "name": "Mortal", + "nodeType": "UserDefinedTypeName", + "referencedDeclaration": 52, + "src": "74:6:0", + "typeDescriptions": { + "typeIdentifier": "t_contract$_Mortal_$52", + "typeString": "contract Mortal" + } + }, + "id": 4, + "nodeType": "InheritanceSpecifier", + "src": "74:6:0" + } + ], + "contractDependencies": [ + 52 + ], + "contractKind": "contract", + "documentation": null, + "fullyImplemented": true, + "id": 25, + "linearizedBaseContracts": [ + 25, + 52 + ], + "name": "Greeter", + "nodeType": "ContractDefinition", + "nodes": [ { - "absolutePath": "mortal.sol", - "file": "mortal.sol", - "id": 2, - "nodeType": "ImportDirective", - "scope": 26, - "sourceUnit": 53, - "src": "32:20:0", - "symbolAliases": [], - "unitAlias": "" + "constant": false, + "id": 6, + "name": "greeting", + "nodeType": "VariableDeclaration", + "scope": 25, + "src": "141:15:0", + "stateVariable": true, + "storageLocation": "default", + "typeDescriptions": { + "typeIdentifier": "t_string_storage", + "typeString": "string" + }, + "typeName": { + "id": 5, + "name": "string", + "nodeType": "ElementaryTypeName", + "src": "141:6:0", + "typeDescriptions": { + "typeIdentifier": "t_string_storage_ptr", + "typeString": "string" + } + }, + "value": null, + "visibility": "internal" }, { - "baseContracts": [ - { - "arguments": null, - "baseName": { - "contractScope": null, - "id": 3, - "name": "Mortal", - "nodeType": "UserDefinedTypeName", - "referencedDeclaration": 52, - "src": "74:6:0", - "typeDescriptions": { - "typeIdentifier": "t_contract$_Mortal_$52", - "typeString": "contract Mortal" - } - }, - "id": 4, - "nodeType": "InheritanceSpecifier", - "src": "74:6:0" - } - ], - "contractDependencies": [ - 52 - ], - "contractKind": "contract", - "documentation": null, - "fullyImplemented": true, - "id": 25, - "linearizedBaseContracts": [ - 25, - 52 - ], - "name": "Greeter", - "nodeType": "ContractDefinition", - "nodes": [ - { - "constant": false, - "id": 6, + "body": { + "id": 15, + "nodeType": "Block", + "src": "257:37:0", + "statements": [ + { + "expression": { + "argumentTypes": null, + "id": 13, + "isConstant": false, + "isLValue": false, + "isPure": false, + "lValueRequested": false, + "leftHandSide": { + "argumentTypes": null, + "id": 11, "name": "greeting", - "nodeType": "VariableDeclaration", - "scope": 25, - "src": "141:15:0", - "stateVariable": true, - "storageLocation": "default", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": 6, + "src": "267:8:0", "typeDescriptions": { - "typeIdentifier": "t_string_storage", - "typeString": "string" - }, - "typeName": { - "id": 5, - "name": "string", - "nodeType": "ElementaryTypeName", - "src": "141:6:0", - "typeDescriptions": { - "typeIdentifier": "t_string_storage_ptr", - "typeString": "string" - } - }, - "value": null, - "visibility": "internal" + "typeIdentifier": "t_string_storage", + "typeString": "string storage ref" + } + }, + "nodeType": "Assignment", + "operator": "=", + "rightHandSide": { + "argumentTypes": null, + "id": 12, + "name": "_greeting", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": 8, + "src": "278:9:0", + "typeDescriptions": { + "typeIdentifier": "t_string_memory_ptr", + "typeString": "string memory" + } + }, + "src": "267:20:0", + "typeDescriptions": { + "typeIdentifier": "t_string_storage", + "typeString": "string storage ref" + } + }, + "id": 14, + "nodeType": "ExpressionStatement", + "src": "267:20:0" + } + ] + }, + "documentation": null, + "id": 16, + "implemented": true, + "kind": "constructor", + "modifiers": [], + "name": "", + "nodeType": "FunctionDefinition", + "parameters": { + "id": 9, + "nodeType": "ParameterList", + "parameters": [ + { + "constant": false, + "id": 8, + "name": "_greeting", + "nodeType": "VariableDeclaration", + "scope": 16, + "src": "225:23:0", + "stateVariable": false, + "storageLocation": "memory", + "typeDescriptions": { + "typeIdentifier": "t_string_memory_ptr", + "typeString": "string" }, - { - "body": { - "id": 15, - "nodeType": "Block", - "src": "257:37:0", - "statements": [ - { - "expression": { - "argumentTypes": null, - "id": 13, - "isConstant": false, - "isLValue": false, - "isPure": false, - "lValueRequested": false, - "leftHandSide": { - "argumentTypes": null, - "id": 11, - "name": "greeting", - "nodeType": "Identifier", - "overloadedDeclarations": [], - "referencedDeclaration": 6, - "src": "267:8:0", - "typeDescriptions": { - "typeIdentifier": "t_string_storage", - "typeString": "string storage ref" - } - }, - "nodeType": "Assignment", - "operator": "=", - "rightHandSide": { - "argumentTypes": null, - "id": 12, - "name": "_greeting", - "nodeType": "Identifier", - "overloadedDeclarations": [], - "referencedDeclaration": 8, - "src": "278:9:0", - "typeDescriptions": { - "typeIdentifier": "t_string_memory_ptr", - "typeString": "string memory" - } - }, - "src": "267:20:0", - "typeDescriptions": { - "typeIdentifier": "t_string_storage", - "typeString": "string storage ref" - } - }, - "id": 14, - "nodeType": "ExpressionStatement", - "src": "267:20:0" - } - ] - }, - "documentation": null, - "id": 16, - "implemented": true, - "kind": "constructor", - "modifiers": [], - "name": "", - "nodeType": "FunctionDefinition", - "parameters": { - "id": 9, - "nodeType": "ParameterList", - "parameters": [ - { - "constant": false, - "id": 8, - "name": "_greeting", - "nodeType": "VariableDeclaration", - "scope": 16, - "src": "225:23:0", - "stateVariable": false, - "storageLocation": "memory", - "typeDescriptions": { - "typeIdentifier": "t_string_memory_ptr", - "typeString": "string" - }, - "typeName": { - "id": 7, - "name": "string", - "nodeType": "ElementaryTypeName", - "src": "225:6:0", - "typeDescriptions": { - "typeIdentifier": "t_string_storage_ptr", - "typeString": "string" - } - }, - "value": null, - "visibility": "internal" - } - ], - "src": "224:25:0" - }, - "returnParameters": { - "id": 10, - "nodeType": "ParameterList", - "parameters": [], - "src": "257:0:0" - }, - "scope": 25, - "src": "213:81:0", - "stateMutability": "nonpayable", - "superFunction": null, - "visibility": "public" + "typeName": { + "id": 7, + "name": "string", + "nodeType": "ElementaryTypeName", + "src": "225:6:0", + "typeDescriptions": { + "typeIdentifier": "t_string_storage_ptr", + "typeString": "string" + } }, - { - "body": { - "id": 23, - "nodeType": "Block", - "src": "377:32:0", - "statements": [ - { - "expression": { - "argumentTypes": null, - "id": 21, - "name": "greeting", - "nodeType": "Identifier", - "overloadedDeclarations": [], - "referencedDeclaration": 6, - "src": "394:8:0", - "typeDescriptions": { - "typeIdentifier": "t_string_storage", - "typeString": "string storage ref" - } - }, - "functionReturnParameters": 20, - "id": 22, - "nodeType": "Return", - "src": "387:15:0" - } - ] - }, - "documentation": null, - "id": 24, - "implemented": true, - "kind": "function", - "modifiers": [], - "name": "greet", - "nodeType": "FunctionDefinition", - "parameters": { - "id": 17, - "nodeType": "ParameterList", - "parameters": [], - "src": "338:2:0" - }, - "returnParameters": { - "id": 20, - "nodeType": "ParameterList", - "parameters": [ - { - "constant": false, - "id": 19, - "name": "", - "nodeType": "VariableDeclaration", - "scope": 24, - "src": "362:13:0", - "stateVariable": false, - "storageLocation": "memory", - "typeDescriptions": { - "typeIdentifier": "t_string_memory_ptr", - "typeString": "string" - }, - "typeName": { - "id": 18, - "name": "string", - "nodeType": "ElementaryTypeName", - "src": "362:6:0", - "typeDescriptions": { - "typeIdentifier": "t_string_storage_ptr", - "typeString": "string" - } - }, - "value": null, - "visibility": "internal" - } - ], - "src": "361:15:0" - }, - "scope": 25, - "src": "324:85:0", - "stateMutability": "view", - "superFunction": null, - "visibility": "public" - } + "value": null, + "visibility": "internal" + } ], - "scope": 26, - "src": "54:357:0" + "src": "224:25:0" + }, + "returnParameters": { + "id": 10, + "nodeType": "ParameterList", + "parameters": [], + "src": "257:0:0" + }, + "scope": 25, + "src": "213:81:0", + "stateMutability": "nonpayable", + "superFunction": null, + "visibility": "public" + }, + { + "body": { + "id": 23, + "nodeType": "Block", + "src": "377:32:0", + "statements": [ + { + "expression": { + "argumentTypes": null, + "id": 21, + "name": "greeting", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": 6, + "src": "394:8:0", + "typeDescriptions": { + "typeIdentifier": "t_string_storage", + "typeString": "string storage ref" + } + }, + "functionReturnParameters": 20, + "id": 22, + "nodeType": "Return", + "src": "387:15:0" + } + ] + }, + "documentation": null, + "id": 24, + "implemented": true, + "kind": "function", + "modifiers": [], + "name": "greet", + "nodeType": "FunctionDefinition", + "parameters": { + "id": 17, + "nodeType": "ParameterList", + "parameters": [], + "src": "338:2:0" + }, + "returnParameters": { + "id": 20, + "nodeType": "ParameterList", + "parameters": [ + { + "constant": false, + "id": 19, + "name": "", + "nodeType": "VariableDeclaration", + "scope": 24, + "src": "362:13:0", + "stateVariable": false, + "storageLocation": "memory", + "typeDescriptions": { + "typeIdentifier": "t_string_memory_ptr", + "typeString": "string" + }, + "typeName": { + "id": 18, + "name": "string", + "nodeType": "ElementaryTypeName", + "src": "362:6:0", + "typeDescriptions": { + "typeIdentifier": "t_string_storage_ptr", + "typeString": "string" + } + }, + "value": null, + "visibility": "internal" + } + ], + "src": "361:15:0" + }, + "scope": 25, + "src": "324:85:0", + "stateMutability": "view", + "superFunction": null, + "visibility": "public" } - ], - "src": "0:412:0" + ], + "scope": 26, + "src": "54:357:0" + } + ], + "src": "0:412:0" } } diff --git a/libs/remix-astwalker/tests/sourceMappings.ts b/libs/remix-astwalker/tests/sourceMappings.ts index 1a2bcbdd31..2f77b93ac3 100644 --- a/libs/remix-astwalker/tests/sourceMappings.ts +++ b/libs/remix-astwalker/tests/sourceMappings.ts @@ -1,93 +1,93 @@ import tape from "tape"; import { - AstNode, isAstNode, - LineColPosition, lineColPositionFromOffset, - LineColRange, Location, - SourceMappings, sourceLocationFromAstNode, - sourceLocationFromSrc + AstNode, isAstNode, + LineColPosition, lineColPositionFromOffset, + LineColRange, Location, + SourceMappings, sourceLocationFromAstNode, + sourceLocationFromSrc } from "../src"; import node from "./resources/newAST"; tape("SourceMappings", (t: tape.Test) => { - const source = node.source; - const srcMappings = new SourceMappings(source); - t.test("SourceMappings conversions", (st: tape.Test) => { - st.plan(9); - const loc = { - start: 32, - length: 6, - file: 0 - }; + const source = node.source; + const srcMappings = new SourceMappings(source); + t.test("SourceMappings conversions", (st: tape.Test) => { + st.plan(9); + const loc = { + start: 32, + length: 6, + file: 0 + }; - const ast = node.ast; + const ast = node.ast; - st.deepEqual(lineColPositionFromOffset(0, srcMappings.lineBreaks), + st.deepEqual(lineColPositionFromOffset(0, srcMappings.lineBreaks), { line: 1, character: 1 }, "lineColPositionFromOffset degenerate case"); - st.deepEqual(lineColPositionFromOffset(200, srcMappings.lineBreaks), + st.deepEqual(lineColPositionFromOffset(200, srcMappings.lineBreaks), { line: 17, character: 1 }, "lineColPositionFromOffset conversion"); - /* Typescript will keep us from calling sourceLocationFromAstNode + /* Typescript will keep us from calling sourceLocationFromAstNode with the wrong type. However, for non-typescript uses, we add this test which casts to an AST to check that there is a run-time check in walkFull. */ - st.notOk(sourceLocationFromAstNode(null), - "sourceLocationFromAstNode rejects an invalid astNode"); + st.notOk(sourceLocationFromAstNode(null), + "sourceLocationFromAstNode rejects an invalid astNode"); - st.deepEqual(sourceLocationFromAstNode(ast.nodes[0]), - { start: 0, length: 31, file: 0 }, - "sourceLocationFromAstNode extracts a location"); - st.deepEqual(sourceLocationFromSrc("32:6:0"), loc, - "sourceLocationFromSrc conversion"); - const startLC = { line: 6, character: 6 }; - st.deepEqual(srcMappings.srcToLineColumnRange("45:96:0"), + st.deepEqual(sourceLocationFromAstNode(ast.nodes[0]), + { start: 0, length: 31, file: 0 }, + "sourceLocationFromAstNode extracts a location"); + st.deepEqual(sourceLocationFromSrc("32:6:0"), loc, + "sourceLocationFromSrc conversion"); + const startLC = { line: 6, character: 6 }; + st.deepEqual(srcMappings.srcToLineColumnRange("45:96:0"), { - start: startLC, - end: { line: 11, character: 6 } + start: startLC, + end: { line: 11, character: 6 } }, "srcToLineColumnRange end of line"); - st.deepEqual(srcMappings.srcToLineColumnRange("45:97:0"), + st.deepEqual(srcMappings.srcToLineColumnRange("45:97:0"), { - start: startLC, - end: { line: 12, character: 1 } + start: startLC, + end: { line: 12, character: 1 } }, "srcToLineColumnRange beginning of next line"); - st.deepEqual(srcMappings.srcToLineColumnRange("45:98:0"), + st.deepEqual(srcMappings.srcToLineColumnRange("45:98:0"), { - start: startLC, - end: { line: 13, character: 1 } + start: startLC, + end: { line: 13, character: 1 } }, "srcToLineColumnRange skip over empty line"); - st.deepEqual(srcMappings.srcToLineColumnRange("-1:0:0"), + st.deepEqual(srcMappings.srcToLineColumnRange("-1:0:0"), { - start: null, - end: null + start: null, + end: null }, "srcToLineColumnRange invalid range"); - st.end(); - }); + st.end(); + }); - t.test("SourceMappings constructor", (st: tape.Test) => { - st.plan(2); - st.equal(srcMappings.source, source, "sourceMappings object has source-code string"); - st.deepEqual(srcMappings.lineBreaks, - [15, 26, 27, 38, 39, 81, 87, 103, 119, 135, 141, 142, 186, 192, 193, 199], - "sourceMappings has line-break offsets"); - st.end(); - }); - t.test("SourceMappings functions", (st: tape.Test) => { - st.plan(5); - const ast = node.ast; + t.test("SourceMappings constructor", (st: tape.Test) => { + st.plan(2); + st.equal(srcMappings.source, source, "sourceMappings object has source-code string"); + st.deepEqual(srcMappings.lineBreaks, + [15, 26, 27, 38, 39, 81, 87, 103, 119, 135, 141, 142, 186, 192, 193, 199], + "sourceMappings has line-break offsets"); + st.end(); + }); + t.test("SourceMappings functions", (st: tape.Test) => { + st.plan(5); + const ast = node.ast; - const loc = { start: 267, length: 20, file: 0 }; - let astNode = srcMappings.findNodeAtSourceLocation('ExpressionStatement', loc, ast); - st.ok(isAstNode(astNode), "findsNodeAtSourceLocation finds something"); - astNode = srcMappings.findNodeAtSourceLocation('NotARealThingToFind', loc, ast); - st.notOk(isAstNode(astNode), - "findsNodeAtSourceLocation fails to find something when it should"); - let astNodes = srcMappings.nodesAtPosition(null, loc, ast); - st.equal(astNodes.length, 2, "nodesAtPosition should find more than one astNode"); - st.ok(isAstNode(astNodes[0]), "nodesAtPosition returns only AST nodes"); - astNodes = srcMappings.nodesAtPosition("ExpressionStatement", loc, ast); - st.equal(astNodes.length, 1, "nodesAtPosition filtered to a single nodeType"); - st.end(); - }); + const loc = { start: 267, length: 20, file: 0 }; + let astNode = srcMappings.findNodeAtSourceLocation('ExpressionStatement', loc, ast); + st.ok(isAstNode(astNode), "findsNodeAtSourceLocation finds something"); + astNode = srcMappings.findNodeAtSourceLocation('NotARealThingToFind', loc, ast); + st.notOk(isAstNode(astNode), + "findsNodeAtSourceLocation fails to find something when it should"); + let astNodes = srcMappings.nodesAtPosition(null, loc, ast); + st.equal(astNodes.length, 2, "nodesAtPosition should find more than one astNode"); + st.ok(isAstNode(astNodes[0]), "nodesAtPosition returns only AST nodes"); + astNodes = srcMappings.nodesAtPosition("ExpressionStatement", loc, ast); + st.equal(astNodes.length, 1, "nodesAtPosition filtered to a single nodeType"); + st.end(); + }); }); diff --git a/libs/remix-core-plugin/src/lib/compiler-artefacts.ts b/libs/remix-core-plugin/src/lib/compiler-artefacts.ts index c37e3a7dab..51a4720162 100644 --- a/libs/remix-core-plugin/src/lib/compiler-artefacts.ts +++ b/libs/remix-core-plugin/src/lib/compiler-artefacts.ts @@ -4,242 +4,242 @@ import { util } from '@remix-project/remix-lib' import { CompilerAbstract } from '@remix-project/remix-solidity' const profile = { - name: 'compilerArtefacts', - methods: ['get', 'addResolvedContract', 'getCompilerAbstract', 'getAllContractDatas', 'getLastCompilationResult', 'getArtefactsByContractName', 'getContractDataFromAddress', 'getContractDataFromByteCode'], - events: [], - version: '0.0.1' + name: 'compilerArtefacts', + methods: ['get', 'addResolvedContract', 'getCompilerAbstract', 'getAllContractDatas', 'getLastCompilationResult', 'getArtefactsByContractName', 'getContractDataFromAddress', 'getContractDataFromByteCode'], + events: [], + version: '0.0.1' } export class CompilerArtefacts extends Plugin { - compilersArtefactsPerFile: any - compilersArtefacts: any - constructor () { - super(profile) - this.compilersArtefacts = {} - this.compilersArtefactsPerFile = {} + compilersArtefactsPerFile: any + compilersArtefacts: any + constructor () { + super(profile) + this.compilersArtefacts = {} + this.compilersArtefactsPerFile = {} + } + + clear () { + this.compilersArtefacts = {} + this.compilersArtefactsPerFile = {} + } + + onActivation () { + const saveCompilationPerFileResult = (file, source, languageVersion, data, input?) => { + this.compilersArtefactsPerFile[file] = new CompilerAbstract(languageVersion, data, source, input) } - clear () { - this.compilersArtefacts = {} - this.compilersArtefactsPerFile = {} - } - - onActivation () { - const saveCompilationPerFileResult = (file, source, languageVersion, data, input?) => { - this.compilersArtefactsPerFile[file] = new CompilerAbstract(languageVersion, data, source, input) - } - - this.on('solidity', 'compilationFinished', (file, source, languageVersion, data, input, version) => { - this.compilersArtefacts.__last = new CompilerAbstract(languageVersion, data, source, input) - saveCompilationPerFileResult(file, source, languageVersion, data) - }) - - this.on('vyper', 'compilationFinished', (file, source, languageVersion, data) => { - this.compilersArtefacts.__last = new CompilerAbstract(languageVersion, data, source) - saveCompilationPerFileResult(file, source, languageVersion, data) - }) - - this.on('lexon', 'compilationFinished', (file, source, languageVersion, data) => { - this.compilersArtefacts.__last = new CompilerAbstract(languageVersion, data, source) - saveCompilationPerFileResult(file, source, languageVersion, data) - }) - - this.on('yulp', 'compilationFinished', (file, source, languageVersion, data) => { - this.compilersArtefacts.__last = new CompilerAbstract(languageVersion, data, source) - saveCompilationPerFileResult(file, source, languageVersion, data) - }) - - this.on('solidityUnitTesting', 'compilationFinished', (file, source, languageVersion, data, input, version) => { - this.compilersArtefacts.__last = new CompilerAbstract(languageVersion, data, source, input) - saveCompilationPerFileResult(file, source, languageVersion, data, input) - }) - - this.on('nahmii-compiler', 'compilationFinished', (file, source, languageVersion, data) => { - this.compilersArtefacts.__last = new CompilerAbstract(languageVersion, data, source) - saveCompilationPerFileResult(file, source, languageVersion, data) - }) - - this.on('hardhat', 'compilationFinished', (file, source, languageVersion, data) => { - this.compilersArtefacts.__last = new CompilerAbstract(languageVersion, data, source) - saveCompilationPerFileResult(file, source, languageVersion, data) - }) - - this.on('truffle', 'compilationFinished', (file, source, languageVersion, data) => { - this.compilersArtefacts.__last = new CompilerAbstract(languageVersion, data, source) - saveCompilationPerFileResult(file, source, languageVersion, data) - }) - - this.on('foundry', 'compilationFinished', (file, source, languageVersion, data) => { - this.compilersArtefacts.__last = new CompilerAbstract(languageVersion, data, source) - saveCompilationPerFileResult(file, source, languageVersion, data) - }) - } + this.on('solidity', 'compilationFinished', (file, source, languageVersion, data, input, version) => { + this.compilersArtefacts.__last = new CompilerAbstract(languageVersion, data, source, input) + saveCompilationPerFileResult(file, source, languageVersion, data) + }) + + this.on('vyper', 'compilationFinished', (file, source, languageVersion, data) => { + this.compilersArtefacts.__last = new CompilerAbstract(languageVersion, data, source) + saveCompilationPerFileResult(file, source, languageVersion, data) + }) + + this.on('lexon', 'compilationFinished', (file, source, languageVersion, data) => { + this.compilersArtefacts.__last = new CompilerAbstract(languageVersion, data, source) + saveCompilationPerFileResult(file, source, languageVersion, data) + }) + + this.on('yulp', 'compilationFinished', (file, source, languageVersion, data) => { + this.compilersArtefacts.__last = new CompilerAbstract(languageVersion, data, source) + saveCompilationPerFileResult(file, source, languageVersion, data) + }) + + this.on('solidityUnitTesting', 'compilationFinished', (file, source, languageVersion, data, input, version) => { + this.compilersArtefacts.__last = new CompilerAbstract(languageVersion, data, source, input) + saveCompilationPerFileResult(file, source, languageVersion, data, input) + }) + + this.on('nahmii-compiler', 'compilationFinished', (file, source, languageVersion, data) => { + this.compilersArtefacts.__last = new CompilerAbstract(languageVersion, data, source) + saveCompilationPerFileResult(file, source, languageVersion, data) + }) + + this.on('hardhat', 'compilationFinished', (file, source, languageVersion, data) => { + this.compilersArtefacts.__last = new CompilerAbstract(languageVersion, data, source) + saveCompilationPerFileResult(file, source, languageVersion, data) + }) + + this.on('truffle', 'compilationFinished', (file, source, languageVersion, data) => { + this.compilersArtefacts.__last = new CompilerAbstract(languageVersion, data, source) + saveCompilationPerFileResult(file, source, languageVersion, data) + }) + + this.on('foundry', 'compilationFinished', (file, source, languageVersion, data) => { + this.compilersArtefacts.__last = new CompilerAbstract(languageVersion, data, source) + saveCompilationPerFileResult(file, source, languageVersion, data) + }) + } - /** + /** * Get artefacts for last compiled contract * * @returns last compiled contract compiler abstract */ - getLastCompilationResult () { - return this.compilersArtefacts.__last - } + getLastCompilationResult () { + return this.compilersArtefacts.__last + } - /** + /** * Get compilation output for contracts compiled during a session of Remix IDE * @returns compilatin output */ - getAllContractDatas () { - return this.filterAllContractDatas(() => true) - } + getAllContractDatas () { + return this.filterAllContractDatas(() => true) + } - /** + /** * filter compilation output for contracts compiled during a session of Remix IDE * @returns compilatin output */ - filterAllContractDatas (filter) { - const contractsData = {} - Object.keys(this.compilersArtefactsPerFile).map((targetFile) => { - const artefact = this.compilersArtefactsPerFile[targetFile] - const contracts = artefact.getContracts() - Object.keys(contracts).map((file) => { - if (filter(file, contracts[file], artefact)) contractsData[file] = contracts[file] - }) - }) - // making sure we save last compilation result in there - if (this.compilersArtefacts.__last) { - const contracts = this.compilersArtefacts.__last.getContracts() - Object.keys(contracts).map((file) => { - if (filter(file, contracts[file], this.compilersArtefacts.__last)) contractsData[file] = contracts[file] - }) - } - return contractsData + filterAllContractDatas (filter) { + const contractsData = {} + Object.keys(this.compilersArtefactsPerFile).map((targetFile) => { + const artefact = this.compilersArtefactsPerFile[targetFile] + const contracts = artefact.getContracts() + Object.keys(contracts).map((file) => { + if (filter(file, contracts[file], artefact)) contractsData[file] = contracts[file] + }) + }) + // making sure we save last compilation result in there + if (this.compilersArtefacts.__last) { + const contracts = this.compilersArtefacts.__last.getContracts() + Object.keys(contracts).map((file) => { + if (filter(file, contracts[file], this.compilersArtefacts.__last)) contractsData[file] = contracts[file] + }) } + return contractsData + } - /** + /** * Get a particular contract output/artefacts from a compiler output of a Solidity file compilation * @param compilerOutput compiler output * @param contractName contract name * @returns arefacts object, with fully qualified name (e.g; contracts/1_Storage.sol:Storage) as key */ - _getAllContractArtefactsfromOutput (compilerOutput, contractName) { - const contractArtefacts = {} - for (const filename in compilerOutput) { - if(Object.keys(compilerOutput[filename]).includes(contractName)) contractArtefacts[filename + ':' + contractName] = compilerOutput[filename][contractName] - } - return contractArtefacts + _getAllContractArtefactsfromOutput (compilerOutput, contractName) { + const contractArtefacts = {} + for (const filename in compilerOutput) { + if(Object.keys(compilerOutput[filename]).includes(contractName)) contractArtefacts[filename + ':' + contractName] = compilerOutput[filename][contractName] } + return contractArtefacts + } - /** + /** * Populate resultant object with a particular contract output/artefacts by processing all the artifacts stored in file explorer * @param path path to start looking from * @param contractName contract to be looked for * @param contractArtefacts populated resultant artefacts object, with fully qualified name (e.g: contracts/1_Storage.sol:Storage) as key * Once method execution completes, contractArtefacts object will hold all possible artefacts for contract */ - async _populateAllContractArtefactsFromFE (path, contractName, contractArtefacts) { - const dirList = await this.call('fileManager', 'dirList', path) - if(dirList && dirList.length) { - for (const dirPath of dirList) { - // check if directory contains an 'artifacts' folder and a 'build-info' folder inside 'artifacts' - if(dirPath === path + '/artifacts' && await this.call('fileManager', 'exists', dirPath + '/build-info')) { - const buildFileList = await this.call('fileManager', 'fileList', dirPath + '/build-info') - // process each build-info file to populate the artefacts for contractName - for (const buildFile of buildFileList) { - let content = await this.call('fileManager', 'readFile', buildFile) - if (content) content = JSON.parse(content) - const compilerOutput = content.output.contracts - const artefacts = this._getAllContractArtefactsfromOutput(compilerOutput, contractName) - // populate the resultant object with artefacts - Object.assign(contractArtefacts, artefacts) - } - } else await this._populateAllContractArtefactsFromFE (dirPath, contractName, contractArtefacts) - } - } else return - } - - /** + async _populateAllContractArtefactsFromFE (path, contractName, contractArtefacts) { + const dirList = await this.call('fileManager', 'dirList', path) + if(dirList && dirList.length) { + for (const dirPath of dirList) { + // check if directory contains an 'artifacts' folder and a 'build-info' folder inside 'artifacts' + if(dirPath === path + '/artifacts' && await this.call('fileManager', 'exists', dirPath + '/build-info')) { + const buildFileList = await this.call('fileManager', 'fileList', dirPath + '/build-info') + // process each build-info file to populate the artefacts for contractName + for (const buildFile of buildFileList) { + let content = await this.call('fileManager', 'readFile', buildFile) + if (content) content = JSON.parse(content) + const compilerOutput = content.output.contracts + const artefacts = this._getAllContractArtefactsfromOutput(compilerOutput, contractName) + // populate the resultant object with artefacts + Object.assign(contractArtefacts, artefacts) + } + } else await this._populateAllContractArtefactsFromFE (dirPath, contractName, contractArtefacts) + } + } else return + } + + /** * Get artefacts for a contract (called by script-runner) * @param name contract name or fully qualified name i.e. : e.g: contracts/1_Storage.sol:Storage * @returns artefacts for the contract */ - async getArtefactsByContractName (name) { - const contractsDataByFilename = this.getAllContractDatas() - // check if name is a fully qualified name - if (name.includes(':')) { - const fullyQualifiedName = name - const nameArr = fullyQualifiedName.split(':') - const filename = nameArr[0] - const contract = nameArr[1] - if(Object.keys(contractsDataByFilename).includes(filename) && contractsDataByFilename[filename][contract]) - return contractsDataByFilename[filename][contract] - else { - const allContractsData = {} - await this._populateAllContractArtefactsFromFE ('contracts', contract, allContractsData) - if(allContractsData[fullyQualifiedName]) return { fullyQualifiedName, artefact: allContractsData[fullyQualifiedName]} - else throw new Error(`Could not find artifacts for ${fullyQualifiedName}. Compile contract to generate artifacts.`) - } - } else { - const contractName = name - const contractArtefacts = this._getAllContractArtefactsfromOutput(contractsDataByFilename, contractName) - let keys = Object.keys(contractArtefacts) - if (!keys.length) { - await this._populateAllContractArtefactsFromFE ('contracts', contractName, contractArtefacts) - keys = Object.keys(contractArtefacts) - } - if (keys.length === 1) return { fullyQualifiedName: keys[0], artefact: contractArtefacts[keys[0]] } - else if (keys.length > 1) { - throw new Error(`There are multiple artifacts for contract "${contractName}", please use a fully qualified name.\n + async getArtefactsByContractName (name) { + const contractsDataByFilename = this.getAllContractDatas() + // check if name is a fully qualified name + if (name.includes(':')) { + const fullyQualifiedName = name + const nameArr = fullyQualifiedName.split(':') + const filename = nameArr[0] + const contract = nameArr[1] + if(Object.keys(contractsDataByFilename).includes(filename) && contractsDataByFilename[filename][contract]) + return contractsDataByFilename[filename][contract] + else { + const allContractsData = {} + await this._populateAllContractArtefactsFromFE ('contracts', contract, allContractsData) + if(allContractsData[fullyQualifiedName]) return { fullyQualifiedName, artefact: allContractsData[fullyQualifiedName]} + else throw new Error(`Could not find artifacts for ${fullyQualifiedName}. Compile contract to generate artifacts.`) + } + } else { + const contractName = name + const contractArtefacts = this._getAllContractArtefactsfromOutput(contractsDataByFilename, contractName) + let keys = Object.keys(contractArtefacts) + if (!keys.length) { + await this._populateAllContractArtefactsFromFE ('contracts', contractName, contractArtefacts) + keys = Object.keys(contractArtefacts) + } + if (keys.length === 1) return { fullyQualifiedName: keys[0], artefact: contractArtefacts[keys[0]] } + else if (keys.length > 1) { + throw new Error(`There are multiple artifacts for contract "${contractName}", please use a fully qualified name.\n Please replace ${contractName} for one of these options wherever you are trying to read its artifact: \n ${keys.join()}\n OR just compile the required contract again`) - } else throw new Error(`Could not find artifacts for ${contractName}. Compile contract to generate artifacts.`) - } - } - - async getCompilerAbstract (file) { - if (!file) return null - if (this.compilersArtefactsPerFile[file]) return this.compilersArtefactsPerFile[file] - const path = await this.call('fileManager', 'getPathFromUrl', file) - - if (path && path.file && this.compilersArtefactsPerFile[path.file]) return this.compilersArtefactsPerFile[path.file] - - let artefact = null - this.filterAllContractDatas((localFile, data, parentArtefact) => { - if (localFile === file || (path && path.file && localFile === path.file)) { - artefact = parentArtefact - } - }) - return artefact - } - - addResolvedContract (address: string, compilerData: CompilerAbstract) { - this.compilersArtefacts[address] = compilerData - } - - isResolved (address) { - return this.compilersArtefacts[address] !== undefined - } - - get (key) { - return this.compilersArtefacts[key] - } - - async getContractDataFromAddress (address) { - const code = await this.call('blockchain', 'getCode', address) - return this.getContractDataFromByteCode(code) - } - - async getContractDataFromByteCode (code) { - 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, file } - return true - } - } - return true - }) - return found + } else throw new Error(`Could not find artifacts for ${contractName}. Compile contract to generate artifacts.`) } + } + + async getCompilerAbstract (file) { + if (!file) return null + if (this.compilersArtefactsPerFile[file]) return this.compilersArtefactsPerFile[file] + const path = await this.call('fileManager', 'getPathFromUrl', file) + + if (path && path.file && this.compilersArtefactsPerFile[path.file]) return this.compilersArtefactsPerFile[path.file] + + let artefact = null + this.filterAllContractDatas((localFile, data, parentArtefact) => { + if (localFile === file || (path && path.file && localFile === path.file)) { + artefact = parentArtefact + } + }) + return artefact + } + + addResolvedContract (address: string, compilerData: CompilerAbstract) { + this.compilersArtefacts[address] = compilerData + } + + isResolved (address) { + return this.compilersArtefacts[address] !== undefined + } + + get (key) { + return this.compilersArtefacts[key] + } + + async getContractDataFromAddress (address) { + const code = await this.call('blockchain', 'getCode', address) + return this.getContractDataFromByteCode(code) + } + + async getContractDataFromByteCode (code) { + 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, file } + return true + } + } + return true + }) + return found + } } diff --git a/libs/remix-core-plugin/src/lib/compiler-content-imports.ts b/libs/remix-core-plugin/src/lib/compiler-content-imports.ts index 50b9b08bb2..198c3db6dc 100644 --- a/libs/remix-core-plugin/src/lib/compiler-content-imports.ts +++ b/libs/remix-core-plugin/src/lib/compiler-content-imports.ts @@ -3,10 +3,10 @@ import { Plugin } from '@remixproject/engine' import { RemixURLResolver } from '@remix-project/remix-url-resolver' const profile = { - name: 'contentImport', - displayName: 'content import', - version: '0.0.1', - methods: ['resolve', 'resolveAndSave', 'isExternalUrl'] + name: 'contentImport', + displayName: 'content import', + version: '0.0.1', + methods: ['resolve', 'resolveAndSave', 'isExternalUrl'] } export type ResolvedImport = { @@ -16,131 +16,131 @@ export type ResolvedImport = { } export class CompilerImports extends Plugin { - urlResolver: any - constructor () { - super(profile) - this.urlResolver = new RemixURLResolver(async () => { - try { - let yarnLock - if (await this.call('fileManager', 'exists', './yarn.lock')) { - yarnLock = await this.call('fileManager', 'readFile', './yarn.lock') - } + urlResolver: any + constructor () { + super(profile) + this.urlResolver = new RemixURLResolver(async () => { + try { + let yarnLock + if (await this.call('fileManager', 'exists', './yarn.lock')) { + yarnLock = await this.call('fileManager', 'readFile', './yarn.lock') + } - let packageLock - if (await this.call('fileManager', 'exists', './package-lock.json')) { - packageLock = await this.call('fileManager', 'readFile', './package-lock.json') - packageLock = JSON.parse(packageLock) - } + let packageLock + if (await this.call('fileManager', 'exists', './package-lock.json')) { + packageLock = await this.call('fileManager', 'readFile', './package-lock.json') + packageLock = JSON.parse(packageLock) + } - if (await this.call('fileManager', 'exists', './package.json')) { - const content = await this.call('fileManager', 'readFile', './package.json') - const pkg = JSON.parse(content) - return { deps: { ...pkg['dependencies'], ...pkg['devDependencies'] }, yarnLock, packageLock } - } else { - return {} - } - } catch (e) { - console.error(e) - return {} - } - }) + if (await this.call('fileManager', 'exists', './package.json')) { + const content = await this.call('fileManager', 'readFile', './package.json') + const pkg = JSON.parse(content) + return { deps: { ...pkg['dependencies'], ...pkg['devDependencies'] }, yarnLock, packageLock } + } else { + return {} + } + } catch (e) { + console.error(e) + return {} + } + }) + } + + onActivation(): void { + const packageFiles = ['package.json', 'package-lock.json', 'yarn.lock'] + this.on('filePanel', 'setWorkspace', () => this.urlResolver.clearCache()) + this.on('fileManager', 'fileRemoved', (file: string) => { + if (packageFiles.includes(file)) { + this.urlResolver.clearCache() + } + }) + this.on('fileManager', 'fileChanged', (file: string) => { + if (packageFiles.includes(file)) { + this.urlResolver.clearCache() + } + }) + } + + async setToken () { + try { + const protocol = typeof window !== 'undefined' && window.location.protocol + const token = await this.call('settings', 'get', 'settings/gist-access-token') + + this.urlResolver.setGistToken(token, protocol) + } catch (error) { + console.log(error) } + } - onActivation(): void { - const packageFiles = ['package.json', 'package-lock.json', 'yarn.lock'] - this.on('filePanel', 'setWorkspace', () => this.urlResolver.clearCache()) - this.on('fileManager', 'fileRemoved', (file: string) => { - if (packageFiles.includes(file)) { - this.urlResolver.clearCache() - } - }) - this.on('fileManager', 'fileChanged', (file: string) => { - if (packageFiles.includes(file)) { - this.urlResolver.clearCache() - } - }) - } + isRelativeImport (url) { + return /^([^/]+)/.exec(url) + } - async setToken () { - try { - const protocol = typeof window !== 'undefined' && window.location.protocol - const token = await this.call('settings', 'get', 'settings/gist-access-token') - - this.urlResolver.setGistToken(token, protocol) - } catch (error) { - console.log(error) - } - } - - isRelativeImport (url) { - return /^([^/]+)/.exec(url) - } + isExternalUrl (url) { + const handlers = this.urlResolver.getHandlers() + // we filter out "npm" because this will be recognized as internal url although it's not the case. + return handlers.filter((handler) => handler.type !== 'npm').some(handler => handler.match(url)) + } - isExternalUrl (url) { - const handlers = this.urlResolver.getHandlers() - // we filter out "npm" because this will be recognized as internal url although it's not the case. - return handlers.filter((handler) => handler.type !== 'npm').some(handler => handler.match(url)) - } - - /** + /** * resolve the content of @arg url. This only resolves external URLs. * * @param {String} url - external URL of the content. can be basically anything like raw HTTP, ipfs URL, github address etc... * @returns {Promise} - { content, cleanUrl, type, url } */ - resolve (url) { - return new Promise((resolve, reject) => { - this.import(url, null, (error, content, cleanUrl, type, url) => { - if (error) return reject(error) - resolve({ content, cleanUrl, type, url }) - }, null) - }) - } - - async import (url, force, loadingCb, cb) { - if (typeof force !== 'boolean') { - const temp = loadingCb - loadingCb = force - cb = temp - force = false - } - if (!loadingCb) loadingCb = () => {} - if (!cb) cb = () => {} - - const self = this - - let resolved - try { - await this.setToken() - resolved = await this.urlResolver.resolve(url, [], force) - const { content, cleanUrl, type } = resolved - cb(null, content, cleanUrl, type, url) - } catch (e) { - return cb(new Error('not found ' + url)) - } + resolve (url) { + return new Promise((resolve, reject) => { + this.import(url, null, (error, content, cleanUrl, type, url) => { + if (error) return reject(error) + resolve({ content, cleanUrl, type, url }) + }, null) + }) + } + + async import (url, force, loadingCb, cb) { + if (typeof force !== 'boolean') { + const temp = loadingCb + loadingCb = force + cb = temp + force = false } - - importExternal (url, targetPath) { - return new Promise((resolve, reject) => { - this.import(url, - // TODO: handle this event - (loadingMsg) => { this.emit('message', loadingMsg) }, - async (error, content, cleanUrl, type, url) => { - if (error) return reject(error) - try { - const provider = await this.call('fileManager', 'getProviderOf', null) - const path = targetPath || type + '/' + cleanUrl - if (provider) await provider.addExternal('.deps/' + path, content, url) - } catch (err) { - console.error(err) - } - resolve(content) - }, null) - }) + if (!loadingCb) loadingCb = () => {} + if (!cb) cb = () => {} + + const self = this + + let resolved + try { + await this.setToken() + resolved = await this.urlResolver.resolve(url, [], force) + const { content, cleanUrl, type } = resolved + cb(null, content, cleanUrl, type, url) + } catch (e) { + return cb(new Error('not found ' + url)) } - - /** + } + + importExternal (url, targetPath) { + return new Promise((resolve, reject) => { + this.import(url, + // TODO: handle this event + (loadingMsg) => { this.emit('message', loadingMsg) }, + async (error, content, cleanUrl, type, url) => { + if (error) return reject(error) + try { + const provider = await this.call('fileManager', 'getProviderOf', null) + const path = targetPath || type + '/' + cleanUrl + if (provider) await provider.addExternal('.deps/' + path, content, url) + } catch (err) { + console.error(err) + } + resolve(content) + }, null) + }) + } + + /** * import the content of @arg url. * first look in the browser localstorage (browser explorer) or locahost explorer. if the url start with `browser/*` or `localhost/*` * then check if the @arg url is located in the localhost, in the node_modules or installed_contracts folder @@ -150,66 +150,66 @@ export class CompilerImports extends Plugin { * @param {String} targetPath - (optional) internal path where the content should be saved to * @returns {Promise} - string content */ - async resolveAndSave (url, targetPath) { - try { - if (targetPath && this.currentRequest) { - const canCall = await this.askUserPermission('resolveAndSave', 'This action will update the path ' + targetPath) - if (!canCall) throw new Error('No permission to update ' + targetPath) - } - const provider = await this.call('fileManager', 'getProviderOf', url) - if (provider) { - if (provider.type === 'localhost' && !provider.isConnected()) { - throw new Error(`file provider ${provider.type} not available while trying to resolve ${url}`) - } - let exist = await provider.exists(url) - /* + async resolveAndSave (url, targetPath) { + try { + if (targetPath && this.currentRequest) { + const canCall = await this.askUserPermission('resolveAndSave', 'This action will update the path ' + targetPath) + if (!canCall) throw new Error('No permission to update ' + targetPath) + } + const provider = await this.call('fileManager', 'getProviderOf', url) + if (provider) { + if (provider.type === 'localhost' && !provider.isConnected()) { + throw new Error(`file provider ${provider.type} not available while trying to resolve ${url}`) + } + let exist = await provider.exists(url) + /* if the path is absolute and the file does not exist, we can stop here Doesn't make sense to try to resolve "localhost/node_modules/localhost/node_modules/" and we'll end in an infinite loop. */ - if (!exist && (url === 'remix_tests.sol' || url === 'remix_accounts.sol')) { - await this.call('solidityUnitTesting', 'createTestLibs') - exist = await provider.exists(url) - } - if (!exist && url.startsWith('browser/')) throw new Error(`not found ${url}`) - if (!exist && url.startsWith('localhost/')) throw new Error(`not found ${url}`) - if (exist) { - const content = await (() => { - return new Promise((resolve, reject) => { - provider.get(url, (error, content) => { - if (error) return reject(error) - resolve(content) - }) - }) - })() - return content - } else { - const localhostProvider = await this.call('fileManager', 'getProviderByName', 'localhost') - if (localhostProvider.isConnected()) { - const splitted = /([^/]+)\/(.*)$/g.exec(url) - - const possiblePaths = ['localhost/installed_contracts/' + url] - // pick remix-tests library contracts from '.deps' - if (url.startsWith('remix_')) possiblePaths.push('localhost/.deps/remix-tests/' + url) - if (splitted) possiblePaths.push('localhost/installed_contracts/' + splitted[1] + '/contracts/' + splitted[2]) - possiblePaths.push('localhost/node_modules/' + url) - if (splitted) possiblePaths.push('localhost/node_modules/' + splitted[1] + '/contracts/' + splitted[2]) - - for (const path of possiblePaths) { - try { - const content = await this.resolveAndSave(path, null) - if (content) { - localhostProvider.addNormalizedName(path.replace('localhost/', ''), url) - return content - } - } catch (e) {} - } - return await this.importExternal(url, targetPath) - } - return await this.importExternal(url, targetPath) + if (!exist && (url === 'remix_tests.sol' || url === 'remix_accounts.sol')) { + await this.call('solidityUnitTesting', 'createTestLibs') + exist = await provider.exists(url) + } + if (!exist && url.startsWith('browser/')) throw new Error(`not found ${url}`) + if (!exist && url.startsWith('localhost/')) throw new Error(`not found ${url}`) + if (exist) { + const content = await (() => { + return new Promise((resolve, reject) => { + provider.get(url, (error, content) => { + if (error) return reject(error) + resolve(content) + }) + }) + })() + return content + } else { + const localhostProvider = await this.call('fileManager', 'getProviderByName', 'localhost') + if (localhostProvider.isConnected()) { + const splitted = /([^/]+)\/(.*)$/g.exec(url) + + const possiblePaths = ['localhost/installed_contracts/' + url] + // pick remix-tests library contracts from '.deps' + if (url.startsWith('remix_')) possiblePaths.push('localhost/.deps/remix-tests/' + url) + if (splitted) possiblePaths.push('localhost/installed_contracts/' + splitted[1] + '/contracts/' + splitted[2]) + possiblePaths.push('localhost/node_modules/' + url) + if (splitted) possiblePaths.push('localhost/node_modules/' + splitted[1] + '/contracts/' + splitted[2]) + + for (const path of possiblePaths) { + try { + const content = await this.resolveAndSave(path, null) + if (content) { + localhostProvider.addNormalizedName(path.replace('localhost/', ''), url) + return content } + } catch (e) {} } - } catch (e) { - throw new Error(e) + return await this.importExternal(url, targetPath) + } + return await this.importExternal(url, targetPath) } + } + } catch (e) { + throw new Error(e) } + } } diff --git a/libs/remix-core-plugin/src/lib/compiler-fetch-and-compile.ts b/libs/remix-core-plugin/src/lib/compiler-fetch-and-compile.ts index 3030321f93..ca34ef8fb9 100644 --- a/libs/remix-core-plugin/src/lib/compiler-fetch-and-compile.ts +++ b/libs/remix-core-plugin/src/lib/compiler-fetch-and-compile.ts @@ -7,29 +7,29 @@ import { fetchContractFromSourcify } from './helpers/fetch-sourcify' import { UUPSDeployedByteCode, UUPSCompilerVersion, UUPSOptimize, UUPSRuns, UUPSEvmVersion, UUPSLanguage } from './constants/uups' const profile = { - name: 'fetchAndCompile', - methods: ['resolve', 'clearCache'], - version: '0.0.1' + name: 'fetchAndCompile', + methods: ['resolve', 'clearCache'], + version: '0.0.1' } export class FetchAndCompile extends Plugin { - unresolvedAddresses: any[] - sourceVerifierNetWork: string[] - constructor () { - super(profile) - this.unresolvedAddresses = [] - this.sourceVerifierNetWork = ['Main', 'Rinkeby', 'Ropsten', 'Goerli'] - } + unresolvedAddresses: any[] + sourceVerifierNetWork: string[] + constructor () { + super(profile) + this.unresolvedAddresses = [] + this.sourceVerifierNetWork = ['Main', 'Rinkeby', 'Ropsten', 'Goerli'] + } - /** + /** * Clear the cache * */ - async clearCache () { - this.unresolvedAddresses = [] - } + async clearCache () { + this.unresolvedAddresses = [] + } - /** + /** * Fetch compiliation metadata from source-Verify from a given @arg contractAddress - https://github.com/ethereum/source-verify * Put the artifacts in the file explorer * Compile the code using Solidity compiler @@ -40,135 +40,135 @@ export class FetchAndCompile extends Plugin { * @param {string} targetPath - Folder where to save the compilation arfefacts * @return {CompilerAbstract} - compilation data targeting the given @arg contractAddress */ - async resolve (contractAddress, codeAtAddress, targetPath) { - contractAddress = toChecksumAddress(contractAddress) + async resolve (contractAddress, codeAtAddress, targetPath) { + contractAddress = toChecksumAddress(contractAddress) - const localCompilation = async () => { - const contractData = await this.call('compilerArtefacts', 'getContractDataFromByteCode', codeAtAddress) - if (contractData) { - return await this.call('compilerArtefacts', 'getCompilerAbstract', contractData.file) - } - else - return await this.call('compilerArtefacts', 'get', '__last') - } + const localCompilation = async () => { + const contractData = await this.call('compilerArtefacts', 'getContractDataFromByteCode', codeAtAddress) + if (contractData) { + return await this.call('compilerArtefacts', 'getCompilerAbstract', contractData.file) + } + else + return await this.call('compilerArtefacts', 'get', '__last') + } - const resolved = await this.call('compilerArtefacts', 'get', contractAddress) - if (resolved) return resolved - if (this.unresolvedAddresses.includes(contractAddress)) return localCompilation() + const resolved = await this.call('compilerArtefacts', 'get', contractAddress) + if (resolved) return resolved + if (this.unresolvedAddresses.includes(contractAddress)) return localCompilation() - if (codeAtAddress === '0x' + UUPSDeployedByteCode) { // proxy - const settings = { - version: UUPSCompilerVersion, - language: UUPSLanguage, - evmVersion: UUPSEvmVersion, - optimize: UUPSOptimize, - runs: UUPSRuns - } - const proxyUrl = 'https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v4.8.0/contracts/proxy/ERC1967/ERC1967Proxy.sol' - const compilationTargets = { - 'proxy.sol': { content: `import "${proxyUrl}";` } - } - const compData = await compile( - compilationTargets, - settings, - async (url, cb) => { - // we first try to resolve the content from the compilation target using a more appropiate path - const path = `${targetPath}/${url}` - if (compilationTargets[path] && compilationTargets[path].content) { - return cb(null, compilationTargets[path].content) - } else { - await this.call('contentImport', 'resolveAndSave', url).then((result) => cb(null, result)).catch((error) => cb(error.message)) - } - }) - await this.call('compilerArtefacts', 'addResolvedContract', contractAddress, compData) - return compData - } + if (codeAtAddress === '0x' + UUPSDeployedByteCode) { // proxy + const settings = { + version: UUPSCompilerVersion, + language: UUPSLanguage, + evmVersion: UUPSEvmVersion, + optimize: UUPSOptimize, + runs: UUPSRuns + } + const proxyUrl = 'https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v4.8.0/contracts/proxy/ERC1967/ERC1967Proxy.sol' + const compilationTargets = { + 'proxy.sol': { content: `import "${proxyUrl}";` } + } + const compData = await compile( + compilationTargets, + settings, + async (url, cb) => { + // we first try to resolve the content from the compilation target using a more appropiate path + const path = `${targetPath}/${url}` + if (compilationTargets[path] && compilationTargets[path].content) { + return cb(null, compilationTargets[path].content) + } else { + await this.call('contentImport', 'resolveAndSave', url).then((result) => cb(null, result)).catch((error) => cb(error.message)) + } + }) + await this.call('compilerArtefacts', 'addResolvedContract', contractAddress, compData) + return compData + } - // sometimes when doing an internal call, the only available artifact is the Solidity interface. - // resolving addresses of internal call would allow to step over the source code, even if the declaration was made using an Interface. + // sometimes when doing an internal call, the only available artifact is the Solidity interface. + // resolving addresses of internal call would allow to step over the source code, even if the declaration was made using an Interface. - let network - try { - network = await this.call('network', 'detectNetwork') - } catch (e) { - return localCompilation() - } - if (!network) return localCompilation() - if (!this.sourceVerifierNetWork.includes(network.name)) { - // check if the contract if part of the local compilation result - const compilation = await localCompilation() - if (compilation) { - let found = false - compilation.visitContracts((contract) => { - found = util.compareByteCode(codeAtAddress, '0x' + contract.object.evm.deployedBytecode.object) - return found - }) - if (found) { - await this.call('compilerArtefacts', 'addResolvedContract', contractAddress, compilation) - return compilation - } - } + let network + try { + network = await this.call('network', 'detectNetwork') + } catch (e) { + return localCompilation() + } + if (!network) return localCompilation() + if (!this.sourceVerifierNetWork.includes(network.name)) { + // check if the contract if part of the local compilation result + const compilation = await localCompilation() + if (compilation) { + let found = false + compilation.visitContracts((contract) => { + found = util.compareByteCode(codeAtAddress, '0x' + contract.object.evm.deployedBytecode.object) + return found + }) + if (found) { + await this.call('compilerArtefacts', 'addResolvedContract', contractAddress, compilation) + return compilation } + } + } - targetPath = `${targetPath}/${network.id}/${contractAddress}` - let data - try { - data = await fetchContractFromSourcify(this, network, contractAddress, targetPath) - } catch (e) { - this.call('notification', 'toast', e.message) - console.log(e) // and fallback to getting the compilation result from etherscan - } + targetPath = `${targetPath}/${network.id}/${contractAddress}` + let data + try { + data = await fetchContractFromSourcify(this, network, contractAddress, targetPath) + } catch (e) { + this.call('notification', 'toast', e.message) + console.log(e) // and fallback to getting the compilation result from etherscan + } - if (!data) { - this.call('notification', 'toast', `contract ${contractAddress} not found in Sourcify, checking in Etherscan..`) - try { - data = await fetchContractFromEtherscan(this, network, contractAddress, targetPath) - } catch (e) { - this.call('notification', 'toast', e.message) - setTimeout(_ => this.emit('notFound', contractAddress), 0) // plugin framework returns a time out error although it actually didn't find the source... - this.unresolvedAddresses.push(contractAddress) - return localCompilation() - } - } + if (!data) { + this.call('notification', 'toast', `contract ${contractAddress} not found in Sourcify, checking in Etherscan..`) + try { + data = await fetchContractFromEtherscan(this, network, contractAddress, targetPath) + } catch (e) { + this.call('notification', 'toast', e.message) + setTimeout(_ => this.emit('notFound', contractAddress), 0) // plugin framework returns a time out error although it actually didn't find the source... + this.unresolvedAddresses.push(contractAddress) + return localCompilation() + } + } - if (!data) { - setTimeout(_ => this.emit('notFound', contractAddress), 0) - this.unresolvedAddresses.push(contractAddress) - const compilation = await localCompilation() - if (compilation) { - let found = false - compilation.visitContracts((contract) => { - found = util.compareByteCode(codeAtAddress, '0x' + contract.object.evm.deployedBytecode.object) - return found - }) - if (found) { - await this.call('compilerArtefacts', 'addResolvedContract', contractAddress, compilation) - return compilation - } - } + if (!data) { + setTimeout(_ => this.emit('notFound', contractAddress), 0) + this.unresolvedAddresses.push(contractAddress) + const compilation = await localCompilation() + if (compilation) { + let found = false + compilation.visitContracts((contract) => { + found = util.compareByteCode(codeAtAddress, '0x' + contract.object.evm.deployedBytecode.object) + return found + }) + if (found) { + await this.call('compilerArtefacts', 'addResolvedContract', contractAddress, compilation) + return compilation } - const { settings, compilationTargets } = data + } + } + const { settings, compilationTargets } = data - try { - setTimeout(_ => this.emit('compiling', settings), 0) - const compData = await compile( - compilationTargets, - settings, - async (url, cb) => { - // we first try to resolve the content from the compilation target using a more appropiate path - const path = `${targetPath}/${url}` - if (compilationTargets[path] && compilationTargets[path].content) { - return cb(null, compilationTargets[path].content) - } else { - await this.call('contentImport', 'resolveAndSave', url).then((result) => cb(null, result)).catch((error) => cb(error.message)) - } - }) - await this.call('compilerArtefacts', 'addResolvedContract', contractAddress, compData) - return compData - } catch (e) { - this.unresolvedAddresses.push(contractAddress) - setTimeout(_ => this.emit('compilationFailed'), 0) - return localCompilation() - } + try { + setTimeout(_ => this.emit('compiling', settings), 0) + const compData = await compile( + compilationTargets, + settings, + async (url, cb) => { + // we first try to resolve the content from the compilation target using a more appropiate path + const path = `${targetPath}/${url}` + if (compilationTargets[path] && compilationTargets[path].content) { + return cb(null, compilationTargets[path].content) + } else { + await this.call('contentImport', 'resolveAndSave', url).then((result) => cb(null, result)).catch((error) => cb(error.message)) + } + }) + await this.call('compilerArtefacts', 'addResolvedContract', contractAddress, compData) + return compData + } catch (e) { + this.unresolvedAddresses.push(contractAddress) + setTimeout(_ => this.emit('compilationFailed'), 0) + return localCompilation() } + } } diff --git a/libs/remix-core-plugin/src/lib/compiler-metadata.ts b/libs/remix-core-plugin/src/lib/compiler-metadata.ts index b45daeb5f0..c9e6ce308a 100644 --- a/libs/remix-core-plugin/src/lib/compiler-metadata.ts +++ b/libs/remix-core-plugin/src/lib/compiler-metadata.ts @@ -4,196 +4,196 @@ import { CompilerAbstract } from '@remix-project/remix-solidity' import { createHash } from 'crypto' const profile = { - name: 'compilerMetadata', - methods: ['deployMetadataOf'], - events: [], - version: '0.0.1' + name: 'compilerMetadata', + methods: ['deployMetadataOf'], + events: [], + version: '0.0.1' } export class CompilerMetadata extends Plugin { - networks: string[] - innerPath: string - buildInfoNames: Record - constructor () { - super(profile) - this.networks = ['VM:-', 'main:1', 'ropsten:3', 'rinkeby:4', 'kovan:42', 'goerli:5', 'Custom'] - this.innerPath = 'artifacts' - this.buildInfoNames = {} + networks: string[] + innerPath: string + buildInfoNames: Record + constructor () { + super(profile) + this.networks = ['VM:-', 'main:1', 'ropsten:3', 'rinkeby:4', 'kovan:42', 'goerli:5', 'Custom'] + this.innerPath = 'artifacts' + this.buildInfoNames = {} + } + + _JSONFileName (path, contractName) { + return this.joinPath(path, this.innerPath, contractName + '.json') + } + + _MetadataFileName (path, contractName) { + return this.joinPath(path, this.innerPath, contractName + '_metadata.json') + } + + onActivation () { + const self = this + this.on('filePanel', 'setWorkspace', () => { + this.buildInfoNames = {} + }) + this.on('solidity', 'compilationFinished', async (file, source, languageVersion, data, input, version) => { + if (!await this.call('settings', 'get', 'settings/generate-contract-metadata')) return + const compiler = new CompilerAbstract(languageVersion, data, source, input) + const path = self._extractPathOf(source.target) + await this.setBuildInfo(version, input, data, path, file) + compiler.visitContracts((contract) => { + if (contract.file !== source.target) return + (async () => { + const fileName = self._JSONFileName(path, contract.name) + const content = await this.call('fileManager', 'exists', fileName) ? await this.call('fileManager', 'readFile', fileName) : null + await this._setArtefacts(content, contract, path) + })() + }) + }) + } + + // Access each file in build-info, check the input sources + // If they are all same as in current compiled file and sources includes the path of compiled file, remove old build file + async removeStoredBuildInfo (currentInput, path, filePath) { + const buildDir = this.joinPath(path, this.innerPath, 'build-info/') + if (await this.call('fileManager', 'exists', buildDir)) { + const allBuildFiles = await this.call('fileManager', 'fileList', buildDir) + const currentInputFileNames = Object.keys(currentInput.sources) + for (const fileName of allBuildFiles) { + let fileContent = await this.call('fileManager', 'readFile', fileName) + fileContent = JSON.parse(fileContent) + const inputFiles = Object.keys(fileContent.input.sources) + const inputIntersection = currentInputFileNames.filter(element => !inputFiles.includes(element)) + if (inputIntersection.length === 0 && inputFiles.includes(filePath)) await this.call('fileManager', 'remove', fileName) + } } - - _JSONFileName (path, contractName) { - return this.joinPath(path, this.innerPath, contractName + '.json') + } + + async setBuildInfo (version, input, output, path, filePath) { + input = JSON.parse(input) + const solcLongVersion = version.replace('.Emscripten.clang', '') + const solcVersion = solcLongVersion.substring(0, solcLongVersion.indexOf('+commit')) + const format = 'hh-sol-build-info-1' + const json = JSON.stringify({ + _format: format, + solcVersion, + solcLongVersion, + input + }) + const id = createHash('md5').update(Buffer.from(json)).digest().toString('hex') + const buildFilename = this.joinPath(path, this.innerPath, 'build-info/' + id + '.json') + // If there are no file in buildInfoNames,it means compilation is running first time after loading Remix + if (!this.buildInfoNames[filePath]) { + // Check the existing build-info and delete all the previous build files for compiled file + await this.removeStoredBuildInfo(input, path, filePath) + this.buildInfoNames[filePath] = buildFilename + const buildData = {id, _format: format, solcVersion, solcLongVersion, input, output} + await this.call('fileManager', 'writeFile', buildFilename, JSON.stringify(buildData, null, '\t')) + } else if (this.buildInfoNames[filePath] && this.buildInfoNames[filePath] !== buildFilename) { + await this.call('fileManager', 'remove', this.buildInfoNames[filePath]) + this.buildInfoNames[filePath] = buildFilename + const buildData = {id, _format: format, solcVersion, solcLongVersion, input, output} + await this.call('fileManager', 'writeFile', buildFilename, JSON.stringify(buildData, null, '\t')) } - - _MetadataFileName (path, contractName) { - return this.joinPath(path, this.innerPath, contractName + '_metadata.json') + } + + _extractPathOf (file) { + const reg = /(.*)(\/).*/ + const path = reg.exec(file) + return path ? path[1] : '/' + } + + async _setArtefacts (content, contract, path) { + content = content || '{}' + const fileName = this._JSONFileName(path, contract.name) + const metadataFileName = this._MetadataFileName(path, contract.name) + + let metadata + try { + metadata = JSON.parse(content) + } catch (e) { + console.log(e) } - onActivation () { - const self = this - this.on('filePanel', 'setWorkspace', () => { - this.buildInfoNames = {} - }) - this.on('solidity', 'compilationFinished', async (file, source, languageVersion, data, input, version) => { - if (!await this.call('settings', 'get', 'settings/generate-contract-metadata')) return - const compiler = new CompilerAbstract(languageVersion, data, source, input) - const path = self._extractPathOf(source.target) - await this.setBuildInfo(version, input, data, path, file) - compiler.visitContracts((contract) => { - if (contract.file !== source.target) return - (async () => { - const fileName = self._JSONFileName(path, contract.name) - const content = await this.call('fileManager', 'exists', fileName) ? await this.call('fileManager', 'readFile', fileName) : null - await this._setArtefacts(content, contract, path) - })() - }) - }) - } - - // Access each file in build-info, check the input sources - // If they are all same as in current compiled file and sources includes the path of compiled file, remove old build file - async removeStoredBuildInfo (currentInput, path, filePath) { - const buildDir = this.joinPath(path, this.innerPath, 'build-info/') - if (await this.call('fileManager', 'exists', buildDir)) { - const allBuildFiles = await this.call('fileManager', 'fileList', buildDir) - const currentInputFileNames = Object.keys(currentInput.sources) - for (const fileName of allBuildFiles) { - let fileContent = await this.call('fileManager', 'readFile', fileName) - fileContent = JSON.parse(fileContent) - const inputFiles = Object.keys(fileContent.input.sources) - const inputIntersection = currentInputFileNames.filter(element => !inputFiles.includes(element)) - if (inputIntersection.length === 0 && inputFiles.includes(filePath)) await this.call('fileManager', 'remove', fileName) - } - } - } + const deploy = metadata.deploy || {} + this.networks.forEach((network) => { + deploy[network] = this._syncContext(contract, deploy[network] || {}) + }) - async setBuildInfo (version, input, output, path, filePath) { - input = JSON.parse(input) - const solcLongVersion = version.replace('.Emscripten.clang', '') - const solcVersion = solcLongVersion.substring(0, solcLongVersion.indexOf('+commit')) - const format = 'hh-sol-build-info-1' - const json = JSON.stringify({ - _format: format, - solcVersion, - solcLongVersion, - input - }) - const id = createHash('md5').update(Buffer.from(json)).digest().toString('hex') - const buildFilename = this.joinPath(path, this.innerPath, 'build-info/' + id + '.json') - // If there are no file in buildInfoNames,it means compilation is running first time after loading Remix - if (!this.buildInfoNames[filePath]) { - // Check the existing build-info and delete all the previous build files for compiled file - await this.removeStoredBuildInfo(input, path, filePath) - this.buildInfoNames[filePath] = buildFilename - const buildData = {id, _format: format, solcVersion, solcLongVersion, input, output} - await this.call('fileManager', 'writeFile', buildFilename, JSON.stringify(buildData, null, '\t')) - } else if (this.buildInfoNames[filePath] && this.buildInfoNames[filePath] !== buildFilename) { - await this.call('fileManager', 'remove', this.buildInfoNames[filePath]) - this.buildInfoNames[filePath] = buildFilename - const buildData = {id, _format: format, solcVersion, solcLongVersion, input, output} - await this.call('fileManager', 'writeFile', buildFilename, JSON.stringify(buildData, null, '\t')) - } + let parsedMetadata + try { + parsedMetadata = contract.object && contract.object.metadata ? JSON.parse(contract.object.metadata) : null + } catch (e) { + console.log(e) } - - _extractPathOf (file) { - const reg = /(.*)(\/).*/ - const path = reg.exec(file) - return path ? path[1] : '/' + if (parsedMetadata) await this.call('fileManager', 'writeFile', metadataFileName, JSON.stringify(parsedMetadata, null, '\t')) + + const data = { + deploy, + data: { + bytecode: contract.object.evm.bytecode, + deployedBytecode: contract.object.evm.deployedBytecode, + gasEstimates: contract.object.evm.gasEstimates, + methodIdentifiers: contract.object.evm.methodIdentifiers + }, + abi: contract.object.abi } - - async _setArtefacts (content, contract, path) { - content = content || '{}' - const fileName = this._JSONFileName(path, contract.name) - const metadataFileName = this._MetadataFileName(path, contract.name) - - let metadata - try { - metadata = JSON.parse(content) - } catch (e) { - console.log(e) + await this.call('fileManager', 'writeFile', fileName, JSON.stringify(data, null, '\t')) + this.emit('artefactsUpdated', fileName, contract) + } + + _syncContext (contract, metadata) { + let linkReferences = metadata.linkReferences + let autoDeployLib = metadata.autoDeployLib + if (!linkReferences) linkReferences = {} + if (autoDeployLib === undefined) autoDeployLib = true + + for (const libFile in contract.object.evm.bytecode.linkReferences) { + if (!linkReferences[libFile]) linkReferences[libFile] = {} + for (const lib in contract.object.evm.bytecode.linkReferences[libFile]) { + if (!linkReferences[libFile][lib]) { + linkReferences[libFile][lib] = '
' } - - const deploy = metadata.deploy || {} - this.networks.forEach((network) => { - deploy[network] = this._syncContext(contract, deploy[network] || {}) - }) - - let parsedMetadata - try { - parsedMetadata = contract.object && contract.object.metadata ? JSON.parse(contract.object.metadata) : null - } catch (e) { - console.log(e) - } - if (parsedMetadata) await this.call('fileManager', 'writeFile', metadataFileName, JSON.stringify(parsedMetadata, null, '\t')) - - const data = { - deploy, - data: { - bytecode: contract.object.evm.bytecode, - deployedBytecode: contract.object.evm.deployedBytecode, - gasEstimates: contract.object.evm.gasEstimates, - methodIdentifiers: contract.object.evm.methodIdentifiers - }, - abi: contract.object.abi - } - await this.call('fileManager', 'writeFile', fileName, JSON.stringify(data, null, '\t')) - this.emit('artefactsUpdated', fileName, contract) + } } - - _syncContext (contract, metadata) { - let linkReferences = metadata.linkReferences - let autoDeployLib = metadata.autoDeployLib - if (!linkReferences) linkReferences = {} - if (autoDeployLib === undefined) autoDeployLib = true - - for (const libFile in contract.object.evm.bytecode.linkReferences) { - if (!linkReferences[libFile]) linkReferences[libFile] = {} - for (const lib in contract.object.evm.bytecode.linkReferences[libFile]) { - if (!linkReferences[libFile][lib]) { - linkReferences[libFile][lib] = '
' - } - } - } - metadata.linkReferences = linkReferences - metadata.autoDeployLib = autoDeployLib - return metadata + metadata.linkReferences = linkReferences + metadata.autoDeployLib = autoDeployLib + return metadata + } + + async deployMetadataOf (contractName, fileLocation) { + let path + if (fileLocation) { + path = fileLocation.split('/') + path.pop() + path = path.join('/') + } else { + try { + path = this._extractPathOf(await this.call('fileManager', 'getCurrentFile')) + } catch (err) { + console.log(err) + throw new Error(err) + } } - - async deployMetadataOf (contractName, fileLocation) { - let path - if (fileLocation) { - path = fileLocation.split('/') - path.pop() - path = path.join('/') - } else { - try { - path = this._extractPathOf(await this.call('fileManager', 'getCurrentFile')) - } catch (err) { - console.log(err) - throw new Error(err) - } - } - try { - const { id, name } = await this.call('network', 'detectNetwork') - const fileName = this._JSONFileName(path, contractName) - try { - const content = await this.call('fileManager', 'readFile', fileName) - if (!content) return null - let metadata = JSON.parse(content) - metadata = metadata.deploy || {} - return metadata[name + ':' + id] || metadata[name] || metadata[id] || metadata[name.toLowerCase() + ':' + id] || metadata[name.toLowerCase()] - } catch (err) { - return null - } - } catch (err) { - console.log(err) - throw new Error(err) - } + try { + const { id, name } = await this.call('network', 'detectNetwork') + const fileName = this._JSONFileName(path, contractName) + try { + const content = await this.call('fileManager', 'readFile', fileName) + if (!content) return null + let metadata = JSON.parse(content) + metadata = metadata.deploy || {} + return metadata[name + ':' + id] || metadata[name] || metadata[id] || metadata[name.toLowerCase() + ':' + id] || metadata[name.toLowerCase()] + } catch (err) { + return null + } + } catch (err) { + console.log(err) + throw new Error(err) } + } - joinPath (...paths) { - paths = paths.filter((value) => value !== '').map((path) => path.replace(/^\/|\/$/g, '')) // remove first and last slash) - if (paths.length === 1) return paths[0] - return paths.join('/') - } + joinPath (...paths) { + paths = paths.filter((value) => value !== '').map((path) => path.replace(/^\/|\/$/g, '')) // remove first and last slash) + if (paths.length === 1) return paths[0] + return paths.join('/') + } } diff --git a/libs/remix-core-plugin/src/lib/constants/uups.ts b/libs/remix-core-plugin/src/lib/constants/uups.ts index 3b67ea6af9..4b3729cf86 100644 --- a/libs/remix-core-plugin/src/lib/constants/uups.ts +++ b/libs/remix-core-plugin/src/lib/constants/uups.ts @@ -9,107 +9,107 @@ export const UUPSOptimize = false export const UUPSRuns = 0 export const UUPSEvmVersion = null export const UUPSABI = [ + { + "inputs": [ + { + "internalType": "address", + "name": "_logic", + "type": "address" + }, + { + "internalType": "bytes", + "name": "_data", + "type": "bytes" + } + ], + "stateMutability": "payable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "previousAdmin", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "newAdmin", + "type": "address" + } + ], + "name": "AdminChanged", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "beacon", + "type": "address" + } + ], + "name": "BeaconUpgraded", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "implementation", + "type": "address" + } + ], + "name": "Upgraded", + "type": "event" + }, + { + "stateMutability": "payable", + "type": "fallback" + }, + { + "stateMutability": "payable", + "type": "receive" + } +] +export const UUPSfunAbi = { + name: "", + inputs: [ { - "inputs": [ - { - "internalType": "address", - "name": "_logic", - "type": "address" - }, - { - "internalType": "bytes", - "name": "_data", - "type": "bytes" - } - ], - "stateMutability": "payable", - "type": "constructor" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": false, - "internalType": "address", - "name": "previousAdmin", - "type": "address" - }, - { - "indexed": false, - "internalType": "address", - "name": "newAdmin", - "type": "address" - } - ], - "name": "AdminChanged", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "beacon", - "type": "address" - } - ], - "name": "BeaconUpgraded", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "implementation", - "type": "address" - } - ], - "name": "Upgraded", - "type": "event" - }, - { - "stateMutability": "payable", - "type": "fallback" + "internalType": "address", + "name": "_logic", + "type": "address" }, { - "stateMutability": "payable", - "type": "receive" + "internalType": "bytes", + "name": "_data", + "type": "bytes" } -] -export const UUPSfunAbi = { - name: "", - inputs: [ - { - "internalType": "address", - "name": "_logic", - "type": "address" - }, - { - "internalType": "bytes", - "name": "_data", - "type": "bytes" - } - ], - type: "constructor", - outputs: [], - stateMutability: "payable" + ], + type: "constructor", + outputs: [], + stateMutability: "payable" } export const UUPSupgradeAbi = { - "inputs": [ - { - "internalType": "address", - "name": "newImplementation", - "type": "address" - } - ], - "name": "upgradeTo", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" + "inputs": [ + { + "internalType": "address", + "name": "newImplementation", + "type": "address" + } + ], + "name": "upgradeTo", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" } export const EnableProxyURLParam = 'deployProxy' export const EnableUpgradeURLParam = 'upgradeProxy' \ No newline at end of file diff --git a/libs/remix-core-plugin/src/lib/gist-handler.ts b/libs/remix-core-plugin/src/lib/gist-handler.ts index 7c0ed92542..16f87d598a 100644 --- a/libs/remix-core-plugin/src/lib/gist-handler.ts +++ b/libs/remix-core-plugin/src/lib/gist-handler.ts @@ -7,134 +7,134 @@ interface StringByString { } const profile = { - name: 'gistHandler', - methods: ['load'], - events: [], - version: '0.0.1' + name: 'gistHandler', + methods: ['load'], + events: [], + version: '0.0.1' } type GistCallBackFn = (gistId: string) => void export class GistHandler extends Plugin { - constructor () { - super(profile) - } + constructor () { + super(profile) + } - async handleLoad (gistId: string | null, cb: GistCallBackFn) { - if (!cb) cb = () => {} + async handleLoad (gistId: string | null, cb: GistCallBackFn) { + if (!cb) cb = () => {} - let loadingFromGist = false - if (!gistId) { - loadingFromGist = true - let value - try { - value = await (() => { - return new Promise((resolve, reject) => { - const modalContent = { - id: 'gisthandler', - title: 'Load a Gist', - message: 'Enter the ID of the Gist or URL you would like to load.', - modalType: 'prompt', - okLabel: 'OK', - cancelLabel: 'Cancel', - okFn: (value) => { - setTimeout(() => resolve(value), 0) - }, - cancelFn: () => { - setTimeout(() => reject(new Error('Canceled')), 0) - }, - hideFn: () => { - setTimeout(() => reject(new Error('Hide')), 0) - } - } - this.call('notification', 'modal', modalContent) - }) - })() - } catch (e) { - // the modal has been canceled - return + let loadingFromGist = false + if (!gistId) { + loadingFromGist = true + let value + try { + value = await (() => { + return new Promise((resolve, reject) => { + const modalContent = { + id: 'gisthandler', + title: 'Load a Gist', + message: 'Enter the ID of the Gist or URL you would like to load.', + modalType: 'prompt', + okLabel: 'OK', + cancelLabel: 'Cancel', + okFn: (value) => { + setTimeout(() => resolve(value), 0) + }, + cancelFn: () => { + setTimeout(() => reject(new Error('Canceled')), 0) + }, + hideFn: () => { + setTimeout(() => reject(new Error('Hide')), 0) + } } + this.call('notification', 'modal', modalContent) + }) + })() + } catch (e) { + // the modal has been canceled + return + } - if (value !== '') { - gistId = getGistId(value) - if (gistId) { - cb(gistId) - } else { - const modalContent = { - id: 'gisthandler', - title: 'Gist load error', - message: 'Error while loading gist. Please provide a valid Gist ID or URL.' - } - this.call('notification', 'alert', modalContent) - } - } else { - const modalContent = { - id: 'gisthandlerEmpty', - title: 'Gist load error', - message: 'Error while loading gist. Id cannot be empty.' - } - this.call('notification', 'alert', modalContent) - } - return loadingFromGist + if (value !== '') { + gistId = getGistId(value) + if (gistId) { + cb(gistId) } else { - loadingFromGist = !!gistId + const modalContent = { + id: 'gisthandler', + title: 'Gist load error', + message: 'Error while loading gist. Please provide a valid Gist ID or URL.' + } + this.call('notification', 'alert', modalContent) } - if (loadingFromGist) { - cb(gistId) + } else { + const modalContent = { + id: 'gisthandlerEmpty', + title: 'Gist load error', + message: 'Error while loading gist. Id cannot be empty.' } - return loadingFromGist + this.call('notification', 'alert', modalContent) + } + return loadingFromGist + } else { + loadingFromGist = !!gistId } + if (loadingFromGist) { + cb(gistId) + } + return loadingFromGist + } - load (gistId: string | null) { - const self = this - return self.handleLoad(gistId, async (gistId: string | null) => { - let data: any - try { - data = await (await fetch(`https://api.github.com/gists/${gistId}`)).json() as any - if (!data.files) { - const modalContent = { - id: 'gisthandler', - title: 'Gist load error', - message: data.message, - modalType: 'alert', - okLabel: 'OK' - } - await this.call('notification', 'modal', modalContent) - return - } - } catch (e: any) { - const modalContent = { - id: 'gisthandler', - title: 'Gist load error', - message: e.message + load (gistId: string | null) { + const self = this + return self.handleLoad(gistId, async (gistId: string | null) => { + let data: any + try { + data = await (await fetch(`https://api.github.com/gists/${gistId}`)).json() as any + if (!data.files) { + const modalContent = { + id: 'gisthandler', + title: 'Gist load error', + message: data.message, + modalType: 'alert', + okLabel: 'OK' + } + await this.call('notification', 'modal', modalContent) + return + } + } catch (e: any) { + const modalContent = { + id: 'gisthandler', + title: 'Gist load error', + message: e.message - } - await this.call('notification', 'alert', modalContent) - return - } + } + await this.call('notification', 'alert', modalContent) + return + } - const obj: StringByString = {} - Object.keys(data.files).forEach((element) => { - const path = element.replace(/\.\.\./g, '/') - obj['/gist-' + gistId + '/' + path] = data.files[element] - }) - this.call('fileManager', 'setBatchFiles', obj, 'workspace', true, async (errorSavingFiles: any) => { - if (errorSavingFiles) { - const modalContent = { - id: 'gisthandler', - title: 'Gist load error', - message: errorSavingFiles.message || errorSavingFiles + const obj: StringByString = {} + Object.keys(data.files).forEach((element) => { + const path = element.replace(/\.\.\./g, '/') + obj['/gist-' + gistId + '/' + path] = data.files[element] + }) + this.call('fileManager', 'setBatchFiles', obj, 'workspace', true, async (errorSavingFiles: any) => { + if (errorSavingFiles) { + const modalContent = { + id: 'gisthandler', + title: 'Gist load error', + message: errorSavingFiles.message || errorSavingFiles - } - this.call('notification', 'alert', modalContent) - } - }) - }) - } + } + this.call('notification', 'alert', modalContent) + } + }) + }) + } } const getGistId = (str) => { - const idr = /[0-9A-Fa-f]{8,}/ - const match = idr.exec(str) - return match ? match[0] : null + const idr = /[0-9A-Fa-f]{8,}/ + const match = idr.exec(str) + return match ? match[0] : null } diff --git a/libs/remix-core-plugin/src/lib/helpers/fetch-etherscan.ts b/libs/remix-core-plugin/src/lib/helpers/fetch-etherscan.ts index 1592663c38..3cd5b780d2 100644 --- a/libs/remix-core-plugin/src/lib/helpers/fetch-etherscan.ts +++ b/libs/remix-core-plugin/src/lib/helpers/fetch-etherscan.ts @@ -1,66 +1,66 @@ export const fetchContractFromEtherscan = async (plugin, network, contractAddress, targetPath, shouldSetFile = true, key?) => { - let data - const compilationTargets = {} - let etherscanKey + let data + const compilationTargets = {} + let etherscanKey - if (!key) etherscanKey = await plugin.call('config', 'getAppParameter', 'etherscan-access-token') - else etherscanKey = key + if (!key) etherscanKey = await plugin.call('config', 'getAppParameter', 'etherscan-access-token') + else etherscanKey = key - if (etherscanKey) { - const endpoint = network.id == 1 ? 'api.etherscan.io' : 'api-' + network.name + '.etherscan.io' - try { - data = await fetch('https://' + endpoint + '/api?module=contract&action=getsourcecode&address=' + contractAddress + '&apikey=' + etherscanKey) - data = await data.json() - // etherscan api doc https://docs.etherscan.io/api-endpoints/contracts - if (data.message === 'OK' && data.status === "1") { - if (data.result.length) { - if (data.result[0].SourceCode === '') throw new Error(`contract not verified on Etherscan ${network.name} network`) - if (data.result[0].SourceCode.startsWith('{')) { - data.result[0].SourceCode = JSON.parse(data.result[0].SourceCode.replace(/(?:\r\n|\r|\n)/g, '').replace(/^{{/, '{').replace(/}}$/, '}')) - } - } - } else throw new Error('unable to retrieve contract data ' + data.message) - } catch (e) { - throw new Error('unable to retrieve contract data: ' + e.message) + if (etherscanKey) { + const endpoint = network.id == 1 ? 'api.etherscan.io' : 'api-' + network.name + '.etherscan.io' + try { + data = await fetch('https://' + endpoint + '/api?module=contract&action=getsourcecode&address=' + contractAddress + '&apikey=' + etherscanKey) + data = await data.json() + // etherscan api doc https://docs.etherscan.io/api-endpoints/contracts + if (data.message === 'OK' && data.status === "1") { + if (data.result.length) { + if (data.result[0].SourceCode === '') throw new Error(`contract not verified on Etherscan ${network.name} network`) + if (data.result[0].SourceCode.startsWith('{')) { + data.result[0].SourceCode = JSON.parse(data.result[0].SourceCode.replace(/(?:\r\n|\r|\n)/g, '').replace(/^{{/, '{').replace(/}}$/, '}')) + } } - } else throw new Error('unable to try fetching the source code from etherscan: etherscan access token not found. please go to the Remix settings page and provide an access token.') - - if (!data || !data.result) { - return null + } else throw new Error('unable to retrieve contract data ' + data.message) + } catch (e) { + throw new Error('unable to retrieve contract data: ' + e.message) } + } else throw new Error('unable to try fetching the source code from etherscan: etherscan access token not found. please go to the Remix settings page and provide an access token.') - if (typeof data.result[0].SourceCode === 'string') { - const fileName = `${targetPath}/${data.result[0].ContractName}.sol` - if (shouldSetFile) await plugin.call('fileManager', 'setFile', fileName, data.result[0].SourceCode) - compilationTargets[fileName] = { content: data.result[0].SourceCode } - } else if (data.result[0].SourceCode && typeof data.result[0].SourceCode == 'object') { - const sources = data.result[0].SourceCode.sources + if (!data || !data.result) { + return null + } + + if (typeof data.result[0].SourceCode === 'string') { + const fileName = `${targetPath}/${data.result[0].ContractName}.sol` + if (shouldSetFile) await plugin.call('fileManager', 'setFile', fileName, data.result[0].SourceCode) + compilationTargets[fileName] = { content: data.result[0].SourceCode } + } else if (data.result[0].SourceCode && typeof data.result[0].SourceCode == 'object') { + const sources = data.result[0].SourceCode.sources for (let [file, source] of Object.entries(sources)) { // eslint-disable-line - file = file.replace('browser/', '') // should be fixed in the remix IDE end. - file = file.replace(/^\//g, '') // remove first slash. - if (await plugin.call('contentImport', 'isExternalUrl', file)) { - // nothing to do, the compiler callback will handle those - } else { - const path = `${targetPath}/${file}` - const content = (source as any).content - if (shouldSetFile) await plugin.call('fileManager', 'setFile', path, content) - compilationTargets[path] = { content } - } - } - } - let runs = 0 - try { - runs = parseInt(data.result[0].Runs) - } catch (e) { } - const settings = { - version: data.result[0].CompilerVersion.replace(/^v/, ''), - language: 'Solidity', - evmVersion: data.result[0].EVMVersion.toLowerCase(), - optimize: data.result[0].OptimizationUsed === '1', - runs - } - return { - settings, - compilationTargets + file = file.replace('browser/', '') // should be fixed in the remix IDE end. + file = file.replace(/^\//g, '') // remove first slash. + if (await plugin.call('contentImport', 'isExternalUrl', file)) { + // nothing to do, the compiler callback will handle those + } else { + const path = `${targetPath}/${file}` + const content = (source as any).content + if (shouldSetFile) await plugin.call('fileManager', 'setFile', path, content) + compilationTargets[path] = { content } + } } + } + let runs = 0 + try { + runs = parseInt(data.result[0].Runs) + } catch (e) { } + const settings = { + version: data.result[0].CompilerVersion.replace(/^v/, ''), + language: 'Solidity', + evmVersion: data.result[0].EVMVersion.toLowerCase(), + optimize: data.result[0].OptimizationUsed === '1', + runs + } + return { + settings, + compilationTargets + } } \ No newline at end of file diff --git a/libs/remix-core-plugin/src/lib/helpers/fetch-sourcify.ts b/libs/remix-core-plugin/src/lib/helpers/fetch-sourcify.ts index 5765e7e9d6..1c9637005f 100644 --- a/libs/remix-core-plugin/src/lib/helpers/fetch-sourcify.ts +++ b/libs/remix-core-plugin/src/lib/helpers/fetch-sourcify.ts @@ -1,46 +1,46 @@ export const fetchContractFromSourcify = async (plugin, network, contractAddress, targetPath) => { - let data - const compilationTargets = {} + let data + const compilationTargets = {} - try { - data = await plugin.call('sourcify', 'fetchByNetwork', contractAddress, network.id) - } catch (e) { - console.log(e) - } + try { + data = await plugin.call('sourcify', 'fetchByNetwork', contractAddress, network.id) + } catch (e) { + console.log(e) + } - if (!data || !data.metadata) { - return null - } + if (!data || !data.metadata) { + return null + } - // set the solidity contract code using metadata - await plugin.call('fileManager', 'setFile', `${targetPath}/metadata.json`, JSON.stringify(data.metadata, null, '\t')) - for (let file in data.metadata.sources) { - const urls = data.metadata.sources[file].urls - for (const url of urls) { - if (url.includes('ipfs')) { - const stdUrl = `ipfs://${url.split('/')[2]}` - const source = await plugin.call('contentImport', 'resolve', stdUrl) - file = file.replace('browser/', '') // should be fixed in the remix IDE end. - if (await plugin.call('contentImport', 'isExternalUrl', file)) { - // nothing to do, the compiler callback will handle those - } else { - const path = `${targetPath}/${file}` - await plugin.call('fileManager', 'setFile', path, source.content) - compilationTargets[path] = { content: source.content } - } - break - } + // set the solidity contract code using metadata + await plugin.call('fileManager', 'setFile', `${targetPath}/metadata.json`, JSON.stringify(data.metadata, null, '\t')) + for (let file in data.metadata.sources) { + const urls = data.metadata.sources[file].urls + for (const url of urls) { + if (url.includes('ipfs')) { + const stdUrl = `ipfs://${url.split('/')[2]}` + const source = await plugin.call('contentImport', 'resolve', stdUrl) + file = file.replace('browser/', '') // should be fixed in the remix IDE end. + if (await plugin.call('contentImport', 'isExternalUrl', file)) { + // nothing to do, the compiler callback will handle those + } else { + const path = `${targetPath}/${file}` + await plugin.call('fileManager', 'setFile', path, source.content) + compilationTargets[path] = { content: source.content } } + break + } } - const settings = { - version: data.metadata.compiler.version, - language: data.metadata.language, - evmVersion: data.metadata.settings.evmVersion, - optimize: data.metadata.settings.optimizer.enabled, - runs: data.metadata.settings.optimizer.runs - } - return { - settings, - compilationTargets - } + } + const settings = { + version: data.metadata.compiler.version, + language: data.metadata.language, + evmVersion: data.metadata.settings.evmVersion, + optimize: data.metadata.settings.optimizer.enabled, + runs: data.metadata.settings.optimizer.runs + } + return { + settings, + compilationTargets + } } diff --git a/libs/remix-core-plugin/src/lib/link-libraries.ts b/libs/remix-core-plugin/src/lib/link-libraries.ts index 84d50d5a14..b5857b7161 100644 --- a/libs/remix-core-plugin/src/lib/link-libraries.ts +++ b/libs/remix-core-plugin/src/lib/link-libraries.ts @@ -4,71 +4,71 @@ import { Plugin } from '@remixproject/engine'; import { ContractData } from '../types/contract'; const profileDeployLibraries = { - name: 'deploy-libraries', - displayName: 'deploy-libraries', - description: 'deploy-libraries', - methods: ['isConcerned', 'execute'] + name: 'deploy-libraries', + displayName: 'deploy-libraries', + description: 'deploy-libraries', + methods: ['isConcerned', 'execute'] }; const profileLinkLibraries = { - name: 'link-libraries', - displayName: 'link-libraries', - description: 'link-libraries', - methods: ['isConcerned', 'execute'] + name: 'link-libraries', + displayName: 'link-libraries', + description: 'link-libraries', + methods: ['isConcerned', 'execute'] }; export class DeployLibraries extends Plugin { - blockchain: any + blockchain: any - constructor(blockchain) { - super(profileDeployLibraries) - this.blockchain = blockchain - } + constructor(blockchain) { + super(profileDeployLibraries) + this.blockchain = blockchain + } - async isConcerned(contractData: ContractData): Promise { - return Object.keys(contractData.bytecodeLinkReferences).length > 0; - } + async isConcerned(contractData: ContractData): Promise { + return Object.keys(contractData.bytecodeLinkReferences).length > 0; + } - execute(contractData: ContractData, contractMetadata: any, compiledContracts: any) { - // we deploy libraries - // and return the linked bytecode - return new Promise((resolve, reject) => { - txFormat.linkBytecode(contractData.object, compiledContracts, (error, bytecode) => { - if (error) return reject (error) - // final Callback - resolve(bytecode) - }, - (message) => { - // step Callback - console.log(message) - }, (data, runTxCallback) => { - // deploy library Callback - // called for libraries deployment - this.blockchain.runTx(data, () => {}, () => {}, () => {}, runTxCallback) - }) - }) - } + execute(contractData: ContractData, contractMetadata: any, compiledContracts: any) { + // we deploy libraries + // and return the linked bytecode + return new Promise((resolve, reject) => { + txFormat.linkBytecode(contractData.object, compiledContracts, (error, bytecode) => { + if (error) return reject (error) + // final Callback + resolve(bytecode) + }, + (message) => { + // step Callback + console.log(message) + }, (data, runTxCallback) => { + // deploy library Callback + // called for libraries deployment + this.blockchain.runTx(data, () => {}, () => {}, () => {}, runTxCallback) + }) + }) + } } export class LinkLibraries extends Plugin { - blockchain: any - constructor(blockchain) { - super(profileLinkLibraries) - this.blockchain = blockchain - } + blockchain: any + constructor(blockchain) { + super(profileLinkLibraries) + this.blockchain = blockchain + } - async isConcerned(contractData: ContractData): Promise { - return Object.keys(contractData.bytecodeLinkReferences).length > 0; - } + async isConcerned(contractData: ContractData): Promise { + return Object.keys(contractData.bytecodeLinkReferences).length > 0; + } - execute(contractData: ContractData, contractMetadata: any, compiledContracts: any) { - // we just link libraries - // and return the linked bytecode - return new Promise((resolve, reject) => { - txFormat.linkLibraries(contractData, contractMetadata.linkReferences, contractData.bytecodeLinkReferences, (error, bytecode) => { - if (error) return reject(error) - resolve(bytecode) - }) - }) - } + execute(contractData: ContractData, contractMetadata: any, compiledContracts: any) { + // we just link libraries + // and return the linked bytecode + return new Promise((resolve, reject) => { + txFormat.linkLibraries(contractData, contractMetadata.linkReferences, contractData.bytecodeLinkReferences, (error, bytecode) => { + if (error) return reject(error) + resolve(bytecode) + }) + }) + } } diff --git a/libs/remix-core-plugin/src/lib/offset-line-to-column-converter.ts b/libs/remix-core-plugin/src/lib/offset-line-to-column-converter.ts index 0c416b2953..15da019de0 100644 --- a/libs/remix-core-plugin/src/lib/offset-line-to-column-converter.ts +++ b/libs/remix-core-plugin/src/lib/offset-line-to-column-converter.ts @@ -4,24 +4,24 @@ import { Plugin } from '@remixproject/engine' import { sourceMappingDecoder } from '@remix-project/remix-debug' const profile = { - name: 'offsetToLineColumnConverter', - methods: ['offsetToLineColumn'], - events: [], - version: '0.0.1' + name: 'offsetToLineColumnConverter', + methods: ['offsetToLineColumn'], + events: [], + version: '0.0.1' } export class OffsetToLineColumnConverter extends Plugin { - lineBreakPositionsByContent: Record> - sourceMappingDecoder: any - offsetConvertion: any - constructor () { - super(profile) - this.lineBreakPositionsByContent = {} - this.offsetConvertion = {} - this.sourceMappingDecoder = sourceMappingDecoder - } + lineBreakPositionsByContent: Record> + sourceMappingDecoder: any + offsetConvertion: any + constructor () { + super(profile) + this.lineBreakPositionsByContent = {} + this.offsetConvertion = {} + this.sourceMappingDecoder = sourceMappingDecoder + } - /** + /** * Convert offset representation with line/column representation. * This is also used to resolve the content: * @arg file is the index of the file in the content sources array and content sources array does have filename as key and not index. @@ -31,58 +31,58 @@ export class OffsetToLineColumnConverter extends Plugin { * @param {Object.} sources - Map of content sources * @param {Object.} asts - Map of content sources */ - offsetToLineColumn (rawLocation, file, sources, asts) { - if (!this.lineBreakPositionsByContent[file]) { - const sourcesArray = Object.keys(sources) - if (!asts || (file === 0 && sourcesArray.length === 1)) { - // if we don't have ast, we process the only one available content (applicable also for compiler older than 0.4.12) - this.lineBreakPositionsByContent[file] = this.sourceMappingDecoder.getLinebreakPositions(sources[sourcesArray[0]].content) - } else { - for (const filename in asts) { - const source = asts[filename] - if (source.id === file) { - this.lineBreakPositionsByContent[file] = this.sourceMappingDecoder.getLinebreakPositions(sources[filename].content) - break - } - } - } + offsetToLineColumn (rawLocation, file, sources, asts) { + if (!this.lineBreakPositionsByContent[file]) { + const sourcesArray = Object.keys(sources) + if (!asts || (file === 0 && sourcesArray.length === 1)) { + // if we don't have ast, we process the only one available content (applicable also for compiler older than 0.4.12) + this.lineBreakPositionsByContent[file] = this.sourceMappingDecoder.getLinebreakPositions(sources[sourcesArray[0]].content) + } else { + for (const filename in asts) { + const source = asts[filename] + if (source.id === file) { + this.lineBreakPositionsByContent[file] = this.sourceMappingDecoder.getLinebreakPositions(sources[filename].content) + break + } } + } + } - const token = `${rawLocation.start}:${rawLocation.length}:${file}` - if (this.offsetConvertion[token]) { - return this.offsetConvertion[token] - } else { - const convertion = this.sourceMappingDecoder.convertOffsetToLineColumn(rawLocation, this.lineBreakPositionsByContent[file]) - this.offsetConvertion[token] = convertion - return convertion - } + const token = `${rawLocation.start}:${rawLocation.length}:${file}` + if (this.offsetConvertion[token]) { + return this.offsetConvertion[token] + } else { + const convertion = this.sourceMappingDecoder.convertOffsetToLineColumn(rawLocation, this.lineBreakPositionsByContent[file]) + this.offsetConvertion[token] = convertion + return convertion } + } - /** + /** * Convert offset representation with line/column representation. * @param {{start, length}} rawLocation - offset location * @param {number} file - The index where to find the source in the sources parameters * @param {string} content - source */ - offsetToLineColumnWithContent (rawLocation, file, content) { - this.lineBreakPositionsByContent[file] = this.sourceMappingDecoder.getLinebreakPositions(content) - return this.sourceMappingDecoder.convertOffsetToLineColumn(rawLocation, this.lineBreakPositionsByContent[file]) - } + offsetToLineColumnWithContent (rawLocation, file, content) { + this.lineBreakPositionsByContent[file] = this.sourceMappingDecoder.getLinebreakPositions(content) + return this.sourceMappingDecoder.convertOffsetToLineColumn(rawLocation, this.lineBreakPositionsByContent[file]) + } - /** + /** * Clear the cache */ - clear () { - this.lineBreakPositionsByContent = {} - this.offsetConvertion = {} - } + clear () { + this.lineBreakPositionsByContent = {} + this.offsetConvertion = {} + } - /** + /** * called by plugin API */ - activate () { - this.on('solidity', 'compilationFinished', (success, data, source, input, version) => { - this.clear() - }) - } + activate () { + this.on('solidity', 'compilationFinished', (success, data, source, input, version) => { + this.clear() + }) + } } diff --git a/libs/remix-core-plugin/src/lib/openzeppelin-proxy.ts b/libs/remix-core-plugin/src/lib/openzeppelin-proxy.ts index 47db52425f..71aa68d83d 100644 --- a/libs/remix-core-plugin/src/lib/openzeppelin-proxy.ts +++ b/libs/remix-core-plugin/src/lib/openzeppelin-proxy.ts @@ -3,124 +3,124 @@ import { ContractAST, ContractSources, DeployOptions } from '../types/contract' import { EnableProxyURLParam, EnableUpgradeURLParam, UUPS, UUPSABI, UUPSBytecode, UUPSfunAbi, UUPSupgradeAbi } from './constants/uups' const proxyProfile = { - name: 'openzeppelin-proxy', - displayName: 'openzeppelin-proxy', - description: 'openzeppelin-proxy', - methods: ['isConcerned', 'executeUUPSProxy', 'executeUUPSContractUpgrade', 'getProxyOptions', 'getUpgradeOptions'] + name: 'openzeppelin-proxy', + displayName: 'openzeppelin-proxy', + description: 'openzeppelin-proxy', + methods: ['isConcerned', 'executeUUPSProxy', 'executeUUPSContractUpgrade', 'getProxyOptions', 'getUpgradeOptions'] }; export class OpenZeppelinProxy extends Plugin { - blockchain: any - kind: 'UUPS' | 'Transparent' - constructor(blockchain) { - super(proxyProfile) - this.blockchain = blockchain + blockchain: any + kind: 'UUPS' | 'Transparent' + constructor(blockchain) { + super(proxyProfile) + this.blockchain = blockchain + } + + async isConcerned(ast: ContractAST = {} as ContractAST): Promise { + // check in the AST if it's an upgradable contract + const UUPSSymbol = ast.exportedSymbols && ast.exportedSymbols[UUPS] ? ast.exportedSymbols[UUPS][0] : null + + if (UUPSSymbol) { + this.kind = 'UUPS' + return true } + // + // else if transparent contract run check true/false + // + return false + } - async isConcerned(ast: ContractAST = {} as ContractAST): Promise { - // check in the AST if it's an upgradable contract - const UUPSSymbol = ast.exportedSymbols && ast.exportedSymbols[UUPS] ? ast.exportedSymbols[UUPS][0] : null - - if (UUPSSymbol) { - this.kind = 'UUPS' - return true - } - // - // else if transparent contract run check true/false - // - return false - } - - async getProxyOptions (data: ContractSources, file: string): Promise<{ [name: string]: DeployOptions }> { - const contracts = data.contracts[file] - const ast = data.sources[file].ast + async getProxyOptions (data: ContractSources, file: string): Promise<{ [name: string]: DeployOptions }> { + const contracts = data.contracts[file] + const ast = data.sources[file].ast - if (this.kind === 'UUPS') { - const options = await (this.getUUPSContractOptions(contracts, ast, file)) + if (this.kind === 'UUPS') { + const options = await (this.getUUPSContractOptions(contracts, ast, file)) - return options - } + return options } + } - async getUUPSContractOptions (contracts, ast, file) { - const options = {} + async getUUPSContractOptions (contracts, ast, file) { + const options = {} - await Promise.all(Object.keys(contracts).map(async (name) => { - if (ast) { - const UUPSSymbol = ast.exportedSymbols[UUPS] ? ast.exportedSymbols[UUPS][0] : null + await Promise.all(Object.keys(contracts).map(async (name) => { + if (ast) { + const UUPSSymbol = ast.exportedSymbols[UUPS] ? ast.exportedSymbols[UUPS][0] : null - await Promise.all(ast.absolutePath === file && ast.nodes.map(async (node) => { - if (node.name === name && node.linearizedBaseContracts.includes(UUPSSymbol)) { - const abi = contracts[name].abi - const initializeInput = abi.find(node => node.name === 'initialize') - const isDeployWithProxyEnabled: boolean = await this.call('config', 'getAppParameter', EnableProxyURLParam) || false - const isDeployWithUpgradeEnabled: boolean = await this.call('config', 'getAppParameter', EnableUpgradeURLParam) || false + await Promise.all(ast.absolutePath === file && ast.nodes.map(async (node) => { + if (node.name === name && node.linearizedBaseContracts.includes(UUPSSymbol)) { + const abi = contracts[name].abi + const initializeInput = abi.find(node => node.name === 'initialize') + const isDeployWithProxyEnabled: boolean = await this.call('config', 'getAppParameter', EnableProxyURLParam) || false + const isDeployWithUpgradeEnabled: boolean = await this.call('config', 'getAppParameter', EnableUpgradeURLParam) || false - options[name] = { - options: [{ title: 'Deploy with Proxy', active: isDeployWithProxyEnabled }, { title: 'Upgrade with Proxy', active: isDeployWithUpgradeEnabled }], - initializeOptions: { - inputs: initializeInput, - initializeInputs: initializeInput ? this.blockchain.getInputs(initializeInput) : null - } - } - } - })) + options[name] = { + options: [{ title: 'Deploy with Proxy', active: isDeployWithProxyEnabled }, { title: 'Upgrade with Proxy', active: isDeployWithUpgradeEnabled }], + initializeOptions: { + inputs: initializeInput, + initializeInputs: initializeInput ? this.blockchain.getInputs(initializeInput) : null + } } + } })) - return options - } - - async executeUUPSProxy(implAddress: string, args: string | string [] = '', initializeABI, implementationContractObject): Promise { - // deploy the proxy, or use an existing one - if (!initializeABI) throw new Error('Cannot deploy proxy: Missing initialize ABI') - args = args === '' ? [] : args - const _data = await this.blockchain.getEncodedFunctionHex(args || [], initializeABI) - - if (this.kind === 'UUPS') this.deployUUPSProxy(implAddress, _data, implementationContractObject) - } - - async executeUUPSContractUpgrade (proxyAddress: string, newImplAddress: string, newImplementationContractObject): Promise { - if (!newImplAddress) throw new Error('Cannot upgrade: Missing implementation address') - if (!proxyAddress) throw new Error('Cannot upgrade: Missing proxy address') - - if (this.kind === 'UUPS') this.upgradeUUPSProxy(proxyAddress, newImplAddress, newImplementationContractObject) - } - - async deployUUPSProxy (implAddress: string, _data: string, implementationContractObject): Promise { - const args = [implAddress, _data] - const constructorData = await this.blockchain.getEncodedParams(args, UUPSfunAbi) - const proxyName = 'ERC1967Proxy' - const data = { - contractABI: UUPSABI, - contractByteCode: UUPSBytecode, - contractName: proxyName, - funAbi: UUPSfunAbi, - funArgs: args, - linkReferences: {}, - dataHex: UUPSBytecode + constructorData.replace('0x', '') - } - - // re-use implementation contract's ABI for UI display in udapp and change name to proxy name. - implementationContractObject.contractName = implementationContractObject.name - implementationContractObject.implementationAddress = implAddress - implementationContractObject.name = proxyName - this.blockchain.deployProxy(data, implementationContractObject) + } + })) + return options + } + + async executeUUPSProxy(implAddress: string, args: string | string [] = '', initializeABI, implementationContractObject): Promise { + // deploy the proxy, or use an existing one + if (!initializeABI) throw new Error('Cannot deploy proxy: Missing initialize ABI') + args = args === '' ? [] : args + const _data = await this.blockchain.getEncodedFunctionHex(args || [], initializeABI) + + if (this.kind === 'UUPS') this.deployUUPSProxy(implAddress, _data, implementationContractObject) + } + + async executeUUPSContractUpgrade (proxyAddress: string, newImplAddress: string, newImplementationContractObject): Promise { + if (!newImplAddress) throw new Error('Cannot upgrade: Missing implementation address') + if (!proxyAddress) throw new Error('Cannot upgrade: Missing proxy address') + + if (this.kind === 'UUPS') this.upgradeUUPSProxy(proxyAddress, newImplAddress, newImplementationContractObject) + } + + async deployUUPSProxy (implAddress: string, _data: string, implementationContractObject): Promise { + const args = [implAddress, _data] + const constructorData = await this.blockchain.getEncodedParams(args, UUPSfunAbi) + const proxyName = 'ERC1967Proxy' + const data = { + contractABI: UUPSABI, + contractByteCode: UUPSBytecode, + contractName: proxyName, + funAbi: UUPSfunAbi, + funArgs: args, + linkReferences: {}, + dataHex: UUPSBytecode + constructorData.replace('0x', '') } - async upgradeUUPSProxy (proxyAddress: string, newImplAddress: string, newImplementationContractObject): Promise { - const fnData = await this.blockchain.getEncodedFunctionHex([newImplAddress], UUPSupgradeAbi) - const proxyName = 'ERC1967Proxy' - const data = { - contractABI: UUPSABI, - contractName: proxyName, - funAbi: UUPSupgradeAbi, - funArgs: [newImplAddress], - linkReferences: {}, - dataHex: fnData.replace('0x', '') - } - // re-use implementation contract's ABI for UI display in udapp and change name to proxy name. - newImplementationContractObject.contractName = newImplementationContractObject.name - newImplementationContractObject.implementationAddress = newImplAddress - newImplementationContractObject.name = proxyName - this.blockchain.upgradeProxy(proxyAddress, newImplAddress, data, newImplementationContractObject) + // re-use implementation contract's ABI for UI display in udapp and change name to proxy name. + implementationContractObject.contractName = implementationContractObject.name + implementationContractObject.implementationAddress = implAddress + implementationContractObject.name = proxyName + this.blockchain.deployProxy(data, implementationContractObject) + } + + async upgradeUUPSProxy (proxyAddress: string, newImplAddress: string, newImplementationContractObject): Promise { + const fnData = await this.blockchain.getEncodedFunctionHex([newImplAddress], UUPSupgradeAbi) + const proxyName = 'ERC1967Proxy' + const data = { + contractABI: UUPSABI, + contractName: proxyName, + funAbi: UUPSupgradeAbi, + funArgs: [newImplAddress], + linkReferences: {}, + dataHex: fnData.replace('0x', '') } + // re-use implementation contract's ABI for UI display in udapp and change name to proxy name. + newImplementationContractObject.contractName = newImplementationContractObject.name + newImplementationContractObject.implementationAddress = newImplAddress + newImplementationContractObject.name = proxyName + this.blockchain.upgradeProxy(proxyAddress, newImplAddress, data, newImplementationContractObject) + } } \ No newline at end of file diff --git a/libs/remix-debug/src/Ethdebugger.ts b/libs/remix-debug/src/Ethdebugger.ts index e3a546b1b4..3792eaf619 100644 --- a/libs/remix-debug/src/Ethdebugger.ts +++ b/libs/remix-debug/src/Ethdebugger.ts @@ -22,180 +22,180 @@ import { extractStateVariables } from './solidity-decoder/stateDecoder' * @param {Map} opts - { function compilationResult } // */ export class Ethdebugger { - compilationResult - web3 - opts - event - tx - traceManager - codeManager - solidityProxy - storageResolver - callTree - breakpointManager - offsetToLineColumnConverter - - constructor (opts) { - this.compilationResult = opts.compilationResult || function (contractAddress) { return null } - this.offsetToLineColumnConverter = opts.offsetToLineColumnConverter - this.web3 = opts.web3 - this.opts = opts - - this.event = new EventManager() - this.traceManager = new TraceManager({ web3: this.web3 }) - this.codeManager = new CodeManager(this.traceManager) - this.solidityProxy = new SolidityProxy({ - getCurrentCalledAddressAt: this.traceManager.getCurrentCalledAddressAt.bind(this.traceManager), - getCode: this.codeManager.getCode.bind(this.codeManager), - compilationResult: this.compilationResult - }) - this.storageResolver = null - - const includeLocalVariables = true - this.callTree = new InternalCallTree(this.event, - this.traceManager, - this.solidityProxy, - this.codeManager, - { ...opts, includeLocalVariables }, - this.offsetToLineColumnConverter) - } - - setManagers () { - this.traceManager = new TraceManager({ web3: this.web3 }) - this.codeManager = new CodeManager(this.traceManager) - this.solidityProxy = new SolidityProxy({ - getCurrentCalledAddressAt: this.traceManager.getCurrentCalledAddressAt.bind(this.traceManager), - getCode: this.codeManager.getCode.bind(this.codeManager), - compilationResult: this.compilationResult - }) - this.storageResolver = null - const includeLocalVariables = true - - this.callTree = new InternalCallTree(this.event, - this.traceManager, - this.solidityProxy, - this.codeManager, - { ...this.opts, includeLocalVariables }, - this.offsetToLineColumnConverter) - } - - resolveStep (index) { - this.codeManager.resolveStep(index, this.tx) - } - - async sourceLocationFromVMTraceIndex (address, stepIndex) { - const compilationResult = await this.compilationResult(address) - return this.callTree.sourceLocationTracker.getSourceLocationFromVMTraceIndex(address, stepIndex, compilationResult.data.contracts) - } - - async getValidSourceLocationFromVMTraceIndex (address, stepIndex) { - const compilationResult = await this.compilationResult(address) - return this.callTree.sourceLocationTracker.getValidSourceLocationFromVMTraceIndex(address, stepIndex, compilationResult.data.contracts) - } - - async sourceLocationFromInstructionIndex (address, instIndex, callback) { - const compilationResult = await this.compilationResult(address) - return this.callTree.sourceLocationTracker.getSourceLocationFromInstructionIndex(address, instIndex, compilationResult.data.contracts) - } - - /* breakpoint */ - setBreakpointManager (breakpointManager) { - this.breakpointManager = breakpointManager - } - - /* decode locals */ - extractLocalsAt (step) { - return this.callTree.findScope(step) - } - - async decodeLocalVariableByIdAtCurrentStep (step: number, id: number) { - const variable = this.callTree.getLocalVariableById(id) - if (!variable) return null - const stack = this.traceManager.getStackAt(step) - const memory = this.traceManager.getMemoryAt(step) - const address = this.traceManager.getCurrentCalledAddressAt(step) - const calldata = this.traceManager.getCallDataAt(step) + compilationResult + web3 + opts + event + tx + traceManager + codeManager + solidityProxy + storageResolver + callTree + breakpointManager + offsetToLineColumnConverter + + constructor (opts) { + this.compilationResult = opts.compilationResult || function (contractAddress) { return null } + this.offsetToLineColumnConverter = opts.offsetToLineColumnConverter + this.web3 = opts.web3 + this.opts = opts + + this.event = new EventManager() + this.traceManager = new TraceManager({ web3: this.web3 }) + this.codeManager = new CodeManager(this.traceManager) + this.solidityProxy = new SolidityProxy({ + getCurrentCalledAddressAt: this.traceManager.getCurrentCalledAddressAt.bind(this.traceManager), + getCode: this.codeManager.getCode.bind(this.codeManager), + compilationResult: this.compilationResult + }) + this.storageResolver = null + + const includeLocalVariables = true + this.callTree = new InternalCallTree(this.event, + this.traceManager, + this.solidityProxy, + this.codeManager, + { ...opts, includeLocalVariables }, + this.offsetToLineColumnConverter) + } + + setManagers () { + this.traceManager = new TraceManager({ web3: this.web3 }) + this.codeManager = new CodeManager(this.traceManager) + this.solidityProxy = new SolidityProxy({ + getCurrentCalledAddressAt: this.traceManager.getCurrentCalledAddressAt.bind(this.traceManager), + getCode: this.codeManager.getCode.bind(this.codeManager), + compilationResult: this.compilationResult + }) + this.storageResolver = null + const includeLocalVariables = true + + this.callTree = new InternalCallTree(this.event, + this.traceManager, + this.solidityProxy, + this.codeManager, + { ...this.opts, includeLocalVariables }, + this.offsetToLineColumnConverter) + } + + resolveStep (index) { + this.codeManager.resolveStep(index, this.tx) + } + + async sourceLocationFromVMTraceIndex (address, stepIndex) { + const compilationResult = await this.compilationResult(address) + return this.callTree.sourceLocationTracker.getSourceLocationFromVMTraceIndex(address, stepIndex, compilationResult.data.contracts) + } + + async getValidSourceLocationFromVMTraceIndex (address, stepIndex) { + const compilationResult = await this.compilationResult(address) + return this.callTree.sourceLocationTracker.getValidSourceLocationFromVMTraceIndex(address, stepIndex, compilationResult.data.contracts) + } + + async sourceLocationFromInstructionIndex (address, instIndex, callback) { + const compilationResult = await this.compilationResult(address) + return this.callTree.sourceLocationTracker.getSourceLocationFromInstructionIndex(address, instIndex, compilationResult.data.contracts) + } + + /* breakpoint */ + setBreakpointManager (breakpointManager) { + this.breakpointManager = breakpointManager + } + + /* decode locals */ + extractLocalsAt (step) { + return this.callTree.findScope(step) + } + + async decodeLocalVariableByIdAtCurrentStep (step: number, id: number) { + const variable = this.callTree.getLocalVariableById(id) + if (!variable) return null + const stack = this.traceManager.getStackAt(step) + const memory = this.traceManager.getMemoryAt(step) + const address = this.traceManager.getCurrentCalledAddressAt(step) + const calldata = this.traceManager.getCallDataAt(step) + const storageViewer = new StorageViewer({ stepIndex: step, tx: this.tx, address: address }, this.storageResolver, this.traceManager) + return await variable.type.decodeFromStack(variable.stackDepth, stack, memory, storageViewer, calldata, null, variable) + } + + async decodeStateVariableByIdAtCurrentStep (step: number, id: number) { + const stateVars = await this.solidityProxy.extractStateVariablesAt(step) + const variable = stateVars.filter((el) => el.variable.id === id) + if (variable && variable.length) { + const state = await this.decodeStateAt(step, variable) + return state[variable[0].name] + } + return null + } + + async decodeLocalsAt (step, sourceLocation, callback) { + try { + const stack = this.traceManager.getStackAt(step) + const memory = this.traceManager.getMemoryAt(step) + const address = this.traceManager.getCurrentCalledAddressAt(step) + const calldata = this.traceManager.getCallDataAt(step) + try { const storageViewer = new StorageViewer({ stepIndex: step, tx: this.tx, address: address }, this.storageResolver, this.traceManager) - return await variable.type.decodeFromStack(variable.stackDepth, stack, memory, storageViewer, calldata, null, variable) - } - - async decodeStateVariableByIdAtCurrentStep (step: number, id: number) { - const stateVars = await this.solidityProxy.extractStateVariablesAt(step) - const variable = stateVars.filter((el) => el.variable.id === id) - if (variable && variable.length) { - const state = await this.decodeStateAt(step, variable) - return state[variable[0].name] - } - return null - } - - async decodeLocalsAt (step, sourceLocation, callback) { - try { - const stack = this.traceManager.getStackAt(step) - const memory = this.traceManager.getMemoryAt(step) - const address = this.traceManager.getCurrentCalledAddressAt(step) - const calldata = this.traceManager.getCallDataAt(step) - try { - const storageViewer = new StorageViewer({ stepIndex: step, tx: this.tx, address: address }, this.storageResolver, this.traceManager) - const locals = await localDecoder.solidityLocals(step, this.callTree, stack, memory, storageViewer, calldata, sourceLocation, null) - if (locals['error']) { - return callback(locals['error']) - } - return callback(null, locals) - } catch (e) { - callback(e.message) - } - } catch (error) { - callback(error) - } - } - - /* decode state */ - async extractStateAt (step) { - return await this.solidityProxy.extractStateVariablesAt(step) - } - - async decodeStateAt (step, stateVars, callback?) { - try { - callback = callback || (() => {}) - const address = this.traceManager.getCurrentCalledAddressAt(step) - const storageViewer = new StorageViewer({ stepIndex: step, tx: this.tx, address: address }, this.storageResolver, this.traceManager) - const result = await stateDecoder.decodeState(stateVars, storageViewer) - callback(result) - return result - } catch (error) { - callback(error) + const locals = await localDecoder.solidityLocals(step, this.callTree, stack, memory, storageViewer, calldata, sourceLocation, null) + if (locals['error']) { + return callback(locals['error']) } - } - - storageViewAt (step, address) { - return new StorageViewer({ stepIndex: step, tx: this.tx, address: address }, this.storageResolver, this.traceManager) - } - - updateWeb3 (web3) { - this.web3 = web3 - this.setManagers() - } - - unLoad () { - this.traceManager.init() - this.codeManager.clear() - this.solidityProxy.reset() - this.event.trigger('traceUnloaded') - } - - async debug (tx) { - if (this.traceManager.isLoading) { - return - } - tx.to = tx.to || contractCreationToken('0') - this.tx = tx - - await this.traceManager.resolveTrace(tx) - this.event.trigger('newTraceLoaded', [this.traceManager.trace]) - if (this.breakpointManager && this.breakpointManager.hasBreakpoint()) { - this.breakpointManager.jumpNextBreakpoint(false) - } - this.storageResolver = new StorageResolver({ web3: this.traceManager.web3 }) - } + return callback(null, locals) + } catch (e) { + callback(e.message) + } + } catch (error) { + callback(error) + } + } + + /* decode state */ + async extractStateAt (step) { + return await this.solidityProxy.extractStateVariablesAt(step) + } + + async decodeStateAt (step, stateVars, callback?) { + try { + callback = callback || (() => {}) + const address = this.traceManager.getCurrentCalledAddressAt(step) + const storageViewer = new StorageViewer({ stepIndex: step, tx: this.tx, address: address }, this.storageResolver, this.traceManager) + const result = await stateDecoder.decodeState(stateVars, storageViewer) + callback(result) + return result + } catch (error) { + callback(error) + } + } + + storageViewAt (step, address) { + return new StorageViewer({ stepIndex: step, tx: this.tx, address: address }, this.storageResolver, this.traceManager) + } + + updateWeb3 (web3) { + this.web3 = web3 + this.setManagers() + } + + unLoad () { + this.traceManager.init() + this.codeManager.clear() + this.solidityProxy.reset() + this.event.trigger('traceUnloaded') + } + + async debug (tx) { + if (this.traceManager.isLoading) { + return + } + tx.to = tx.to || contractCreationToken('0') + this.tx = tx + + await this.traceManager.resolveTrace(tx) + this.event.trigger('newTraceLoaded', [this.traceManager.trace]) + if (this.breakpointManager && this.breakpointManager.hasBreakpoint()) { + this.breakpointManager.jumpNextBreakpoint(false) + } + this.storageResolver = new StorageResolver({ web3: this.traceManager.web3 }) + } } diff --git a/libs/remix-debug/src/cmdline/index.ts b/libs/remix-debug/src/cmdline/index.ts index b019e7ebd0..d7e5c48e17 100644 --- a/libs/remix-debug/src/cmdline/index.ts +++ b/libs/remix-debug/src/cmdline/index.ts @@ -3,201 +3,201 @@ import { Debugger } from '../debugger/debugger' import { EventEmitter } from 'events' export class CmdLine { - events - lineColumnPos - rawLocation - web3 - compilation - debugger - filename - txHash - solidityState - solidityLocals - - constructor () { - this.events = new EventEmitter() - this.lineColumnPos = null - this.rawLocation = null - } - - connect (providerType, url) { - if (providerType !== 'http') throw new Error('unsupported provider type') - this.web3 = new Web3(new Web3.providers.HttpProvider(url)) - } - - loadCompilationData (inputJson, outputJson) { - const data = {} - data['data'] = outputJson - data['source'] = { sources: inputJson.sources } - this.loadCompilationResult(data) - } - - loadCompilationResult (compilationResult) { - this.compilation = {} - this.compilation.compilationResult = compilationResult - } - - initDebugger (cb) { - this.debugger = new Debugger({ - web3: this.web3, - compilationResult: () => { return this.compilation.compilationResult } - }) - } - - getSource () { - const lineColumnPos = this.lineColumnPos - if (!lineColumnPos || !lineColumnPos.start) return [] - const content = this.compilation.compilationResult.source.sources[this.filename].content.split('\n') - const source = [] - let line - line = content[lineColumnPos.start.line - 2] - if (line !== undefined) { - source.push(' ' + (lineColumnPos.start.line - 1) + ': ' + line) - } - line = content[lineColumnPos.start.line - 1] - if (line !== undefined) { - source.push(' ' + lineColumnPos.start.line + ': ' + line) - } - - const currentLineNumber = lineColumnPos.start.line - const currentLine = content[currentLineNumber] - source.push('=> ' + (currentLineNumber + 1) + ': ' + currentLine) - - const startLine = lineColumnPos.start.line - for (let i = 1; i < 4; i++) { - const line = content[startLine + i] - source.push(' ' + (startLine + i + 1) + ': ' + line) - } - - return source - } - - getCurrentLine () { - const lineColumnPos = this.lineColumnPos - if (!lineColumnPos) return '' - const currentLineNumber = lineColumnPos.start.line - const content = this.compilation.compilationResult.source.sources[this.filename].content.split('\n') - return content[currentLineNumber] - } - - startDebug (txNumber, filename, cb) { - this.filename = filename - this.txHash = txNumber - this.debugger.debug(null, txNumber, null, () => { - this.debugger.event.register('newSourceLocation', (lineColumnPos, rawLocation) => { - if (!lineColumnPos) return - this.lineColumnPos = lineColumnPos - this.rawLocation = rawLocation - this.events.emit('source', [lineColumnPos, rawLocation]) - }) - - this.debugger.vmDebuggerLogic.event.register('solidityState', (data) => { - this.solidityState = data - this.events.emit('globals', data) - }) - - // TODO: this doesnt work too well, it should request the data instead... - this.debugger.vmDebuggerLogic.event.register('solidityLocals', (data) => { - if (JSON.stringify(data) === '{}') return - this.solidityLocals = data - this.events.emit('locals', data) - }) - - if (cb) { - // TODO: this should be an onReady event - setTimeout(cb, 1000) - } - }) - } - - getVars () { - return { - locals: this.solidityLocals, - contract: this.solidityState - } - } - - triggerSourceUpdate () { - this.events.emit('source', [this.lineColumnPos, this.rawLocation]) - } - - stepJumpNextBreakpoint () { - this.debugger.step_manager.jumpNextBreakpoint() - } - - stepJumpPreviousBreakpoint () { - this.debugger.step_manager.jumpPreviousBreakpoint() - } - - stepOverForward (solidityMode) { - this.debugger.step_manager.stepOverForward(solidityMode) - } - - stepOverBack (solidityMode) { - this.debugger.step_manager.stepOverBack(solidityMode) - } - - stepIntoForward (solidityMode) { - this.debugger.step_manager.stepIntoForward(solidityMode) - } - - stepIntoBack (solidityMode) { - this.debugger.step_manager.stepIntoBack(solidityMode) - } - - jumpTo (step) { - this.debugger.step_manager.jumpTo(step) - } - - getTraceLength () { - if (!this.debugger.step_manager) return 0 - return this.debugger.step_manager.traceLength - } - - getCodeFirstStep () { - if (!this.debugger.step_manager) return 0 - return this.debugger.step_manager.calculateFirstStep() - } - - getCodeTraceLength () { - if (!this.debugger.step_manager) return 0 - return this.debugger.step_manager.calculateCodeLength() - } - - nextStep () { - if (!this.debugger.step_manager) return 0 - return this.debugger.step_manager.nextStep() - } - - previousStep () { - if (!this.debugger.step_manager) return 0 - return this.debugger.step_manager.previousStep() - } - - currentStep () { - if (!this.debugger.step_manager) return 0 - return this.debugger.step_manager.currentStepIndex - } - - canGoNext () { - return this.currentStep() < this.getCodeTraceLength() - } - - canGoPrevious () { - return this.currentStep() > this.getCodeFirstStep() - } - - unload () { - return this.debugger.unload() - } - - displayLocals () { - console.dir('= displayLocals') - console.dir(this.solidityLocals) - } - - displayGlobals () { - console.dir('= displayGlobals') - console.dir(this.solidityState) - } + events + lineColumnPos + rawLocation + web3 + compilation + debugger + filename + txHash + solidityState + solidityLocals + + constructor () { + this.events = new EventEmitter() + this.lineColumnPos = null + this.rawLocation = null + } + + connect (providerType, url) { + if (providerType !== 'http') throw new Error('unsupported provider type') + this.web3 = new Web3(new Web3.providers.HttpProvider(url)) + } + + loadCompilationData (inputJson, outputJson) { + const data = {} + data['data'] = outputJson + data['source'] = { sources: inputJson.sources } + this.loadCompilationResult(data) + } + + loadCompilationResult (compilationResult) { + this.compilation = {} + this.compilation.compilationResult = compilationResult + } + + initDebugger (cb) { + this.debugger = new Debugger({ + web3: this.web3, + compilationResult: () => { return this.compilation.compilationResult } + }) + } + + getSource () { + const lineColumnPos = this.lineColumnPos + if (!lineColumnPos || !lineColumnPos.start) return [] + const content = this.compilation.compilationResult.source.sources[this.filename].content.split('\n') + const source = [] + let line + line = content[lineColumnPos.start.line - 2] + if (line !== undefined) { + source.push(' ' + (lineColumnPos.start.line - 1) + ': ' + line) + } + line = content[lineColumnPos.start.line - 1] + if (line !== undefined) { + source.push(' ' + lineColumnPos.start.line + ': ' + line) + } + + const currentLineNumber = lineColumnPos.start.line + const currentLine = content[currentLineNumber] + source.push('=> ' + (currentLineNumber + 1) + ': ' + currentLine) + + const startLine = lineColumnPos.start.line + for (let i = 1; i < 4; i++) { + const line = content[startLine + i] + source.push(' ' + (startLine + i + 1) + ': ' + line) + } + + return source + } + + getCurrentLine () { + const lineColumnPos = this.lineColumnPos + if (!lineColumnPos) return '' + const currentLineNumber = lineColumnPos.start.line + const content = this.compilation.compilationResult.source.sources[this.filename].content.split('\n') + return content[currentLineNumber] + } + + startDebug (txNumber, filename, cb) { + this.filename = filename + this.txHash = txNumber + this.debugger.debug(null, txNumber, null, () => { + this.debugger.event.register('newSourceLocation', (lineColumnPos, rawLocation) => { + if (!lineColumnPos) return + this.lineColumnPos = lineColumnPos + this.rawLocation = rawLocation + this.events.emit('source', [lineColumnPos, rawLocation]) + }) + + this.debugger.vmDebuggerLogic.event.register('solidityState', (data) => { + this.solidityState = data + this.events.emit('globals', data) + }) + + // TODO: this doesnt work too well, it should request the data instead... + this.debugger.vmDebuggerLogic.event.register('solidityLocals', (data) => { + if (JSON.stringify(data) === '{}') return + this.solidityLocals = data + this.events.emit('locals', data) + }) + + if (cb) { + // TODO: this should be an onReady event + setTimeout(cb, 1000) + } + }) + } + + getVars () { + return { + locals: this.solidityLocals, + contract: this.solidityState + } + } + + triggerSourceUpdate () { + this.events.emit('source', [this.lineColumnPos, this.rawLocation]) + } + + stepJumpNextBreakpoint () { + this.debugger.step_manager.jumpNextBreakpoint() + } + + stepJumpPreviousBreakpoint () { + this.debugger.step_manager.jumpPreviousBreakpoint() + } + + stepOverForward (solidityMode) { + this.debugger.step_manager.stepOverForward(solidityMode) + } + + stepOverBack (solidityMode) { + this.debugger.step_manager.stepOverBack(solidityMode) + } + + stepIntoForward (solidityMode) { + this.debugger.step_manager.stepIntoForward(solidityMode) + } + + stepIntoBack (solidityMode) { + this.debugger.step_manager.stepIntoBack(solidityMode) + } + + jumpTo (step) { + this.debugger.step_manager.jumpTo(step) + } + + getTraceLength () { + if (!this.debugger.step_manager) return 0 + return this.debugger.step_manager.traceLength + } + + getCodeFirstStep () { + if (!this.debugger.step_manager) return 0 + return this.debugger.step_manager.calculateFirstStep() + } + + getCodeTraceLength () { + if (!this.debugger.step_manager) return 0 + return this.debugger.step_manager.calculateCodeLength() + } + + nextStep () { + if (!this.debugger.step_manager) return 0 + return this.debugger.step_manager.nextStep() + } + + previousStep () { + if (!this.debugger.step_manager) return 0 + return this.debugger.step_manager.previousStep() + } + + currentStep () { + if (!this.debugger.step_manager) return 0 + return this.debugger.step_manager.currentStepIndex + } + + canGoNext () { + return this.currentStep() < this.getCodeTraceLength() + } + + canGoPrevious () { + return this.currentStep() > this.getCodeFirstStep() + } + + unload () { + return this.debugger.unload() + } + + displayLocals () { + console.dir('= displayLocals') + console.dir(this.solidityLocals) + } + + displayGlobals () { + console.dir('= displayGlobals') + console.dir(this.solidityState) + } } diff --git a/libs/remix-debug/src/code/breakpointManager.ts b/libs/remix-debug/src/code/breakpointManager.ts index 58c4691b36..3043e87e8c 100644 --- a/libs/remix-debug/src/code/breakpointManager.ts +++ b/libs/remix-debug/src/code/breakpointManager.ts @@ -9,132 +9,132 @@ import { isJumpDestInstruction } from '../trace/traceHelper' * Trigger events: breakpointHit, breakpointAdded, breakpointRemoved */ export class BreakpointManager { - event - traceManager - callTree - solidityProxy - breakpoints + event + traceManager + callTree + solidityProxy + breakpoints - /** + /** * constructor * * @param {Object} _debugger - type of EthDebugger * @return {Function} _locationToRowConverter - function implemented by editor which return a column/line position for a char source location */ - constructor ({ traceManager, callTree, solidityProxy }) { - this.event = new EventManager() - this.traceManager = traceManager - this.callTree = callTree - this.solidityProxy = solidityProxy - this.breakpoints = {} - } + constructor ({ traceManager, callTree, solidityProxy }) { + this.event = new EventManager() + this.traceManager = traceManager + this.callTree = callTree + this.solidityProxy = solidityProxy + this.breakpoints = {} + } - setManagers ({ traceManager, callTree, solidityProxy }) { - this.traceManager = traceManager - this.callTree = callTree - this.solidityProxy = solidityProxy - } + setManagers ({ traceManager, callTree, solidityProxy }) { + this.traceManager = traceManager + this.callTree = callTree + this.solidityProxy = solidityProxy + } - /** + /** * start looking for the next breakpoint * @param {Bool} defaultToLimit - if true jump to the end of the trace if no more breakpoint found * */ - async jumpNextBreakpoint (fromStep, defaultToLimit) { - if (!this.callTree.locationAndOpcodePerVMTraceIndex[fromStep]) { - return console.log('row converter not provided') - } - this.jump(fromStep || 0, 1, defaultToLimit, this.traceManager.trace) + async jumpNextBreakpoint (fromStep, defaultToLimit) { + if (!this.callTree.locationAndOpcodePerVMTraceIndex[fromStep]) { + return console.log('row converter not provided') } + this.jump(fromStep || 0, 1, defaultToLimit, this.traceManager.trace) + } - /** + /** * start looking for the previous breakpoint * @param {Bool} defaultToLimit - if true jump to the start of the trace if no more breakpoint found * */ - async jumpPreviousBreakpoint (fromStep, defaultToLimit) { - if (!this.callTree.locationAndOpcodePerVMTraceIndex[fromStep]) { - return console.log('row converter not provided') - } - this.jump(fromStep || 0, -1, defaultToLimit, this.traceManager.trace) + async jumpPreviousBreakpoint (fromStep, defaultToLimit) { + if (!this.callTree.locationAndOpcodePerVMTraceIndex[fromStep]) { + return console.log('row converter not provided') } + this.jump(fromStep || 0, -1, defaultToLimit, this.traceManager.trace) + } - depthChange (step, trace) { - return trace[step].depth !== trace[step - 1].depth - } + depthChange (step, trace) { + return trace[step].depth !== trace[step - 1].depth + } - hitLine (currentStep, sourceLocation, previousSourceLocation, trace) { - // isJumpDestInstruction -> returning from a internal function call - // depthChange -> returning from an external call - // sourceLocation.start <= previousSourceLocation.start && ... -> previous src is contained in the current one - if ((isJumpDestInstruction(trace[currentStep]) && previousSourceLocation.jump === 'o') || + hitLine (currentStep, sourceLocation, previousSourceLocation, trace) { + // isJumpDestInstruction -> returning from a internal function call + // depthChange -> returning from an external call + // sourceLocation.start <= previousSourceLocation.start && ... -> previous src is contained in the current one + if ((isJumpDestInstruction(trace[currentStep]) && previousSourceLocation.jump === 'o') || this.depthChange(currentStep, trace) || (sourceLocation.start <= previousSourceLocation.start && sourceLocation.start + sourceLocation.length >= previousSourceLocation.start + previousSourceLocation.length)) { - return false - } - this.event.trigger('breakpointStep', [currentStep]) - this.event.trigger('breakpointHit', [sourceLocation, currentStep]) - return true + return false } + this.event.trigger('breakpointStep', [currentStep]) + this.event.trigger('breakpointHit', [sourceLocation, currentStep]) + return true + } - /** + /** * start looking for the previous or next breakpoint * @param {Int} direction - 1 or -1 direction of the search * @param {Bool} defaultToLimit - if true jump to the limit (end if direction is 1, beginning if direction is -1) of the trace if no more breakpoint found * */ - async jump (fromStep, direction, defaultToLimit, trace) { - this.event.trigger('locatingBreakpoint', []) - let sourceLocation - let lineColumn - let contractAddress - let previousSourceLocation - let currentStep = fromStep + direction - let lineHadBreakpoint = false - let initialLine - while (currentStep > 0 && currentStep < trace.length) { - try { - previousSourceLocation = sourceLocation - const stepInfo = this.callTree.locationAndOpcodePerVMTraceIndex[currentStep] - sourceLocation = stepInfo.sourceLocation - lineColumn = stepInfo.lineColumnPos - contractAddress = stepInfo.contractAddress - } catch (e) { - console.log('cannot jump to breakpoint ' + e) - currentStep += direction - continue - } - if (!initialLine) initialLine = lineColumn + async jump (fromStep, direction, defaultToLimit, trace) { + this.event.trigger('locatingBreakpoint', []) + let sourceLocation + let lineColumn + let contractAddress + let previousSourceLocation + let currentStep = fromStep + direction + let lineHadBreakpoint = false + let initialLine + while (currentStep > 0 && currentStep < trace.length) { + try { + previousSourceLocation = sourceLocation + const stepInfo = this.callTree.locationAndOpcodePerVMTraceIndex[currentStep] + sourceLocation = stepInfo.sourceLocation + lineColumn = stepInfo.lineColumnPos + contractAddress = stepInfo.contractAddress + } catch (e) { + console.log('cannot jump to breakpoint ' + e) + currentStep += direction + continue + } + if (!initialLine) initialLine = lineColumn - if (initialLine.start.line !== lineColumn.start.line) { - if (direction === -1 && lineHadBreakpoint) { // TODO : improve this when we will build the correct structure before hand - lineHadBreakpoint = false - if (this.hitLine(currentStep + 1, previousSourceLocation, sourceLocation, trace)) { - return - } - } - if (await this.hasBreakpointAtLine(sourceLocation.file, lineColumn.start.line, contractAddress)) { - lineHadBreakpoint = true - if (this.hitLine(currentStep, sourceLocation, previousSourceLocation, trace)) { - return - } - } - } - currentStep += direction - } - this.event.trigger('noBreakpointHit', []) - if (!defaultToLimit) { + if (initialLine.start.line !== lineColumn.start.line) { + if (direction === -1 && lineHadBreakpoint) { // TODO : improve this when we will build the correct structure before hand + lineHadBreakpoint = false + if (this.hitLine(currentStep + 1, previousSourceLocation, sourceLocation, trace)) { return + } } - if (direction === -1) { - this.event.trigger('breakpointStep', [0]) - } else if (direction === 1) { - this.event.trigger('breakpointStep', [trace.length - 1]) + if (await this.hasBreakpointAtLine(sourceLocation.file, lineColumn.start.line, contractAddress)) { + lineHadBreakpoint = true + if (this.hitLine(currentStep, sourceLocation, previousSourceLocation, trace)) { + return + } } + } + currentStep += direction + } + this.event.trigger('noBreakpointHit', []) + if (!defaultToLimit) { + return + } + if (direction === -1) { + this.event.trigger('breakpointStep', [0]) + } else if (direction === 1) { + this.event.trigger('breakpointStep', [trace.length - 1]) } + } - /** + /** * check the given pair fileIndex/line against registered breakpoints * * @param {Int} fileIndex - index of the file content (from the compilation result) @@ -142,67 +142,67 @@ export class BreakpointManager { * @param {String} contractAddress - address of the contract being executed * @return {Bool} return true if the given @arg fileIndex @arg line refers to a breakpoint */ - async hasBreakpointAtLine (fileIndex, line, contractAddress) { - const compResult = await this.solidityProxy.compilationResult(contractAddress) - const filename = Object.keys(compResult.data.contracts)[fileIndex] - if (!(filename && this.breakpoints[filename])) { - return false - } - const sources = this.breakpoints[filename] - for (const k in sources) { - const source = sources[k] - if (line === source.row) { - return true - } - } + async hasBreakpointAtLine (fileIndex, line, contractAddress) { + const compResult = await this.solidityProxy.compilationResult(contractAddress) + const filename = Object.keys(compResult.data.contracts)[fileIndex] + if (!(filename && this.breakpoints[filename])) { + return false + } + const sources = this.breakpoints[filename] + for (const k in sources) { + const source = sources[k] + if (line === source.row) { + return true + } } + } - /** + /** * return true if current manager has breakpoint * * @return {Bool} true if breapoint registered */ - hasBreakpoint () { - for (const k in this.breakpoints) { - if (this.breakpoints[k].length) { - return true - } - } - return false + hasBreakpoint () { + for (const k in this.breakpoints) { + if (this.breakpoints[k].length) { + return true + } } + return false + } - /** + /** * add a new breakpoint to the manager * * @param {Object} sourceLocation - position of the breakpoint { file: '', row: '', row: ' { - return new Promise((resolve, reject) => { - this.traceManager.web3.eth.getCode(address, (error, code) => { - if (error) { - return reject(error) - } - return resolve(code) - }) - }) - }, - fork: this.traceManager.getCurrentFork() + constructor (_traceManager) { + this.event = new EventManager() + this.isLoading = false + this.traceManager = _traceManager + this.codeResolver = new CodeResolver({ + getCode: async (address) => { + return new Promise((resolve, reject) => { + this.traceManager.web3.eth.getCode(address, (error, code) => { + if (error) { + return reject(error) + } + return resolve(code) + }) }) - } + }, + fork: this.traceManager.getCurrentFork() + }) + } - /** + /** * clear the cache * */ - clear () { - this.codeResolver.clear() - } + clear () { + this.codeResolver.clear() + } - /** + /** * resolve the code of the given @arg stepIndex and trigger appropriate event * * @param {String} stepIndex - vm trace step * @param {Object} tx - transaction (given by web3) */ - resolveStep (stepIndex, tx) { - if (stepIndex < 0) return - this.event.trigger('resolvingStep') - if (stepIndex === 0) { - return this.retrieveCodeAndTrigger(this, tx.to, stepIndex, tx) - } - try { - const address = this.traceManager.getCurrentCalledAddressAt(stepIndex) - this.retrieveCodeAndTrigger(this, address, stepIndex, tx) - } catch (error) { - return console.log(error) - } + resolveStep (stepIndex, tx) { + if (stepIndex < 0) return + this.event.trigger('resolvingStep') + if (stepIndex === 0) { + return this.retrieveCodeAndTrigger(this, tx.to, stepIndex, tx) + } + try { + const address = this.traceManager.getCurrentCalledAddressAt(stepIndex) + this.retrieveCodeAndTrigger(this, address, stepIndex, tx) + } catch (error) { + return console.log(error) } + } - /** + /** * Retrieve the code located at the given @arg address * * @param {String} address - address of the contract to get the code from * @param {Function} cb - callback function, return the bytecode */ - async getCode (address) { - if (!isContractCreation(address)) { - const code = await this.codeResolver.resolveCode(address) - return code - } - let codes = this.codeResolver.getExecutingCodeFromCache(address) - if (codes) { - return codes - } - const hexCode = this.traceManager.getContractCreationCode(address) - codes = this.codeResolver.cacheExecutingCode(address, hexCode) - return codes + async getCode (address) { + if (!isContractCreation(address)) { + const code = await this.codeResolver.resolveCode(address) + return code + } + let codes = this.codeResolver.getExecutingCodeFromCache(address) + if (codes) { + return codes } + const hexCode = this.traceManager.getContractCreationCode(address) + codes = this.codeResolver.cacheExecutingCode(address, hexCode) + return codes + } - /** + /** * Retrieve the called function for the current vm step for the given @arg address * * @param {String} stepIndex - vm trace step @@ -94,36 +94,36 @@ export class CodeManager { * @param {Object} ast - ast given by the compilation result * @return {Object} return the ast node of the function */ - getFunctionFromStep (stepIndex, sourceMap, ast) { - try { - const address = this.traceManager.getCurrentCalledAddressAt(stepIndex) - const pc = this.traceManager.getCurrentPC(stepIndex) - return this.getFunctionFromPC(address, pc, sourceMap, ast) - } catch (error) { - console.log(error) - return { error: 'Cannot retrieve current address or PC for ' + stepIndex } - } + getFunctionFromStep (stepIndex, sourceMap, ast) { + try { + const address = this.traceManager.getCurrentCalledAddressAt(stepIndex) + const pc = this.traceManager.getCurrentPC(stepIndex) + return this.getFunctionFromPC(address, pc, sourceMap, ast) + } catch (error) { + console.log(error) + return { error: 'Cannot retrieve current address or PC for ' + stepIndex } } + } - /** + /** * Retrieve the instruction index of the given @arg step * * @param {String} address - address of the current context * @param {String} step - vm trace step * @param {Function} callback - instruction index */ - getInstructionIndex (address, step) { - try { - const pc = this.traceManager.getCurrentPC(step) - const itemIndex = this.codeResolver.getInstructionIndex(address, pc) - return itemIndex - } catch (error) { - console.log(error) - throw new Error('Cannot retrieve current PC for ' + step) - } + getInstructionIndex (address, step) { + try { + const pc = this.traceManager.getCurrentPC(step) + const itemIndex = this.codeResolver.getInstructionIndex(address, pc) + return itemIndex + } catch (error) { + console.log(error) + throw new Error('Cannot retrieve current PC for ' + step) } + } - /** + /** * Retrieve the called function for the given @arg pc and @arg address * * @param {String} address - address of the current context (used to resolve instruction index) @@ -132,57 +132,57 @@ export class CodeManager { * @param {Object} ast - ast given by the compilation result * @return {Object} return the ast node of the function */ - getFunctionFromPC (address, pc, sourceMap, ast) { - const instIndex = this.codeResolver.getInstructionIndex(address, pc) - return findNodeAtInstructionIndex('FunctionDefinition', instIndex, sourceMap, ast) - } + getFunctionFromPC (address, pc, sourceMap, ast) { + const instIndex = this.codeResolver.getInstructionIndex(address, pc) + return findNodeAtInstructionIndex('FunctionDefinition', instIndex, sourceMap, ast) + } - private retrieveCodeAndTrigger (codeMananger, address, stepIndex, tx) { - codeMananger.getCode(address).then((result) => { - this.retrieveIndexAndTrigger(codeMananger, address, stepIndex, result.instructions) - }).catch((error) => { - return console.log(error) - }) - } + private retrieveCodeAndTrigger (codeMananger, address, stepIndex, tx) { + codeMananger.getCode(address).then((result) => { + this.retrieveIndexAndTrigger(codeMananger, address, stepIndex, result.instructions) + }).catch((error) => { + return console.log(error) + }) + } - private async retrieveIndexAndTrigger (codeMananger, address, step, code) { - let result - const next = [] - const returnInstructionIndexes = [] - const outOfGasInstructionIndexes = [] - - try { - result = codeMananger.getInstructionIndex(address, step) - for (let i = 1; i < 6; i++) { - if (this.traceManager.inRange(step + i)) { - next.push(codeMananger.getInstructionIndex(address, step + i)) - } - } + private async retrieveIndexAndTrigger (codeMananger, address, step, code) { + let result + const next = [] + const returnInstructionIndexes = [] + const outOfGasInstructionIndexes = [] - let values = this.traceManager.getAllStopIndexes() - if (values) { - for (const value of values) { - if (value.address === address) { - returnInstructionIndexes.push({ instructionIndex: this.getInstructionIndex(address, value.index), address }) - } - } - } + try { + result = codeMananger.getInstructionIndex(address, step) + for (let i = 1; i < 6; i++) { + if (this.traceManager.inRange(step + i)) { + next.push(codeMananger.getInstructionIndex(address, step + i)) + } + } - values = this.traceManager.getAllOutofGasIndexes() - if (values) { - for (const value of values) { - if (value.address === address) { - outOfGasInstructionIndexes.push({ instructionIndex: this.getInstructionIndex(address, value.index), address }) - } - } - } - } catch (error) { - return console.log(error) + let values = this.traceManager.getAllStopIndexes() + if (values) { + for (const value of values) { + if (value.address === address) { + returnInstructionIndexes.push({ instructionIndex: this.getInstructionIndex(address, value.index), address }) + } } - try { - codeMananger.event.trigger('changed', [code, address, result, next, returnInstructionIndexes, outOfGasInstructionIndexes]) - } catch (e) { - console.log('dispatching event failed', e) + } + + values = this.traceManager.getAllOutofGasIndexes() + if (values) { + for (const value of values) { + if (value.address === address) { + outOfGasInstructionIndexes.push({ instructionIndex: this.getInstructionIndex(address, value.index), address }) + } } + } + } catch (error) { + return console.log(error) + } + try { + codeMananger.event.trigger('changed', [code, address, result, next, returnInstructionIndexes, outOfGasInstructionIndexes]) + } catch (e) { + console.log('dispatching event failed', e) } + } } diff --git a/libs/remix-debug/src/code/codeResolver.ts b/libs/remix-debug/src/code/codeResolver.ts index 94be135a5c..8fc49fa4ae 100644 --- a/libs/remix-debug/src/code/codeResolver.ts +++ b/libs/remix-debug/src/code/codeResolver.ts @@ -2,61 +2,61 @@ import { nameOpCodes } from './codeUtils' export class CodeResolver { - getCode - bytecodeByAddress - instructionsByAddress - instructionsIndexByBytesOffset - fork - - constructor ({ getCode, fork }) { - this.getCode = getCode - this.bytecodeByAddress = {} // bytes code by contract addesses - this.instructionsByAddress = {} // assembly items instructions list by contract addesses - this.instructionsIndexByBytesOffset = {} // mapping between bytes offset and instructions index. - this.fork = fork + getCode + bytecodeByAddress + instructionsByAddress + instructionsIndexByBytesOffset + fork + + constructor ({ getCode, fork }) { + this.getCode = getCode + this.bytecodeByAddress = {} // bytes code by contract addesses + this.instructionsByAddress = {} // assembly items instructions list by contract addesses + this.instructionsIndexByBytesOffset = {} // mapping between bytes offset and instructions index. + this.fork = fork + } + + clear () { + this.bytecodeByAddress = {} + this.instructionsByAddress = {} + this.instructionsIndexByBytesOffset = {} + } + + async resolveCode (address) { + const cache = this.getExecutingCodeFromCache(address) + if (cache) { + return cache } - clear () { - this.bytecodeByAddress = {} - this.instructionsByAddress = {} - this.instructionsIndexByBytesOffset = {} + const code = await this.getCode(address) + return this.cacheExecutingCode(address, code) + } + + cacheExecutingCode (address, hexCode) { + const codes = this.formatCode(hexCode) + this.bytecodeByAddress[address] = hexCode + this.instructionsByAddress[address] = codes.code + this.instructionsIndexByBytesOffset[address] = codes.instructionsIndexByBytesOffset + return this.getExecutingCodeFromCache(address) + } + + formatCode (hexCode) { + const [code, instructionsIndexByBytesOffset] = nameOpCodes(Buffer.from(hexCode.substring(2), 'hex'), this.fork) + return { code, instructionsIndexByBytesOffset } + } + + getExecutingCodeFromCache (address) { + if (!this.instructionsByAddress[address]) { + return null } - - async resolveCode (address) { - const cache = this.getExecutingCodeFromCache(address) - if (cache) { - return cache - } - - const code = await this.getCode(address) - return this.cacheExecutingCode(address, code) + return { + instructions: this.instructionsByAddress[address], + instructionsIndexByBytesOffset: this.instructionsIndexByBytesOffset[address], + bytecode: this.bytecodeByAddress[address] } + } - cacheExecutingCode (address, hexCode) { - const codes = this.formatCode(hexCode) - this.bytecodeByAddress[address] = hexCode - this.instructionsByAddress[address] = codes.code - this.instructionsIndexByBytesOffset[address] = codes.instructionsIndexByBytesOffset - return this.getExecutingCodeFromCache(address) - } - - formatCode (hexCode) { - const [code, instructionsIndexByBytesOffset] = nameOpCodes(Buffer.from(hexCode.substring(2), 'hex'), this.fork) - return { code, instructionsIndexByBytesOffset } - } - - getExecutingCodeFromCache (address) { - if (!this.instructionsByAddress[address]) { - return null - } - return { - instructions: this.instructionsByAddress[address], - instructionsIndexByBytesOffset: this.instructionsIndexByBytesOffset[address], - bytecode: this.bytecodeByAddress[address] - } - } - - getInstructionIndex (address, pc) { - return this.getExecutingCodeFromCache(address).instructionsIndexByBytesOffset[pc] - } + getInstructionIndex (address, pc) { + return this.getExecutingCodeFromCache(address).instructionsIndexByBytesOffset[pc] + } } diff --git a/libs/remix-debug/src/code/codeUtils.ts b/libs/remix-debug/src/code/codeUtils.ts index f9784bb135..9ab3f28aaf 100644 --- a/libs/remix-debug/src/code/codeUtils.ts +++ b/libs/remix-debug/src/code/codeUtils.ts @@ -4,35 +4,35 @@ import { getOpcodesForHF, OpcodeList } from '@ethereumjs/evm/dist/opcodes/codes' import getOpcodes from './opcodes' export function nameOpCodes (raw, hardfork) { - const common = new Common({ chain: 'mainnet', hardfork }) - const opcodes = getOpcodesForHF(common).opcodes + const common = new Common({ chain: 'mainnet', hardfork }) + const opcodes = getOpcodesForHF(common).opcodes - let pushData = '' - const codeMap = {} - const code = [] + let pushData = '' + const codeMap = {} + const code = [] - for (let i = 0; i < raw.length; i++) { - const pc = i - let curOpCode - try { - curOpCode = opcodes.get(raw[pc]).fullName - } catch (e) { - curOpCode = 'INVALID' - } - codeMap[i] = code.length - // no destinations into the middle of PUSH - if (curOpCode.slice(0, 4) === 'PUSH') { - const jumpNum = raw[pc] - 0x5f - pushData = raw.slice(pc + 1, pc + jumpNum + 1) - i += jumpNum - } + for (let i = 0; i < raw.length; i++) { + const pc = i + let curOpCode + try { + curOpCode = opcodes.get(raw[pc]).fullName + } catch (e) { + curOpCode = 'INVALID' + } + codeMap[i] = code.length + // no destinations into the middle of PUSH + if (curOpCode.slice(0, 4) === 'PUSH') { + const jumpNum = raw[pc] - 0x5f + pushData = raw.slice(pc + 1, pc + jumpNum + 1) + i += jumpNum + } - const data = (pushData as any).toString('hex') !== '' ? ' ' + (pushData as any).toString('hex') : '' + const data = (pushData as any).toString('hex') !== '' ? ' ' + (pushData as any).toString('hex') : '' - code.push(pad(pc, roundLog(raw.length, 10)) + ' ' + curOpCode + data) - pushData = '' - } - return [code, codeMap] + code.push(pad(pc, roundLog(raw.length, 10)) + ' ' + curOpCode + data) + pushData = '' + } + return [code, codeMap] } type Opcode = { @@ -46,47 +46,47 @@ type Opcode = { * information about the opcode. */ export function parseCode (raw) { - const common = new Common({ chain: 'mainnet', hardfork: 'merge' }) - const opcodes = getOpcodesForHF(common).opcodes + const common = new Common({ chain: 'mainnet', hardfork: 'merge' }) + const opcodes = getOpcodesForHF(common).opcodes - const code = [] - for (let i = 0; i < raw.length; i++) { - const opcode: Opcode = { name: 'INVALID' } - try { - const code = opcodes.get(raw[i]) - const opcodeDetails = getOpcodes(raw[i], false) - opcode.in = opcodeDetails.in - opcode.out = opcodeDetails.out - opcode.name = code.fullName - } catch (e) { - opcode.name = 'INVALID' - } - if (opcode.name.slice(0, 4) === 'PUSH') { - const length = raw[i] - 0x5f - opcode.pushData = raw.slice(i + 1, i + length + 1) - // in case pushdata extends beyond code - if (i + 1 + length > raw.length) { - for (let j = opcode['pushData'].length; j < length; j++) { - opcode['pushData'].push(0) - } - } - i += length + const code = [] + for (let i = 0; i < raw.length; i++) { + const opcode: Opcode = { name: 'INVALID' } + try { + const code = opcodes.get(raw[i]) + const opcodeDetails = getOpcodes(raw[i], false) + opcode.in = opcodeDetails.in + opcode.out = opcodeDetails.out + opcode.name = code.fullName + } catch (e) { + opcode.name = 'INVALID' + } + if (opcode.name.slice(0, 4) === 'PUSH') { + const length = raw[i] - 0x5f + opcode.pushData = raw.slice(i + 1, i + length + 1) + // in case pushdata extends beyond code + if (i + 1 + length > raw.length) { + for (let j = opcode['pushData'].length; j < length; j++) { + opcode['pushData'].push(0) } - code.push(opcode) + } + i += length } - return code + code.push(opcode) + } + return code } export function pad (num, size) { - let s = num + '' - while (s.length < size) s = '0' + s - return s + let s = num + '' + while (s.length < size) s = '0' + s + return s } export function log (num, base) { - return Math.log(num) / Math.log(base) + return Math.log(num) / Math.log(base) } export function roundLog (num, base) { - return Math.ceil(log(num, base)) + return Math.ceil(log(num, base)) } diff --git a/libs/remix-debug/src/code/disassembler.ts b/libs/remix-debug/src/code/disassembler.ts index b828fe7e5d..d26bd1f9f4 100644 --- a/libs/remix-debug/src/code/disassembler.ts +++ b/libs/remix-debug/src/code/disassembler.ts @@ -5,51 +5,51 @@ import { util } from '@remix-project/remix-lib' import { bufferToHex } from '@ethereumjs/util' function createExpressions (instructions) { - const expressions = [] - let labels = 0 - for (let i = 0; i < instructions.length; i++) { - const expr = instructions[i] - expr.functional = false - if (expr.name === 'JUMPDEST') { - expr.label = 'label' + (++labels) - // eslint-disable-next-line no-empty - } else if (expr.name.slice(0, 3) === 'DUP') { - // eslint-disable-next-line no-empty - } else if (expr.name.slice(0, 4) === 'SWAP') { - } else if (expr.out <= 1 && expr.in <= expressions.length) { - let error = false - for (let j = 0; j < expr.in && !error; j++) { - const arg = expressions[expressions.length - j - 1] - if (!arg.functional || arg.out !== 1) { - error = true - break - } - } - if (!error) { - expr.args = expressions.splice(expressions.length - expr.in) - expr.functional = true - } + const expressions = [] + let labels = 0 + for (let i = 0; i < instructions.length; i++) { + const expr = instructions[i] + expr.functional = false + if (expr.name === 'JUMPDEST') { + expr.label = 'label' + (++labels) + // eslint-disable-next-line no-empty + } else if (expr.name.slice(0, 3) === 'DUP') { + // eslint-disable-next-line no-empty + } else if (expr.name.slice(0, 4) === 'SWAP') { + } else if (expr.out <= 1 && expr.in <= expressions.length) { + let error = false + for (let j = 0; j < expr.in && !error; j++) { + const arg = expressions[expressions.length - j - 1] + if (!arg.functional || arg.out !== 1) { + error = true + break } - expressions.push(expr) + } + if (!error) { + expr.args = expressions.splice(expressions.length - expr.in) + expr.functional = true + } } - return expressions + expressions.push(expr) + } + return expressions } function toString (expr) { - if (expr.name.slice(0, 4) === 'PUSH') { - return bufferToHex(expr.pushData) - } else if (expr.name === 'JUMPDEST') { - return expr.label + ':' - } else if (expr.args) { - return expr.name.toLowerCase() + '(' + expr.args.reverse().map(toString).join(', ') + ')' - } - return expr.name.toLowerCase() + if (expr.name.slice(0, 4) === 'PUSH') { + return bufferToHex(expr.pushData) + } else if (expr.name === 'JUMPDEST') { + return expr.label + ':' + } else if (expr.args) { + return expr.name.toLowerCase() + '(' + expr.args.reverse().map(toString).join(', ') + ')' + } + return expr.name.toLowerCase() } /** * Disassembler that turns bytecode (as a hex string) into Solidity inline assembly. */ export function disassemble (input) { - const code = parseCode(util.hexToIntArray(input)) - return createExpressions(code).map(toString).join('\n') + const code = parseCode(util.hexToIntArray(input)) + return createExpressions(code).map(toString).join('\n') } diff --git a/libs/remix-debug/src/code/opcodes.ts b/libs/remix-debug/src/code/opcodes.ts index c5c1bc6557..5f641acfcd 100644 --- a/libs/remix-debug/src/code/opcodes.ts +++ b/libs/remix-debug/src/code/opcodes.ts @@ -1,193 +1,193 @@ 'use strict' export default function (op, full) { - const codes = { - // 0x0 range - arithmetic ops - // name, baseCost, off stack, on stack, dynamic, async - // @todo can be improved on basis of this: https://github.com/ethereumjs/ethereumjs-vm/blob/master/lib/evm/opcodes.ts - - 0x00: ['STOP', 0, 0, 0, false], - 0x01: ['ADD', 3, 2, 1, false], - 0x02: ['MUL', 5, 2, 1, false], - 0x03: ['SUB', 3, 2, 1, false], - 0x04: ['DIV', 5, 2, 1, false], - 0x05: ['SDIV', 5, 2, 1, false], - 0x06: ['MOD', 5, 2, 1, false], - 0x07: ['SMOD', 5, 2, 1, false], - 0x08: ['ADDMOD', 8, 3, 1, false], - 0x09: ['MULMOD', 8, 3, 1, false], - 0x0a: ['EXP', 10, 2, 1, false], - 0x0b: ['SIGNEXTEND', 5, 2, 1, false], - - // 0x10 range - bit ops - 0x10: ['LT', 3, 2, 1, false], - 0x11: ['GT', 3, 2, 1, false], - 0x12: ['SLT', 3, 2, 1, false], - 0x13: ['SGT', 3, 2, 1, false], - 0x14: ['EQ', 3, 2, 1, false], - 0x15: ['ISZERO', 3, 1, 1, false], - 0x16: ['AND', 3, 2, 1, false], - 0x17: ['OR', 3, 2, 1, false], - 0x18: ['XOR', 3, 2, 1, false], - 0x19: ['NOT', 3, 1, 1, false], - 0x1a: ['BYTE', 3, 2, 1, false], - 0x1b: ['SHL', 3, 2, 1, false], - 0x1c: ['SHR', 3, 2, 1, false], - 0x1d: ['SAR', 3, 2, 1, false], - - // 0x20 range - crypto - 0x20: ['SHA3', 30, 2, 1, false], - - // 0x30 range - closure state - 0x30: ['ADDRESS', 2, 0, 1, true], - 0x31: ['BALANCE', 700, 1, 1, true, true], - 0x32: ['ORIGIN', 2, 0, 1, true], - 0x33: ['CALLER', 2, 0, 1, true], - 0x34: ['CALLVALUE', 2, 0, 1, true], - 0x35: ['CALLDATALOAD', 3, 1, 1, true], - 0x36: ['CALLDATASIZE', 2, 0, 1, true], - 0x37: ['CALLDATACOPY', 3, 3, 0, true], - 0x38: ['CODESIZE', 2, 0, 1, false], - 0x39: ['CODECOPY', 3, 3, 0, false], - 0x3a: ['GASPRICE', 2, 0, 1, false], - 0x3b: ['EXTCODESIZE', 700, 1, 1, true, true], - 0x3c: ['EXTCODECOPY', 700, 4, 0, true, true], - 0x3d: ['RETURNDATASIZE', 2, 0, 1, true], - 0x3e: ['RETURNDATACOPY', 3, 3, 0, true], - 0x3f: ['EXTCODEHASH', 400, 3, 0, true], - - // '0x40' range - block operations - 0x40: ['BLOCKHASH', 20, 1, 1, true, true], - 0x41: ['COINBASE', 2, 0, 1, true], - 0x42: ['TIMESTAMP', 2, 0, 1, true], - 0x43: ['NUMBER', 2, 0, 1, true], - 0x44: ['DIFFICULTY', 2, 0, 1, true], - 0x45: ['GASLIMIT', 2, 0, 1, true], - 0x46: ['CHAINID', 2, 0, 1, false], - 0x47: ['SELFBALANCE', 5, 0, 1, false], - - // 0x50 range - 'storage' and execution - 0x50: ['POP', 2, 1, 0, false], - 0x51: ['MLOAD', 3, 1, 1, false], - 0x52: ['MSTORE', 3, 2, 0, false], - 0x53: ['MSTORE8', 3, 2, 0, false], - 0x54: ['SLOAD', 800, 1, 1, true, true], - 0x55: ['SSTORE', 0, 2, 0, true, true], - 0x56: ['JUMP', 8, 1, 0, false], - 0x57: ['JUMPI', 10, 2, 0, false], - 0x58: ['PC', 2, 0, 1, false], - 0x59: ['MSIZE', 2, 0, 1, false], - 0x5a: ['GAS', 2, 0, 1, false], - 0x5b: ['JUMPDEST', 1, 0, 0, false], - - // 0x60, range - 0x60: ['PUSH1', 3, 0, 1, false], - 0x61: ['PUSH2', 3, 0, 1, false], - 0x62: ['PUSH3', 3, 0, 1, false], - 0x63: ['PUSH4', 3, 0, 1, false], - 0x64: ['PUSH5', 3, 0, 1, false], - 0x65: ['PUSH6', 3, 0, 1, false], - 0x66: ['PUSH7', 3, 0, 1, false], - 0x67: ['PUSH8', 3, 0, 1, false], - 0x68: ['PUSH9', 3, 0, 1, false], - 0x69: ['PUSH10', 3, 0, 1, false], - 0x6a: ['PUSH11', 3, 0, 1, false], - 0x6b: ['PUSH12', 3, 0, 1, false], - 0x6c: ['PUSH13', 3, 0, 1, false], - 0x6d: ['PUSH14', 3, 0, 1, false], - 0x6e: ['PUSH15', 3, 0, 1, false], - 0x6f: ['PUSH16', 3, 0, 1, false], - 0x70: ['PUSH17', 3, 0, 1, false], - 0x71: ['PUSH18', 3, 0, 1, false], - 0x72: ['PUSH19', 3, 0, 1, false], - 0x73: ['PUSH20', 3, 0, 1, false], - 0x74: ['PUSH21', 3, 0, 1, false], - 0x75: ['PUSH22', 3, 0, 1, false], - 0x76: ['PUSH23', 3, 0, 1, false], - 0x77: ['PUSH24', 3, 0, 1, false], - 0x78: ['PUSH25', 3, 0, 1, false], - 0x79: ['PUSH26', 3, 0, 1, false], - 0x7a: ['PUSH27', 3, 0, 1, false], - 0x7b: ['PUSH28', 3, 0, 1, false], - 0x7c: ['PUSH29', 3, 0, 1, false], - 0x7d: ['PUSH30', 3, 0, 1, false], - 0x7e: ['PUSH31', 3, 0, 1, false], - 0x7f: ['PUSH32', 3, 0, 1, false], - - 0x80: ['DUP1', 3, 0, 1, false], - 0x81: ['DUP2', 3, 0, 1, false], - 0x82: ['DUP3', 3, 0, 1, false], - 0x83: ['DUP4', 3, 0, 1, false], - 0x84: ['DUP5', 3, 0, 1, false], - 0x85: ['DUP6', 3, 0, 1, false], - 0x86: ['DUP7', 3, 0, 1, false], - 0x87: ['DUP8', 3, 0, 1, false], - 0x88: ['DUP9', 3, 0, 1, false], - 0x89: ['DUP10', 3, 0, 1, false], - 0x8a: ['DUP11', 3, 0, 1, false], - 0x8b: ['DUP12', 3, 0, 1, false], - 0x8c: ['DUP13', 3, 0, 1, false], - 0x8d: ['DUP14', 3, 0, 1, false], - 0x8e: ['DUP15', 3, 0, 1, false], - 0x8f: ['DUP16', 3, 0, 1, false], - - 0x90: ['SWAP1', 3, 0, 0, false], - 0x91: ['SWAP2', 3, 0, 0, false], - 0x92: ['SWAP3', 3, 0, 0, false], - 0x93: ['SWAP4', 3, 0, 0, false], - 0x94: ['SWAP5', 3, 0, 0, false], - 0x95: ['SWAP6', 3, 0, 0, false], - 0x96: ['SWAP7', 3, 0, 0, false], - 0x97: ['SWAP8', 3, 0, 0, false], - 0x98: ['SWAP9', 3, 0, 0, false], - 0x99: ['SWAP10', 3, 0, 0, false], - 0x9a: ['SWAP11', 3, 0, 0, false], - 0x9b: ['SWAP12', 3, 0, 0, false], - 0x9c: ['SWAP13', 3, 0, 0, false], - 0x9d: ['SWAP14', 3, 0, 0, false], - 0x9e: ['SWAP15', 3, 0, 0, false], - 0x9f: ['SWAP16', 3, 0, 0, false], - - 0xa0: ['LOG0', 375, 2, 0, false], - 0xa1: ['LOG1', 375, 3, 0, false], - 0xa2: ['LOG2', 375, 4, 0, false], - 0xa3: ['LOG3', 375, 5, 0, false], - 0xa4: ['LOG4', 375, 6, 0, false], - - // '0xf0' range - closures - 0xf0: ['CREATE', 32000, 3, 1, true, true], - 0xf1: ['CALL', 700, 7, 1, true, true], - 0xf2: ['CALLCODE', 700, 7, 1, true, true], - 0xf3: ['RETURN', 0, 2, 0, false], - 0xf4: ['DELEGATECALL', 700, 6, 1, true, true], - 0xf5: ['CREATE2', 32000, 4, 1, true, true], - 0xfa: ['STATICCALL', 700, 6, 1, true, true], - 0xfd: ['REVERT', 0, 2, 0, false], - - // '0x70', range - other - 0xfe: ['INVALID', 0, 0, 0, false], - 0xff: ['SELFDESTRUCT', 5000, 1, 0, false, true] + const codes = { + // 0x0 range - arithmetic ops + // name, baseCost, off stack, on stack, dynamic, async + // @todo can be improved on basis of this: https://github.com/ethereumjs/ethereumjs-vm/blob/master/lib/evm/opcodes.ts + + 0x00: ['STOP', 0, 0, 0, false], + 0x01: ['ADD', 3, 2, 1, false], + 0x02: ['MUL', 5, 2, 1, false], + 0x03: ['SUB', 3, 2, 1, false], + 0x04: ['DIV', 5, 2, 1, false], + 0x05: ['SDIV', 5, 2, 1, false], + 0x06: ['MOD', 5, 2, 1, false], + 0x07: ['SMOD', 5, 2, 1, false], + 0x08: ['ADDMOD', 8, 3, 1, false], + 0x09: ['MULMOD', 8, 3, 1, false], + 0x0a: ['EXP', 10, 2, 1, false], + 0x0b: ['SIGNEXTEND', 5, 2, 1, false], + + // 0x10 range - bit ops + 0x10: ['LT', 3, 2, 1, false], + 0x11: ['GT', 3, 2, 1, false], + 0x12: ['SLT', 3, 2, 1, false], + 0x13: ['SGT', 3, 2, 1, false], + 0x14: ['EQ', 3, 2, 1, false], + 0x15: ['ISZERO', 3, 1, 1, false], + 0x16: ['AND', 3, 2, 1, false], + 0x17: ['OR', 3, 2, 1, false], + 0x18: ['XOR', 3, 2, 1, false], + 0x19: ['NOT', 3, 1, 1, false], + 0x1a: ['BYTE', 3, 2, 1, false], + 0x1b: ['SHL', 3, 2, 1, false], + 0x1c: ['SHR', 3, 2, 1, false], + 0x1d: ['SAR', 3, 2, 1, false], + + // 0x20 range - crypto + 0x20: ['SHA3', 30, 2, 1, false], + + // 0x30 range - closure state + 0x30: ['ADDRESS', 2, 0, 1, true], + 0x31: ['BALANCE', 700, 1, 1, true, true], + 0x32: ['ORIGIN', 2, 0, 1, true], + 0x33: ['CALLER', 2, 0, 1, true], + 0x34: ['CALLVALUE', 2, 0, 1, true], + 0x35: ['CALLDATALOAD', 3, 1, 1, true], + 0x36: ['CALLDATASIZE', 2, 0, 1, true], + 0x37: ['CALLDATACOPY', 3, 3, 0, true], + 0x38: ['CODESIZE', 2, 0, 1, false], + 0x39: ['CODECOPY', 3, 3, 0, false], + 0x3a: ['GASPRICE', 2, 0, 1, false], + 0x3b: ['EXTCODESIZE', 700, 1, 1, true, true], + 0x3c: ['EXTCODECOPY', 700, 4, 0, true, true], + 0x3d: ['RETURNDATASIZE', 2, 0, 1, true], + 0x3e: ['RETURNDATACOPY', 3, 3, 0, true], + 0x3f: ['EXTCODEHASH', 400, 3, 0, true], + + // '0x40' range - block operations + 0x40: ['BLOCKHASH', 20, 1, 1, true, true], + 0x41: ['COINBASE', 2, 0, 1, true], + 0x42: ['TIMESTAMP', 2, 0, 1, true], + 0x43: ['NUMBER', 2, 0, 1, true], + 0x44: ['DIFFICULTY', 2, 0, 1, true], + 0x45: ['GASLIMIT', 2, 0, 1, true], + 0x46: ['CHAINID', 2, 0, 1, false], + 0x47: ['SELFBALANCE', 5, 0, 1, false], + + // 0x50 range - 'storage' and execution + 0x50: ['POP', 2, 1, 0, false], + 0x51: ['MLOAD', 3, 1, 1, false], + 0x52: ['MSTORE', 3, 2, 0, false], + 0x53: ['MSTORE8', 3, 2, 0, false], + 0x54: ['SLOAD', 800, 1, 1, true, true], + 0x55: ['SSTORE', 0, 2, 0, true, true], + 0x56: ['JUMP', 8, 1, 0, false], + 0x57: ['JUMPI', 10, 2, 0, false], + 0x58: ['PC', 2, 0, 1, false], + 0x59: ['MSIZE', 2, 0, 1, false], + 0x5a: ['GAS', 2, 0, 1, false], + 0x5b: ['JUMPDEST', 1, 0, 0, false], + + // 0x60, range + 0x60: ['PUSH1', 3, 0, 1, false], + 0x61: ['PUSH2', 3, 0, 1, false], + 0x62: ['PUSH3', 3, 0, 1, false], + 0x63: ['PUSH4', 3, 0, 1, false], + 0x64: ['PUSH5', 3, 0, 1, false], + 0x65: ['PUSH6', 3, 0, 1, false], + 0x66: ['PUSH7', 3, 0, 1, false], + 0x67: ['PUSH8', 3, 0, 1, false], + 0x68: ['PUSH9', 3, 0, 1, false], + 0x69: ['PUSH10', 3, 0, 1, false], + 0x6a: ['PUSH11', 3, 0, 1, false], + 0x6b: ['PUSH12', 3, 0, 1, false], + 0x6c: ['PUSH13', 3, 0, 1, false], + 0x6d: ['PUSH14', 3, 0, 1, false], + 0x6e: ['PUSH15', 3, 0, 1, false], + 0x6f: ['PUSH16', 3, 0, 1, false], + 0x70: ['PUSH17', 3, 0, 1, false], + 0x71: ['PUSH18', 3, 0, 1, false], + 0x72: ['PUSH19', 3, 0, 1, false], + 0x73: ['PUSH20', 3, 0, 1, false], + 0x74: ['PUSH21', 3, 0, 1, false], + 0x75: ['PUSH22', 3, 0, 1, false], + 0x76: ['PUSH23', 3, 0, 1, false], + 0x77: ['PUSH24', 3, 0, 1, false], + 0x78: ['PUSH25', 3, 0, 1, false], + 0x79: ['PUSH26', 3, 0, 1, false], + 0x7a: ['PUSH27', 3, 0, 1, false], + 0x7b: ['PUSH28', 3, 0, 1, false], + 0x7c: ['PUSH29', 3, 0, 1, false], + 0x7d: ['PUSH30', 3, 0, 1, false], + 0x7e: ['PUSH31', 3, 0, 1, false], + 0x7f: ['PUSH32', 3, 0, 1, false], + + 0x80: ['DUP1', 3, 0, 1, false], + 0x81: ['DUP2', 3, 0, 1, false], + 0x82: ['DUP3', 3, 0, 1, false], + 0x83: ['DUP4', 3, 0, 1, false], + 0x84: ['DUP5', 3, 0, 1, false], + 0x85: ['DUP6', 3, 0, 1, false], + 0x86: ['DUP7', 3, 0, 1, false], + 0x87: ['DUP8', 3, 0, 1, false], + 0x88: ['DUP9', 3, 0, 1, false], + 0x89: ['DUP10', 3, 0, 1, false], + 0x8a: ['DUP11', 3, 0, 1, false], + 0x8b: ['DUP12', 3, 0, 1, false], + 0x8c: ['DUP13', 3, 0, 1, false], + 0x8d: ['DUP14', 3, 0, 1, false], + 0x8e: ['DUP15', 3, 0, 1, false], + 0x8f: ['DUP16', 3, 0, 1, false], + + 0x90: ['SWAP1', 3, 0, 0, false], + 0x91: ['SWAP2', 3, 0, 0, false], + 0x92: ['SWAP3', 3, 0, 0, false], + 0x93: ['SWAP4', 3, 0, 0, false], + 0x94: ['SWAP5', 3, 0, 0, false], + 0x95: ['SWAP6', 3, 0, 0, false], + 0x96: ['SWAP7', 3, 0, 0, false], + 0x97: ['SWAP8', 3, 0, 0, false], + 0x98: ['SWAP9', 3, 0, 0, false], + 0x99: ['SWAP10', 3, 0, 0, false], + 0x9a: ['SWAP11', 3, 0, 0, false], + 0x9b: ['SWAP12', 3, 0, 0, false], + 0x9c: ['SWAP13', 3, 0, 0, false], + 0x9d: ['SWAP14', 3, 0, 0, false], + 0x9e: ['SWAP15', 3, 0, 0, false], + 0x9f: ['SWAP16', 3, 0, 0, false], + + 0xa0: ['LOG0', 375, 2, 0, false], + 0xa1: ['LOG1', 375, 3, 0, false], + 0xa2: ['LOG2', 375, 4, 0, false], + 0xa3: ['LOG3', 375, 5, 0, false], + 0xa4: ['LOG4', 375, 6, 0, false], + + // '0xf0' range - closures + 0xf0: ['CREATE', 32000, 3, 1, true, true], + 0xf1: ['CALL', 700, 7, 1, true, true], + 0xf2: ['CALLCODE', 700, 7, 1, true, true], + 0xf3: ['RETURN', 0, 2, 0, false], + 0xf4: ['DELEGATECALL', 700, 6, 1, true, true], + 0xf5: ['CREATE2', 32000, 4, 1, true, true], + 0xfa: ['STATICCALL', 700, 6, 1, true, true], + 0xfd: ['REVERT', 0, 2, 0, false], + + // '0x70', range - other + 0xfe: ['INVALID', 0, 0, 0, false], + 0xff: ['SELFDESTRUCT', 5000, 1, 0, false, true] + } + + const code = codes[op] ? codes[op] : ['INVALID', 0, 0, 0, false, false] + let opcode = code[0] + + if (full) { + if (opcode === 'LOG') { + opcode += op - 0xa0 } - const code = codes[op] ? codes[op] : ['INVALID', 0, 0, 0, false, false] - let opcode = code[0] - - if (full) { - if (opcode === 'LOG') { - opcode += op - 0xa0 - } - - if (opcode === 'PUSH') { - opcode += op - 0x5f - } + if (opcode === 'PUSH') { + opcode += op - 0x5f + } - if (opcode === 'DUP') { - opcode += op - 0x7f - } + if (opcode === 'DUP') { + opcode += op - 0x7f + } - if (opcode === 'SWAP') { - opcode += op - 0x8f - } + if (opcode === 'SWAP') { + opcode += op - 0x8f } + } - return { name: opcode, fee: code[1], in: code[2], out: code[3], dynamic: code[4], async: code[5] } + return { name: opcode, fee: code[1], in: code[2], out: code[3], dynamic: code[4], async: code[5] } } diff --git a/libs/remix-debug/src/debugger/VmDebugger.ts b/libs/remix-debug/src/debugger/VmDebugger.ts index 19a8be3d51..28249024a5 100644 --- a/libs/remix-debug/src/debugger/VmDebugger.ts +++ b/libs/remix-debug/src/debugger/VmDebugger.ts @@ -7,269 +7,269 @@ import { DebuggerSolidityLocals } from './solidityLocals' const { ui } = helpers export class VmDebuggerLogic { - event - debugger - stepManager - _traceManager - _codeManager - _solidityProxy - _callTree - storageResolver - tx - debuggerSolidityState - debuggerSolidityLocals - address - traceLength - addresses + event + debugger + stepManager + _traceManager + _codeManager + _solidityProxy + _callTree + storageResolver + tx + debuggerSolidityState + debuggerSolidityLocals + address + traceLength + addresses - constructor (_debugger, tx, _stepManager, _traceManager, _codeManager, _solidityProxy, _callTree) { - this.event = new EventManager() - this.debugger = _debugger - this.stepManager = _stepManager - this._traceManager = _traceManager - this._codeManager = _codeManager - this._solidityProxy = _solidityProxy - this._callTree = _callTree - this.storageResolver = null - this.tx = tx + constructor (_debugger, tx, _stepManager, _traceManager, _codeManager, _solidityProxy, _callTree) { + this.event = new EventManager() + this.debugger = _debugger + this.stepManager = _stepManager + this._traceManager = _traceManager + this._codeManager = _codeManager + this._solidityProxy = _solidityProxy + this._callTree = _callTree + this.storageResolver = null + this.tx = tx - this.debuggerSolidityState = new DebuggerSolidityState(tx, _stepManager, _traceManager, _codeManager, _solidityProxy) - this.debuggerSolidityLocals = new DebuggerSolidityLocals(tx, _stepManager, _traceManager, _callTree) - } + this.debuggerSolidityState = new DebuggerSolidityState(tx, _stepManager, _traceManager, _codeManager, _solidityProxy) + this.debuggerSolidityLocals = new DebuggerSolidityLocals(tx, _stepManager, _traceManager, _callTree) + } - start () { - this.listenToEvents() - this.listenToCodeManagerEvents() - this.listenToTraceManagerEvents() - this.listenToFullStorageChanges() - this.listenToNewChanges() + start () { + this.listenToEvents() + this.listenToCodeManagerEvents() + this.listenToTraceManagerEvents() + this.listenToFullStorageChanges() + this.listenToNewChanges() - this.listenToSolidityStateEvents() - this.listenToSolidityLocalsEvents() - } + this.listenToSolidityStateEvents() + this.listenToSolidityLocalsEvents() + } - listenToEvents () { - this.debugger.event.register('traceUnloaded', () => { - this.event.trigger('traceUnloaded') - }) + listenToEvents () { + this.debugger.event.register('traceUnloaded', () => { + this.event.trigger('traceUnloaded') + }) - this.debugger.event.register('newTraceLoaded', () => { - this.event.trigger('newTraceLoaded') - }) - } + this.debugger.event.register('newTraceLoaded', () => { + this.event.trigger('newTraceLoaded') + }) + } - listenToCodeManagerEvents () { - this._codeManager.event.register('changed', (code, address, index, nextIndexes, returnInstructionIndexes, outOfGasInstructionIndexes) => { - this.event.trigger('codeManagerChanged', [code, address, index, nextIndexes, returnInstructionIndexes, outOfGasInstructionIndexes]) - }) - } + listenToCodeManagerEvents () { + this._codeManager.event.register('changed', (code, address, index, nextIndexes, returnInstructionIndexes, outOfGasInstructionIndexes) => { + this.event.trigger('codeManagerChanged', [code, address, index, nextIndexes, returnInstructionIndexes, outOfGasInstructionIndexes]) + }) + } - listenToTraceManagerEvents () { - let triggerStorageUpdateStampId - this.event.register('indexChanged', this, (index) => { - if (index < 0) return - if (this.stepManager.currentStepIndex !== index) return + listenToTraceManagerEvents () { + let triggerStorageUpdateStampId + this.event.register('indexChanged', this, (index) => { + if (index < 0) return + if (this.stepManager.currentStepIndex !== index) return - this.event.trigger('indexUpdate', [index]) + this.event.trigger('indexUpdate', [index]) - try { - const calldata = this._traceManager.getCallDataAt(index) - if (this.stepManager.currentStepIndex === index) { - this.event.trigger('traceManagerCallDataUpdate', [calldata]) - } - } catch (error) { - this.event.trigger('traceManagerCallDataUpdate', [{}]) - } + try { + const calldata = this._traceManager.getCallDataAt(index) + if (this.stepManager.currentStepIndex === index) { + this.event.trigger('traceManagerCallDataUpdate', [calldata]) + } + } catch (error) { + this.event.trigger('traceManagerCallDataUpdate', [{}]) + } - try { - const callstack = this._traceManager.getCallStackAt(index) - if (this.stepManager.currentStepIndex === index) { - this.event.trigger('traceManagerCallStackUpdate', [callstack]) - } - } catch (error) { - this.event.trigger('traceManagerCallStackUpdate', [{}]) - } + try { + const callstack = this._traceManager.getCallStackAt(index) + if (this.stepManager.currentStepIndex === index) { + this.event.trigger('traceManagerCallStackUpdate', [callstack]) + } + } catch (error) { + this.event.trigger('traceManagerCallStackUpdate', [{}]) + } - try { - const callstack = this._traceManager.getStackAt(index) - if (this.stepManager.currentStepIndex === index) { - this.event.trigger('traceManagerStackUpdate', [callstack]) - } - } catch (error) { - this.event.trigger('traceManagerStackUpdate', [{}]) - } + try { + const callstack = this._traceManager.getStackAt(index) + if (this.stepManager.currentStepIndex === index) { + this.event.trigger('traceManagerStackUpdate', [callstack]) + } + } catch (error) { + this.event.trigger('traceManagerStackUpdate', [{}]) + } - if (triggerStorageUpdateStampId) { - clearTimeout(triggerStorageUpdateStampId) - triggerStorageUpdateStampId = null - } - triggerStorageUpdateStampId = setTimeout(() => { - (() => { - try { - this.event.trigger('functionsStackUpdate', [this._callTree.retrieveFunctionsStack(index)]) - } catch (e) { - console.log(e) - } + if (triggerStorageUpdateStampId) { + clearTimeout(triggerStorageUpdateStampId) + triggerStorageUpdateStampId = null + } + triggerStorageUpdateStampId = setTimeout(() => { + (() => { + try { + this.event.trigger('functionsStackUpdate', [this._callTree.retrieveFunctionsStack(index)]) + } catch (e) { + console.log(e) + } - try { - const memory = this._traceManager.getMemoryAt(index) - if (this.stepManager.currentStepIndex === index) { - this.event.trigger('traceManagerMemoryUpdate', [ui.formatMemory(memory, 32)]) - } - } catch (error) { - this.event.trigger('traceManagerMemoryUpdate', [{}]) - } - try { - const address = this._traceManager.getCurrentCalledAddressAt(index) - if (!this.storageResolver) return + try { + const memory = this._traceManager.getMemoryAt(index) + if (this.stepManager.currentStepIndex === index) { + this.event.trigger('traceManagerMemoryUpdate', [ui.formatMemory(memory, 32)]) + } + } catch (error) { + this.event.trigger('traceManagerMemoryUpdate', [{}]) + } + try { + const address = this._traceManager.getCurrentCalledAddressAt(index) + if (!this.storageResolver) return - const storageViewer = new StorageViewer({ stepIndex: this.stepManager.currentStepIndex, tx: this.tx, address: address }, this.storageResolver, this._traceManager) + const storageViewer = new StorageViewer({ stepIndex: this.stepManager.currentStepIndex, tx: this.tx, address: address }, this.storageResolver, this._traceManager) - storageViewer.storageRange().then((storage) => { - if (this.stepManager.currentStepIndex === index) { - const header = storageViewer.isComplete(address) ? '[Completely Loaded]' : '[Partially Loaded]' - this.event.trigger('traceManagerStorageUpdate', [storage, header]) - } - }).catch((_error) => { - this.event.trigger('traceManagerStorageUpdate', [{}]) - }) - } catch (error) { - this.event.trigger('traceManagerStorageUpdate', [{}]) - } - - try { - const returnValue = this._traceManager.getReturnValue(index) - if (this.stepManager.currentStepIndex === index) { - this.event.trigger('traceReturnValueUpdate', [[returnValue]]) - } - } catch (error) { - this.event.trigger('traceReturnValueUpdate', [[error]]) - } - })() - }, 1000) + storageViewer.storageRange().then((storage) => { + if (this.stepManager.currentStepIndex === index) { + const header = storageViewer.isComplete(address) ? '[Completely Loaded]' : '[Partially Loaded]' + this.event.trigger('traceManagerStorageUpdate', [storage, header]) + } + }).catch((_error) => { + this.event.trigger('traceManagerStorageUpdate', [{}]) + }) + } catch (error) { + this.event.trigger('traceManagerStorageUpdate', [{}]) + } - try { - const step = this._traceManager.getCurrentStep(index) - this.event.trigger('traceCurrentStepUpdate', [null, step]) - } catch (error) { - this.event.trigger('traceCurrentStepUpdate', [error]) + try { + const returnValue = this._traceManager.getReturnValue(index) + if (this.stepManager.currentStepIndex === index) { + this.event.trigger('traceReturnValueUpdate', [[returnValue]]) } + } catch (error) { + this.event.trigger('traceReturnValueUpdate', [[error]]) + } + })() + }, 1000) - try { - const addmem = this._traceManager.getMemExpand(index) - this.event.trigger('traceMemExpandUpdate', [null, addmem]) - } catch (error) { - this.event.trigger('traceMemExpandUpdate', [error]) - } + try { + const step = this._traceManager.getCurrentStep(index) + this.event.trigger('traceCurrentStepUpdate', [null, step]) + } catch (error) { + this.event.trigger('traceCurrentStepUpdate', [error]) + } - try { - const gas = this._traceManager.getStepCost(index) - this.event.trigger('traceStepCostUpdate', [null, gas]) - } catch (error) { - this.event.trigger('traceStepCostUpdate', [error]) - } + try { + const addmem = this._traceManager.getMemExpand(index) + this.event.trigger('traceMemExpandUpdate', [null, addmem]) + } catch (error) { + this.event.trigger('traceMemExpandUpdate', [error]) + } - try { - const address = this._traceManager.getCurrentCalledAddressAt(index) - this.event.trigger('traceCurrentCalledAddressAtUpdate', [null, address]) - } catch (error) { - this.event.trigger('traceCurrentCalledAddressAtUpdate', [error]) - } + try { + const gas = this._traceManager.getStepCost(index) + this.event.trigger('traceStepCostUpdate', [null, gas]) + } catch (error) { + this.event.trigger('traceStepCostUpdate', [error]) + } - try { - const remaining = this._traceManager.getRemainingGas(index) - this.event.trigger('traceRemainingGasUpdate', [null, remaining]) - } catch (error) { - this.event.trigger('traceRemainingGasUpdate', [error]) - } - }) - } + try { + const address = this._traceManager.getCurrentCalledAddressAt(index) + this.event.trigger('traceCurrentCalledAddressAtUpdate', [null, address]) + } catch (error) { + this.event.trigger('traceCurrentCalledAddressAtUpdate', [error]) + } - listenToFullStorageChanges () { - this.address = [] - this.traceLength = 0 + try { + const remaining = this._traceManager.getRemainingGas(index) + this.event.trigger('traceRemainingGasUpdate', [null, remaining]) + } catch (error) { + this.event.trigger('traceRemainingGasUpdate', [error]) + } + }) + } - this.debugger.event.register('newTraceLoaded', (length) => { - const addresses = this._traceManager.getAddresses() - this.event.trigger('traceAddressesUpdate', [addresses]) - this.addresses = addresses + listenToFullStorageChanges () { + this.address = [] + this.traceLength = 0 - this._traceManager.getLength((error, length) => { - if (error) return - this.event.trigger('traceLengthUpdate', [length]) - this.traceLength = length - }) - }) + this.debugger.event.register('newTraceLoaded', (length) => { + const addresses = this._traceManager.getAddresses() + this.event.trigger('traceAddressesUpdate', [addresses]) + this.addresses = addresses - this.debugger.event.register('indexChanged', this, async (index) => { - if (index < 0) return - if (this.stepManager.currentStepIndex !== index) return - if (!this.storageResolver) return - // Clean up storage update - if (index === this.traceLength - 1) { - return this.event.trigger('traceStorageUpdate', [{}]) - } - const storageJSON = {} - for (const k in this.addresses) { - const address = this.addresses[k] - const storageViewer = new StorageViewer({ stepIndex: this.stepManager.currentStepIndex, tx: this.tx, address: address }, this.storageResolver, this._traceManager) - try { - storageJSON[address] = await storageViewer.storageRange() - } catch (e) { - console.error(e) - } - } - this.event.trigger('traceStorageUpdate', [storageJSON]) - }) - } + this._traceManager.getLength((error, length) => { + if (error) return + this.event.trigger('traceLengthUpdate', [length]) + this.traceLength = length + }) + }) - listenToNewChanges () { - this.debugger.event.register('newTraceLoaded', this, () => { - this.storageResolver = new StorageResolver({ web3: this.debugger.web3 }) - this.debuggerSolidityState.storageResolver = this.storageResolver - this.debuggerSolidityLocals.storageResolver = this.storageResolver - this.event.trigger('newTrace', []) - }) + this.debugger.event.register('indexChanged', this, async (index) => { + if (index < 0) return + if (this.stepManager.currentStepIndex !== index) return + if (!this.storageResolver) return + // Clean up storage update + if (index === this.traceLength - 1) { + return this.event.trigger('traceStorageUpdate', [{}]) + } + const storageJSON = {} + for (const k in this.addresses) { + const address = this.addresses[k] + const storageViewer = new StorageViewer({ stepIndex: this.stepManager.currentStepIndex, tx: this.tx, address: address }, this.storageResolver, this._traceManager) + try { + storageJSON[address] = await storageViewer.storageRange() + } catch (e) { + console.error(e) + } + } + this.event.trigger('traceStorageUpdate', [storageJSON]) + }) + } - this.debugger.callTree.event.register('callTreeReady', () => { - if (this.debugger.callTree.reducedTrace.length) { - return this.event.trigger('newCallTree', []) - } - }) - } + listenToNewChanges () { + this.debugger.event.register('newTraceLoaded', this, () => { + this.storageResolver = new StorageResolver({ web3: this.debugger.web3 }) + this.debuggerSolidityState.storageResolver = this.storageResolver + this.debuggerSolidityLocals.storageResolver = this.storageResolver + this.event.trigger('newTrace', []) + }) + + this.debugger.callTree.event.register('callTreeReady', () => { + if (this.debugger.callTree.reducedTrace.length) { + return this.event.trigger('newCallTree', []) + } + }) + } - listenToSolidityStateEvents () { - this.event.register('indexChanged', this.debuggerSolidityState.init.bind(this.debuggerSolidityState)) - this.debuggerSolidityState.event.register('solidityState', (state) => { - this.event.trigger('solidityState', [state]) - }) - this.debuggerSolidityState.event.register('solidityStateMessage', (message) => { - this.event.trigger('solidityStateMessage', [message]) - }) - this.debuggerSolidityState.event.register('solidityStateUpdating', () => { - this.event.trigger('solidityStateUpdating', []) - }) - this.event.register('traceUnloaded', this.debuggerSolidityState.reset.bind(this.debuggerSolidityState)) - this.event.register('newTraceLoaded', this.debuggerSolidityState.reset.bind(this.debuggerSolidityState)) - } + listenToSolidityStateEvents () { + this.event.register('indexChanged', this.debuggerSolidityState.init.bind(this.debuggerSolidityState)) + this.debuggerSolidityState.event.register('solidityState', (state) => { + this.event.trigger('solidityState', [state]) + }) + this.debuggerSolidityState.event.register('solidityStateMessage', (message) => { + this.event.trigger('solidityStateMessage', [message]) + }) + this.debuggerSolidityState.event.register('solidityStateUpdating', () => { + this.event.trigger('solidityStateUpdating', []) + }) + this.event.register('traceUnloaded', this.debuggerSolidityState.reset.bind(this.debuggerSolidityState)) + this.event.register('newTraceLoaded', this.debuggerSolidityState.reset.bind(this.debuggerSolidityState)) + } - listenToSolidityLocalsEvents () { - this.event.register('sourceLocationChanged', this.debuggerSolidityLocals.init.bind(this.debuggerSolidityLocals)) - this.event.register('solidityLocalsLoadMore', this.debuggerSolidityLocals.decodeMore.bind(this.debuggerSolidityLocals)) - this.debuggerSolidityLocals.event.register('solidityLocalsLoadMoreCompleted', (locals) => { - this.event.trigger('solidityLocalsLoadMoreCompleted', [locals]) - }) - this.debuggerSolidityLocals.event.register('solidityLocals', (state) => { - this.event.trigger('solidityLocals', [state]) - }) - this.debuggerSolidityLocals.event.register('solidityLocalsMessage', (message) => { - this.event.trigger('solidityLocalsMessage', [message]) - }) - this.debuggerSolidityLocals.event.register('solidityLocalsUpdating', () => { - this.event.trigger('solidityLocalsUpdating', []) - }) - this.debuggerSolidityLocals.event.register('traceReturnValueUpdate', (data, header) => { - this.event.trigger('traceReturnValueUpdate', [data, header]) - }) - } + listenToSolidityLocalsEvents () { + this.event.register('sourceLocationChanged', this.debuggerSolidityLocals.init.bind(this.debuggerSolidityLocals)) + this.event.register('solidityLocalsLoadMore', this.debuggerSolidityLocals.decodeMore.bind(this.debuggerSolidityLocals)) + this.debuggerSolidityLocals.event.register('solidityLocalsLoadMoreCompleted', (locals) => { + this.event.trigger('solidityLocalsLoadMoreCompleted', [locals]) + }) + this.debuggerSolidityLocals.event.register('solidityLocals', (state) => { + this.event.trigger('solidityLocals', [state]) + }) + this.debuggerSolidityLocals.event.register('solidityLocalsMessage', (message) => { + this.event.trigger('solidityLocalsMessage', [message]) + }) + this.debuggerSolidityLocals.event.register('solidityLocalsUpdating', () => { + this.event.trigger('solidityLocalsUpdating', []) + }) + this.debuggerSolidityLocals.event.register('traceReturnValueUpdate', (data, header) => { + this.event.trigger('traceReturnValueUpdate', [data, header]) + }) + } } diff --git a/libs/remix-debug/src/debugger/debugger.ts b/libs/remix-debug/src/debugger/debugger.ts index a939357a98..1b5e01bce1 100644 --- a/libs/remix-debug/src/debugger/debugger.ts +++ b/libs/remix-debug/src/debugger/debugger.ts @@ -7,176 +7,176 @@ import { DebuggerStepManager } from './stepManager' import { VmDebuggerLogic } from './VmDebugger' export class Debugger { - event - offsetToLineColumnConverter - compilationResult - debugger - breakPointManager - step_manager // eslint-disable-line camelcase - vmDebuggerLogic - currentFile = -1 - currentLine = -1 - - constructor (options) { - this.event = new EventManager() - this.offsetToLineColumnConverter = options.offsetToLineColumnConverter - /* + event + offsetToLineColumnConverter + compilationResult + debugger + breakPointManager + step_manager // eslint-disable-line camelcase + vmDebuggerLogic + currentFile = -1 + currentLine = -1 + + constructor (options) { + this.event = new EventManager() + this.offsetToLineColumnConverter = options.offsetToLineColumnConverter + /* Returns a compilation result for a given address or the last one available if none are found */ - this.compilationResult = options.compilationResult || function (contractAddress) { return null } - - this.debugger = new Ethdebugger({ - web3: options.web3, - debugWithGeneratedSources: options.debugWithGeneratedSources, - compilationResult: this.compilationResult, - offsetToLineColumnConverter: this.offsetToLineColumnConverter - }) - - const { traceManager, callTree, solidityProxy } = this.debugger - this.breakPointManager = new BreakpointManager({ - traceManager, - callTree, - solidityProxy - }) - - this.breakPointManager.event.register('breakpointStep', (step) => { - this.step_manager.jumpTo(step) - }) - - this.breakPointManager.event.register('noBreakpointHit', (step) => { - this.event.trigger('noBreakpointHit', []) - }) - - this.breakPointManager.event.register('locatingBreakpoint', () => { - this.event.trigger('locatingBreakpoint', []) - }) - - this.debugger.setBreakpointManager(this.breakPointManager) - - this.debugger.event.register('newTraceLoaded', this, () => { - this.event.trigger('debuggerStatus', [true]) - }) - - this.debugger.event.register('traceUnloaded', this, () => { - this.event.trigger('debuggerStatus', [false]) - }) - } - - async registerAndHighlightCodeItem (index) { - // register selected code item, highlight the corresponding source location - // this.debugger.traceManager.getCurrentCalledAddressAt(index, async (error, address) => { - - try { - const address = this.debugger.traceManager.getCurrentCalledAddressAt(index) - const compilationResultForAddress = await this.compilationResult(address) - if (!compilationResultForAddress) { - this.event.trigger('newSourceLocation', [null]) - this.currentFile = -1 - this.currentLine = -1 - this.vmDebuggerLogic.event.trigger('lineGasCostChanged', [null]) - return - } - - this.debugger.callTree.getValidSourceLocationFromVMTraceIndexFromCache(address, index, compilationResultForAddress.data.contracts).then(async (rawLocationAndOpcode) => { - if (compilationResultForAddress && compilationResultForAddress.data) { - const rawLocation = rawLocationAndOpcode.sourceLocation - const stepDetail = rawLocationAndOpcode.stepDetail - const generatedSources = this.debugger.callTree.sourceLocationTracker.getGeneratedSourcesFromAddress(address) - - const lineColumnPos = rawLocationAndOpcode.lineColumnPos + this.compilationResult = options.compilationResult || function (contractAddress) { return null } + + this.debugger = new Ethdebugger({ + web3: options.web3, + debugWithGeneratedSources: options.debugWithGeneratedSources, + compilationResult: this.compilationResult, + offsetToLineColumnConverter: this.offsetToLineColumnConverter + }) + + const { traceManager, callTree, solidityProxy } = this.debugger + this.breakPointManager = new BreakpointManager({ + traceManager, + callTree, + solidityProxy + }) + + this.breakPointManager.event.register('breakpointStep', (step) => { + this.step_manager.jumpTo(step) + }) + + this.breakPointManager.event.register('noBreakpointHit', (step) => { + this.event.trigger('noBreakpointHit', []) + }) + + this.breakPointManager.event.register('locatingBreakpoint', () => { + this.event.trigger('locatingBreakpoint', []) + }) + + this.debugger.setBreakpointManager(this.breakPointManager) + + this.debugger.event.register('newTraceLoaded', this, () => { + this.event.trigger('debuggerStatus', [true]) + }) + + this.debugger.event.register('traceUnloaded', this, () => { + this.event.trigger('debuggerStatus', [false]) + }) + } + + async registerAndHighlightCodeItem (index) { + // register selected code item, highlight the corresponding source location + // this.debugger.traceManager.getCurrentCalledAddressAt(index, async (error, address) => { + + try { + const address = this.debugger.traceManager.getCurrentCalledAddressAt(index) + const compilationResultForAddress = await this.compilationResult(address) + if (!compilationResultForAddress) { + this.event.trigger('newSourceLocation', [null]) + this.currentFile = -1 + this.currentLine = -1 + this.vmDebuggerLogic.event.trigger('lineGasCostChanged', [null]) + return + } + + this.debugger.callTree.getValidSourceLocationFromVMTraceIndexFromCache(address, index, compilationResultForAddress.data.contracts).then(async (rawLocationAndOpcode) => { + if (compilationResultForAddress && compilationResultForAddress.data) { + const rawLocation = rawLocationAndOpcode.sourceLocation + const stepDetail = rawLocationAndOpcode.stepDetail + const generatedSources = this.debugger.callTree.sourceLocationTracker.getGeneratedSourcesFromAddress(address) + + const lineColumnPos = rawLocationAndOpcode.lineColumnPos - let lineGasCostObj = null - try { - lineGasCostObj = await this.debugger.callTree.getGasCostPerLine(rawLocation.file, lineColumnPos.start.line) - } catch (e) { - console.log(e) - } - this.event.trigger('newSourceLocation', [lineColumnPos, rawLocation, generatedSources, address, stepDetail, (lineGasCostObj && lineGasCostObj.gasCost) || -1]) - this.vmDebuggerLogic.event.trigger('sourceLocationChanged', [rawLocation]) - if (this.currentFile !== rawLocation.file || this.currentLine !== lineColumnPos.start.line) { - const instructionIndexes = lineGasCostObj.indexes.map((index) => { // translate from vmtrace index to instruction index - return this.debugger.codeManager.getInstructionIndex(address, index) - }) - this.vmDebuggerLogic.event.trigger('lineGasCostChanged', [instructionIndexes, lineColumnPos.start.line ]) - this.currentFile = rawLocation.file - this.currentLine = lineColumnPos.start.line - } - } else { - this.event.trigger('newSourceLocation', [null]) - this.currentFile = -1 - this.currentLine = -1 - this.vmDebuggerLogic.event.trigger('lineGasCostChanged', [null]) - } - }).catch((_error) => { - this.event.trigger('newSourceLocation', [null]) - this.vmDebuggerLogic.event.trigger('sourceLocationChanged', [null]) - this.currentFile = -1 - this.currentLine = -1 - this.vmDebuggerLogic.event.trigger('lineGasCostChanged', [null]) + let lineGasCostObj = null + try { + lineGasCostObj = await this.debugger.callTree.getGasCostPerLine(rawLocation.file, lineColumnPos.start.line) + } catch (e) { + console.log(e) + } + this.event.trigger('newSourceLocation', [lineColumnPos, rawLocation, generatedSources, address, stepDetail, (lineGasCostObj && lineGasCostObj.gasCost) || -1]) + this.vmDebuggerLogic.event.trigger('sourceLocationChanged', [rawLocation]) + if (this.currentFile !== rawLocation.file || this.currentLine !== lineColumnPos.start.line) { + const instructionIndexes = lineGasCostObj.indexes.map((index) => { // translate from vmtrace index to instruction index + return this.debugger.codeManager.getInstructionIndex(address, index) }) - // }) - } catch (error) { - this.event.trigger('newSourceLocation', [null]) - this.vmDebuggerLogic.event.trigger('sourceLocationChanged', [null]) - this.currentFile = -1 - this.currentLine = -1 - this.vmDebuggerLogic.event.trigger('lineGasCostChanged', [null]) - return console.log(error) - } - } - - updateWeb3 (web3) { - this.debugger.web3 = web3 - } - - async debug (blockNumber, txNumber, tx, loadingCb) { - const web3 = this.debugger.web3 - - if (this.debugger.traceManager.isLoading) { - return - } - - if (tx) { - if (!tx.to) { - tx.to = contractCreationToken('0') - } - return await this.debugTx(tx, loadingCb) - } - - if (txNumber.indexOf('0x') !== -1) { - tx = await web3.eth.getTransaction(txNumber) - if (!tx) throw new Error('cannot find transaction ' + txNumber) + this.vmDebuggerLogic.event.trigger('lineGasCostChanged', [instructionIndexes, lineColumnPos.start.line ]) + this.currentFile = rawLocation.file + this.currentLine = lineColumnPos.start.line + } } else { - tx = await web3.eth.getTransactionFromBlock(blockNumber, txNumber) - if (!tx) throw new Error('cannot find transaction ' + blockNumber + ' ' + txNumber) + this.event.trigger('newSourceLocation', [null]) + this.currentFile = -1 + this.currentLine = -1 + this.vmDebuggerLogic.event.trigger('lineGasCostChanged', [null]) } - return await this.debugTx(tx, loadingCb) + }).catch((_error) => { + this.event.trigger('newSourceLocation', [null]) + this.vmDebuggerLogic.event.trigger('sourceLocationChanged', [null]) + this.currentFile = -1 + this.currentLine = -1 + this.vmDebuggerLogic.event.trigger('lineGasCostChanged', [null]) + }) + // }) + } catch (error) { + this.event.trigger('newSourceLocation', [null]) + this.vmDebuggerLogic.event.trigger('sourceLocationChanged', [null]) + this.currentFile = -1 + this.currentLine = -1 + this.vmDebuggerLogic.event.trigger('lineGasCostChanged', [null]) + return console.log(error) } + } - async debugTx (tx, loadingCb) { - this.step_manager = new DebuggerStepManager(this.debugger, this.debugger.traceManager) - - this.vmDebuggerLogic = new VmDebuggerLogic(this.debugger, tx, this.step_manager, this.debugger.traceManager, this.debugger.codeManager, this.debugger.solidityProxy, this.debugger.callTree) - this.vmDebuggerLogic.start() + updateWeb3 (web3) { + this.debugger.web3 = web3 + } - this.step_manager.event.register('stepChanged', this, (stepIndex) => { - if (typeof stepIndex !== 'number' || stepIndex >= this.step_manager.traceLength) { - return this.event.trigger('endDebug') - } + async debug (blockNumber, txNumber, tx, loadingCb) { + const web3 = this.debugger.web3 - this.debugger.codeManager.resolveStep(stepIndex, tx) - this.step_manager.event.trigger('indexChanged', [stepIndex]) - this.vmDebuggerLogic.event.trigger('indexChanged', [stepIndex]) - this.vmDebuggerLogic.debugger.event.trigger('indexChanged', [stepIndex]) - this.registerAndHighlightCodeItem(stepIndex) - }) + if (this.debugger.traceManager.isLoading) { + return + } - loadingCb() - await this.debugger.debug(tx) + if (tx) { + if (!tx.to) { + tx.to = contractCreationToken('0') + } + return await this.debugTx(tx, loadingCb) } - unload () { - this.debugger.unLoad() - this.event.trigger('debuggerUnloaded') + if (txNumber.indexOf('0x') !== -1) { + tx = await web3.eth.getTransaction(txNumber) + if (!tx) throw new Error('cannot find transaction ' + txNumber) + } else { + tx = await web3.eth.getTransactionFromBlock(blockNumber, txNumber) + if (!tx) throw new Error('cannot find transaction ' + blockNumber + ' ' + txNumber) } + return await this.debugTx(tx, loadingCb) + } + + async debugTx (tx, loadingCb) { + this.step_manager = new DebuggerStepManager(this.debugger, this.debugger.traceManager) + + this.vmDebuggerLogic = new VmDebuggerLogic(this.debugger, tx, this.step_manager, this.debugger.traceManager, this.debugger.codeManager, this.debugger.solidityProxy, this.debugger.callTree) + this.vmDebuggerLogic.start() + + this.step_manager.event.register('stepChanged', this, (stepIndex) => { + if (typeof stepIndex !== 'number' || stepIndex >= this.step_manager.traceLength) { + return this.event.trigger('endDebug') + } + + this.debugger.codeManager.resolveStep(stepIndex, tx) + this.step_manager.event.trigger('indexChanged', [stepIndex]) + this.vmDebuggerLogic.event.trigger('indexChanged', [stepIndex]) + this.vmDebuggerLogic.debugger.event.trigger('indexChanged', [stepIndex]) + this.registerAndHighlightCodeItem(stepIndex) + }) + + loadingCb() + await this.debugger.debug(tx) + } + + unload () { + this.debugger.unLoad() + this.event.trigger('debuggerUnloaded') + } } diff --git a/libs/remix-debug/src/debugger/solidityLocals.ts b/libs/remix-debug/src/debugger/solidityLocals.ts index bbc9afd179..c15c6443d0 100644 --- a/libs/remix-debug/src/debugger/solidityLocals.ts +++ b/libs/remix-debug/src/debugger/solidityLocals.ts @@ -3,111 +3,111 @@ import { solidityLocals } from '../solidity-decoder/localDecoder' import { StorageViewer } from '../storage/storageViewer' export class DebuggerSolidityLocals { - event - stepManager - internalTreeCall - storageResolver - traceManager - tx - _sourceLocation - decodeTimeout + event + stepManager + internalTreeCall + storageResolver + traceManager + tx + _sourceLocation + decodeTimeout - constructor (tx, _stepManager, _traceManager, _internalTreeCall) { - this.event = new EventManager() - this.stepManager = _stepManager - this.internalTreeCall = _internalTreeCall - this.storageResolver = null - this.traceManager = _traceManager - this.tx = tx - this.decodeTimeout = null + constructor (tx, _stepManager, _traceManager, _internalTreeCall) { + this.event = new EventManager() + this.stepManager = _stepManager + this.internalTreeCall = _internalTreeCall + this.storageResolver = null + this.traceManager = _traceManager + this.tx = tx + this.decodeTimeout = null + } + + init (sourceLocation) { + this._sourceLocation = sourceLocation + if (!this.storageResolver) { + return this.event.trigger('solidityLocalsMessage', ['storage not ready']) + } + if (this.decodeTimeout) { + window.clearTimeout(this.decodeTimeout) } + this.event.trigger('solidityLocalsUpdating') + this.decodeTimeout = setTimeout(() => { + this.decode(sourceLocation) + }, 1000) + } - init (sourceLocation) { - this._sourceLocation = sourceLocation - if (!this.storageResolver) { - return this.event.trigger('solidityLocalsMessage', ['storage not ready']) + decode (sourceLocation, cursor?) { + const self = this + this.event.trigger('solidityLocalsMessage', ['']) + this.traceManager.waterfall([ + function getStackAt (stepIndex, callback) { + try { + const result = self.traceManager.getStackAt(stepIndex) + callback(null, result) + } catch (error) { + callback(error) } - if (this.decodeTimeout) { - window.clearTimeout(this.decodeTimeout) + }, + function getMemoryAt (stepIndex, callback) { + try { + const result = self.traceManager.getMemoryAt(stepIndex) + callback(null, result) + } catch (error) { + callback(error) } - this.event.trigger('solidityLocalsUpdating') - this.decodeTimeout = setTimeout(() => { - this.decode(sourceLocation) - }, 1000) - } - - decode (sourceLocation, cursor?) { - const self = this - this.event.trigger('solidityLocalsMessage', ['']) - this.traceManager.waterfall([ - function getStackAt (stepIndex, callback) { - try { - const result = self.traceManager.getStackAt(stepIndex) - callback(null, result) - } catch (error) { - callback(error) - } - }, - function getMemoryAt (stepIndex, callback) { - try { - const result = self.traceManager.getMemoryAt(stepIndex) - callback(null, result) - } catch (error) { - callback(error) - } - }, - function getCurrentCalledAddressAt (stepIndex, next) { - try { - const address = self.traceManager.getCurrentCalledAddressAt(stepIndex) - next(null, address) - } catch (error) { - next(error) - } - }, - function getCallDataAt (stepIndex, next) { - try { - const calldata = self.traceManager.getCallDataAt(stepIndex) - next(null, calldata) - } catch (error) { - next(error) - } - }], - this.stepManager.currentStepIndex, - (error, result) => { - if (error) { - return error + }, + function getCurrentCalledAddressAt (stepIndex, next) { + try { + const address = self.traceManager.getCurrentCalledAddressAt(stepIndex) + next(null, address) + } catch (error) { + next(error) + } + }, + function getCallDataAt (stepIndex, next) { + try { + const calldata = self.traceManager.getCallDataAt(stepIndex) + next(null, calldata) + } catch (error) { + next(error) + } + }], + this.stepManager.currentStepIndex, + (error, result) => { + if (error) { + return error + } + const stack = result[0].value + const memory = result[1].value + const calldata = result[3].value + try { + const storageViewer = new StorageViewer({ stepIndex: this.stepManager.currentStepIndex, tx: this.tx, address: result[2].value }, this.storageResolver, this.traceManager) + solidityLocals(this.stepManager.currentStepIndex, this.internalTreeCall, stack, memory, storageViewer, calldata, sourceLocation, cursor).then((locals) => { + if (!cursor) { + if (!locals['error']) { + this.event.trigger('solidityLocals', [locals]) + } + if (!Object.keys(locals).length) { + this.event.trigger('solidityLocalsMessage', ['no locals']) } - const stack = result[0].value - const memory = result[1].value - const calldata = result[3].value - try { - const storageViewer = new StorageViewer({ stepIndex: this.stepManager.currentStepIndex, tx: this.tx, address: result[2].value }, this.storageResolver, this.traceManager) - solidityLocals(this.stepManager.currentStepIndex, this.internalTreeCall, stack, memory, storageViewer, calldata, sourceLocation, cursor).then((locals) => { - if (!cursor) { - if (!locals['error']) { - this.event.trigger('solidityLocals', [locals]) - } - if (!Object.keys(locals).length) { - this.event.trigger('solidityLocalsMessage', ['no locals']) - } - } else { - if (!locals['error']) { - this.event.trigger('solidityLocalsLoadMoreCompleted', [locals]) - } - } - }) - } catch (e) { - this.event.trigger('solidityLocalsMessage', [e.message]) + } else { + if (!locals['error']) { + this.event.trigger('solidityLocalsLoadMoreCompleted', [locals]) } + } }) - } + } catch (e) { + this.event.trigger('solidityLocalsMessage', [e.message]) + } + }) + } - decodeMore (cursor) { - let decodeTimeout = null - if (!this.storageResolver) return this.event.trigger('solidityLocalsMessage', ['storage not ready']) - if (decodeTimeout) window.clearTimeout(decodeTimeout) - decodeTimeout = setTimeout(() => { - this.decode(this._sourceLocation, cursor) - }, 500) - } + decodeMore (cursor) { + let decodeTimeout = null + if (!this.storageResolver) return this.event.trigger('solidityLocalsMessage', ['storage not ready']) + if (decodeTimeout) window.clearTimeout(decodeTimeout) + decodeTimeout = setTimeout(() => { + this.decode(this._sourceLocation, cursor) + }, 500) + } } diff --git a/libs/remix-debug/src/debugger/solidityState.ts b/libs/remix-debug/src/debugger/solidityState.ts index 237be4e7da..917668a534 100644 --- a/libs/remix-debug/src/debugger/solidityState.ts +++ b/libs/remix-debug/src/debugger/solidityState.ts @@ -4,81 +4,81 @@ import { decodeState } from '../solidity-decoder/stateDecoder' import { StorageViewer } from '../storage/storageViewer' export class DebuggerSolidityState { - event - storageResolver - stepManager - traceManager - codeManager - solidityProxy - stateVariablesByAddresses - tx - decodeTimeout + event + storageResolver + stepManager + traceManager + codeManager + solidityProxy + stateVariablesByAddresses + tx + decodeTimeout - constructor (tx, _stepManager, _traceManager, _codeManager, _solidityProxy) { - this.event = new EventManager() - this.storageResolver = null - this.stepManager = _stepManager - this.traceManager = _traceManager - this.codeManager = _codeManager - this.solidityProxy = _solidityProxy - this.stateVariablesByAddresses = {} - this.tx = tx - this.decodeTimeout = null - } + constructor (tx, _stepManager, _traceManager, _codeManager, _solidityProxy) { + this.event = new EventManager() + this.storageResolver = null + this.stepManager = _stepManager + this.traceManager = _traceManager + this.codeManager = _codeManager + this.solidityProxy = _solidityProxy + this.stateVariablesByAddresses = {} + this.tx = tx + this.decodeTimeout = null + } - init (index) { - if (index < 0) { - return this.event.trigger('solidityStateMessage', ['invalid step index']) - } + init (index) { + if (index < 0) { + return this.event.trigger('solidityStateMessage', ['invalid step index']) + } - if (this.stepManager.currentStepIndex !== index) return + if (this.stepManager.currentStepIndex !== index) return - if (!this.storageResolver) { - return - } - if (this.decodeTimeout) { - window.clearTimeout(this.decodeTimeout) - } - this.event.trigger('solidityStateUpdating') - this.decodeTimeout = setTimeout(() => { - // necessary due to some states that can crash the debugger - try { - this.decode(index) - } catch (err) { - console.dir(err) - } - }, 1000) + if (!this.storageResolver) { + return } - - reset () { - this.stateVariablesByAddresses = {} + if (this.decodeTimeout) { + window.clearTimeout(this.decodeTimeout) } + this.event.trigger('solidityStateUpdating') + this.decodeTimeout = setTimeout(() => { + // necessary due to some states that can crash the debugger + try { + this.decode(index) + } catch (err) { + console.dir(err) + } + }, 1000) + } - decode (index) { - try { - const address = this.traceManager.getCurrentCalledAddressAt(this.stepManager.currentStepIndex) - if (this.stateVariablesByAddresses[address]) { - return this.extractStateVariables(this.stateVariablesByAddresses[address], address) - } - this.solidityProxy.extractStateVariablesAt(index).then((stateVars) => { - this.stateVariablesByAddresses[address] = stateVars - this.extractStateVariables(stateVars, address) - }).catch((_error) => { - this.event.trigger('solidityState', [{}]) - }) - } catch (error) { - return this.event.trigger('solidityState', [{}]) - } - } + reset () { + this.stateVariablesByAddresses = {} + } - extractStateVariables (stateVars, address) { - const storageViewer = new StorageViewer({ stepIndex: this.stepManager.currentStepIndex, tx: this.tx, address: address }, this.storageResolver, this.traceManager) - decodeState(stateVars, storageViewer).then((result) => { - this.event.trigger('solidityStateMessage', ['']) - if (result['error']) { - return this.event.trigger('solidityStateMessage', [result['error']]) - } - this.event.trigger('solidityState', [result]) - }) + decode (index) { + try { + const address = this.traceManager.getCurrentCalledAddressAt(this.stepManager.currentStepIndex) + if (this.stateVariablesByAddresses[address]) { + return this.extractStateVariables(this.stateVariablesByAddresses[address], address) + } + this.solidityProxy.extractStateVariablesAt(index).then((stateVars) => { + this.stateVariablesByAddresses[address] = stateVars + this.extractStateVariables(stateVars, address) + }).catch((_error) => { + this.event.trigger('solidityState', [{}]) + }) + } catch (error) { + return this.event.trigger('solidityState', [{}]) } + } + + extractStateVariables (stateVars, address) { + const storageViewer = new StorageViewer({ stepIndex: this.stepManager.currentStepIndex, tx: this.tx, address: address }, this.storageResolver, this.traceManager) + decodeState(stateVars, storageViewer).then((result) => { + this.event.trigger('solidityStateMessage', ['']) + if (result['error']) { + return this.event.trigger('solidityStateMessage', [result['error']]) + } + this.event.trigger('solidityState', [result]) + }) + } } diff --git a/libs/remix-debug/src/debugger/stepManager.ts b/libs/remix-debug/src/debugger/stepManager.ts index 8c0e6512d5..5de5327d97 100644 --- a/libs/remix-debug/src/debugger/stepManager.ts +++ b/libs/remix-debug/src/debugger/stepManager.ts @@ -2,241 +2,241 @@ import { util } from '@remix-project/remix-lib' import { EventManager } from '../eventManager' export class DebuggerStepManager { - event - debugger - traceManager - currentStepIndex: number - traceLength: number - codeTraceLength: number - revertionPoint - currentCall - - constructor (_debugger, traceManager) { - this.event = new EventManager() - this.debugger = _debugger - this.traceManager = traceManager - this.currentStepIndex = -1 - this.traceLength = 0 - this.codeTraceLength = 0 - this.revertionPoint = null - - this.listenToEvents() - } - - listenToEvents () { - this.debugger.event.register('newTraceLoaded', this, () => { - this.traceManager.getLength((error, newLength) => { - if (error) { - return console.log(error) - } - if (this.traceLength !== newLength) { - this.event.trigger('traceLengthChanged', [newLength]) - this.traceLength = newLength - this.codeTraceLength = this.calculateCodeLength() - } - }) - }) - - this.debugger.callTree.event.register('callTreeBuildFailed', () => { - setTimeout(() => { - this.jumpTo(0) - }, 500) - }) - - this.debugger.callTree.event.register('callTreeNotReady', () => { - setTimeout(() => { - this.jumpTo(0) - }, 500) - }) - - this.debugger.callTree.event.register('noCallTreeAvailable', () => { - setTimeout(() => { - this.jumpTo(0) - }, 500) - }) - - this.debugger.callTree.event.register('callTreeReady', () => { - if (this.debugger.callTree.functionCallStack.length) { - setTimeout(() => { - this.jumpTo(this.debugger.callTree.functionCallStack[0]) - }, 500) - } else { - setTimeout(() => { - this.jumpTo(0) - }, 500) - } - }) - - this.event.register('indexChanged', this, (index) => { - if (index < 0) return - if (this.currentStepIndex !== index) return - - this.traceManager.buildCallPath(index).then((callsPath) => { - this.currentCall = callsPath[callsPath.length - 1] - if (this.currentCall.reverted) { - const revertedReason = this.currentCall.outofgas ? 'outofgas' : 'reverted' - this.revertionPoint = this.currentCall.return - this.event.trigger('revertWarning', [revertedReason]) - return - } - for (let k = callsPath.length - 2; k >= 0; k--) { - const parent = callsPath[k] - if (parent.reverted) { - this.revertionPoint = parent.return - this.event.trigger('revertWarning', ['parenthasthrown']) - return - } - } - this.event.trigger('revertWarning', ['']) - }).catch((error) => { - console.log(error) - this.event.trigger('revertWarning', ['']) - }) - }) - } - - triggerStepChanged (step) { - this.traceManager.getLength((error, length) => { - let stepState = 'valid' - - if (error) { - stepState = 'invalid' - } else if (step <= 0) { - stepState = 'initial' - } else if (step >= length - 1) { - stepState = 'end' - } - - const jumpOutDisabled = (step === this.traceManager.findStepOut(step)) - this.event.trigger('stepChanged', [step, stepState, jumpOutDisabled]) - }) - } - - stepIntoBack (solidityMode) { - if (!this.traceManager.isLoaded()) return - let step = this.currentStepIndex - 1 - this.currentStepIndex = step - if (solidityMode) { - step = this.resolveToReducedTrace(step, -1) + event + debugger + traceManager + currentStepIndex: number + traceLength: number + codeTraceLength: number + revertionPoint + currentCall + + constructor (_debugger, traceManager) { + this.event = new EventManager() + this.debugger = _debugger + this.traceManager = traceManager + this.currentStepIndex = -1 + this.traceLength = 0 + this.codeTraceLength = 0 + this.revertionPoint = null + + this.listenToEvents() + } + + listenToEvents () { + this.debugger.event.register('newTraceLoaded', this, () => { + this.traceManager.getLength((error, newLength) => { + if (error) { + return console.log(error) } - if (!this.traceManager.inRange(step)) { - return + if (this.traceLength !== newLength) { + this.event.trigger('traceLengthChanged', [newLength]) + this.traceLength = newLength + this.codeTraceLength = this.calculateCodeLength() } - this.triggerStepChanged(step) - } - - stepIntoForward (solidityMode) { - if (!this.traceManager.isLoaded()) return - let step = this.currentStepIndex + 1 - this.currentStepIndex = step - if (solidityMode) { - step = this.resolveToReducedTrace(step, 1) + }) + }) + + this.debugger.callTree.event.register('callTreeBuildFailed', () => { + setTimeout(() => { + this.jumpTo(0) + }, 500) + }) + + this.debugger.callTree.event.register('callTreeNotReady', () => { + setTimeout(() => { + this.jumpTo(0) + }, 500) + }) + + this.debugger.callTree.event.register('noCallTreeAvailable', () => { + setTimeout(() => { + this.jumpTo(0) + }, 500) + }) + + this.debugger.callTree.event.register('callTreeReady', () => { + if (this.debugger.callTree.functionCallStack.length) { + setTimeout(() => { + this.jumpTo(this.debugger.callTree.functionCallStack[0]) + }, 500) + } else { + setTimeout(() => { + this.jumpTo(0) + }, 500) + } + }) + + this.event.register('indexChanged', this, (index) => { + if (index < 0) return + if (this.currentStepIndex !== index) return + + this.traceManager.buildCallPath(index).then((callsPath) => { + this.currentCall = callsPath[callsPath.length - 1] + if (this.currentCall.reverted) { + const revertedReason = this.currentCall.outofgas ? 'outofgas' : 'reverted' + this.revertionPoint = this.currentCall.return + this.event.trigger('revertWarning', [revertedReason]) + return } - if (!this.traceManager.inRange(step)) { + for (let k = callsPath.length - 2; k >= 0; k--) { + const parent = callsPath[k] + if (parent.reverted) { + this.revertionPoint = parent.return + this.event.trigger('revertWarning', ['parenthasthrown']) return + } } - this.triggerStepChanged(step) - } - - stepOverBack (solidityMode) { - if (!this.traceManager.isLoaded()) return - let step = this.traceManager.findStepOverBack(this.currentStepIndex) - if (solidityMode) { - step = this.resolveToReducedTrace(step, -1) - } - if (this.currentStepIndex === step) return - this.currentStepIndex = step - this.triggerStepChanged(step) - } - - stepOverForward (solidityMode) { - if (!this.traceManager.isLoaded()) return - if (this.currentStepIndex >= this.traceLength - 1) return - let step = this.currentStepIndex + 1 - const scope = this.debugger.callTree.findScope(step) - if (scope && scope.firstStep === step) { - step = scope.lastStep + 1 - } - if (solidityMode) { - step = this.resolveToReducedTrace(step, 1) - } - if (this.currentStepIndex === step) return - this.currentStepIndex = step - this.triggerStepChanged(step) - } - - jumpOut (solidityMode) { - if (!this.traceManager.isLoaded()) return - let step = this.traceManager.findStepOut(this.currentStepIndex) - if (solidityMode) { - step = this.resolveToReducedTrace(step, 0) - } - if (this.currentStepIndex === step) return - this.currentStepIndex = step - this.triggerStepChanged(step) - } - - jumpTo (step) { - if (!this.traceManager.inRange(step)) return - if (this.currentStepIndex === step) return - this.currentStepIndex = step - this.triggerStepChanged(step) - } - - jumpToException () { - this.jumpTo(this.revertionPoint) - } - - jumpNextBreakpoint () { - this.debugger.breakpointManager.jumpNextBreakpoint(this.currentStepIndex, true) - } - - jumpPreviousBreakpoint () { - this.debugger.breakpointManager.jumpPreviousBreakpoint(this.currentStepIndex, true) - } - - calculateFirstStep () { - const step = this.resolveToReducedTrace(0, 1) - return this.resolveToReducedTrace(step, 1) - } - - calculateCodeStepList () { - let step = 0 - let steps = [] - while (step < this.traceLength) { - const _step = this.resolveToReducedTrace(step, 1) - if (!_step) break - steps.push(_step) - step += 1 - } - steps = steps.filter((item, pos, self) => { return steps.indexOf(item) === pos }) - return steps - } - - calculateCodeLength () { - this.calculateCodeStepList().reverse() - return this.calculateCodeStepList().reverse()[1] || this.traceLength - } - - nextStep () { - return this.resolveToReducedTrace(this.currentStepIndex, 1) - } - - previousStep () { - return this.resolveToReducedTrace(this.currentStepIndex, -1) - } - - resolveToReducedTrace (value, incr) { - if (!this.debugger.callTree.reducedTrace.length) { - return value - } - let nextSource = util.findClosestIndex(value, this.debugger.callTree.reducedTrace) - nextSource = nextSource + incr - if (nextSource <= 0) { - nextSource = 0 - } else if (nextSource > this.debugger.callTree.reducedTrace.length) { - nextSource = this.debugger.callTree.reducedTrace.length - 1 - } - return this.debugger.callTree.reducedTrace[nextSource] - } + this.event.trigger('revertWarning', ['']) + }).catch((error) => { + console.log(error) + this.event.trigger('revertWarning', ['']) + }) + }) + } + + triggerStepChanged (step) { + this.traceManager.getLength((error, length) => { + let stepState = 'valid' + + if (error) { + stepState = 'invalid' + } else if (step <= 0) { + stepState = 'initial' + } else if (step >= length - 1) { + stepState = 'end' + } + + const jumpOutDisabled = (step === this.traceManager.findStepOut(step)) + this.event.trigger('stepChanged', [step, stepState, jumpOutDisabled]) + }) + } + + stepIntoBack (solidityMode) { + if (!this.traceManager.isLoaded()) return + let step = this.currentStepIndex - 1 + this.currentStepIndex = step + if (solidityMode) { + step = this.resolveToReducedTrace(step, -1) + } + if (!this.traceManager.inRange(step)) { + return + } + this.triggerStepChanged(step) + } + + stepIntoForward (solidityMode) { + if (!this.traceManager.isLoaded()) return + let step = this.currentStepIndex + 1 + this.currentStepIndex = step + if (solidityMode) { + step = this.resolveToReducedTrace(step, 1) + } + if (!this.traceManager.inRange(step)) { + return + } + this.triggerStepChanged(step) + } + + stepOverBack (solidityMode) { + if (!this.traceManager.isLoaded()) return + let step = this.traceManager.findStepOverBack(this.currentStepIndex) + if (solidityMode) { + step = this.resolveToReducedTrace(step, -1) + } + if (this.currentStepIndex === step) return + this.currentStepIndex = step + this.triggerStepChanged(step) + } + + stepOverForward (solidityMode) { + if (!this.traceManager.isLoaded()) return + if (this.currentStepIndex >= this.traceLength - 1) return + let step = this.currentStepIndex + 1 + const scope = this.debugger.callTree.findScope(step) + if (scope && scope.firstStep === step) { + step = scope.lastStep + 1 + } + if (solidityMode) { + step = this.resolveToReducedTrace(step, 1) + } + if (this.currentStepIndex === step) return + this.currentStepIndex = step + this.triggerStepChanged(step) + } + + jumpOut (solidityMode) { + if (!this.traceManager.isLoaded()) return + let step = this.traceManager.findStepOut(this.currentStepIndex) + if (solidityMode) { + step = this.resolveToReducedTrace(step, 0) + } + if (this.currentStepIndex === step) return + this.currentStepIndex = step + this.triggerStepChanged(step) + } + + jumpTo (step) { + if (!this.traceManager.inRange(step)) return + if (this.currentStepIndex === step) return + this.currentStepIndex = step + this.triggerStepChanged(step) + } + + jumpToException () { + this.jumpTo(this.revertionPoint) + } + + jumpNextBreakpoint () { + this.debugger.breakpointManager.jumpNextBreakpoint(this.currentStepIndex, true) + } + + jumpPreviousBreakpoint () { + this.debugger.breakpointManager.jumpPreviousBreakpoint(this.currentStepIndex, true) + } + + calculateFirstStep () { + const step = this.resolveToReducedTrace(0, 1) + return this.resolveToReducedTrace(step, 1) + } + + calculateCodeStepList () { + let step = 0 + let steps = [] + while (step < this.traceLength) { + const _step = this.resolveToReducedTrace(step, 1) + if (!_step) break + steps.push(_step) + step += 1 + } + steps = steps.filter((item, pos, self) => { return steps.indexOf(item) === pos }) + return steps + } + + calculateCodeLength () { + this.calculateCodeStepList().reverse() + return this.calculateCodeStepList().reverse()[1] || this.traceLength + } + + nextStep () { + return this.resolveToReducedTrace(this.currentStepIndex, 1) + } + + previousStep () { + return this.resolveToReducedTrace(this.currentStepIndex, -1) + } + + resolveToReducedTrace (value, incr) { + if (!this.debugger.callTree.reducedTrace.length) { + return value + } + let nextSource = util.findClosestIndex(value, this.debugger.callTree.reducedTrace) + nextSource = nextSource + incr + if (nextSource <= 0) { + nextSource = 0 + } else if (nextSource > this.debugger.callTree.reducedTrace.length) { + nextSource = this.debugger.callTree.reducedTrace.length - 1 + } + return this.debugger.callTree.reducedTrace[nextSource] + } } diff --git a/libs/remix-debug/src/eventManager.ts b/libs/remix-debug/src/eventManager.ts index f878395ed3..d01d48e9b5 100644 --- a/libs/remix-debug/src/eventManager.ts +++ b/libs/remix-debug/src/eventManager.ts @@ -1,15 +1,15 @@ 'use strict' export class EventManager { - registered - anonymous + registered + anonymous - constructor () { - this.registered = {} - this.anonymous = {} - } + constructor () { + this.registered = {} + this.anonymous = {} + } - /* + /* * Unregister a listener. * Note that if obj is a function. the unregistration will be applied to the dummy obj {}. * @@ -17,22 +17,22 @@ export class EventManager { * @param {Object or Func} obj - object that will listen on this event * @param {Func} func - function of the listeners that will be executed */ - unregister (eventName, obj, func) { - if (!this.registered[eventName]) { - return - } - if (obj instanceof Function) { - func = obj - obj = this.anonymous - } - for (const reg in this.registered[eventName]) { - if (this.registered[eventName][reg].obj === obj && this.registered[eventName][reg].func === func) { - this.registered[eventName].splice(reg, 1) - } - } + unregister (eventName, obj, func) { + if (!this.registered[eventName]) { + return + } + if (obj instanceof Function) { + func = obj + obj = this.anonymous + } + for (const reg in this.registered[eventName]) { + if (this.registered[eventName][reg].obj === obj && this.registered[eventName][reg].func === func) { + this.registered[eventName].splice(reg, 1) + } } + } - /* + /* * Register a new listener. * Note that if obj is a function, the function registration will be associated with the dummy object {} * @@ -40,34 +40,34 @@ export class EventManager { * @param {Object or Func} obj - object that will listen on this event * @param {Func} func - function of the listeners that will be executed */ - register (eventName, obj, func) { - if (!this.registered[eventName]) { - this.registered[eventName] = [] - } - if (obj instanceof Function) { - func = obj - obj = this.anonymous - } - this.registered[eventName].push({ - obj: obj, - func: func - }) + register (eventName, obj, func) { + if (!this.registered[eventName]) { + this.registered[eventName] = [] } + if (obj instanceof Function) { + func = obj + obj = this.anonymous + } + this.registered[eventName].push({ + obj: obj, + func: func + }) + } - /* + /* * trigger event. * Every listener have their associated function executed * * @param {String} eventName - the event name * @param {Array}j - argument that will be passed to the executed function. */ - trigger (eventName, args) { - if (!this.registered[eventName]) { - return - } - for (const listener in this.registered[eventName]) { - const l = this.registered[eventName][listener] - if (l.func) l.func.apply(l.obj === this.anonymous ? {} : l.obj, args) - } + trigger (eventName, args) { + if (!this.registered[eventName]) { + return + } + for (const listener in this.registered[eventName]) { + const l = this.registered[eventName][listener] + if (l.func) l.func.apply(l.obj === this.anonymous ? {} : l.obj, args) } + } } diff --git a/libs/remix-debug/src/index.ts b/libs/remix-debug/src/index.ts index 855b53f83e..8ac8dafd54 100644 --- a/libs/remix-debug/src/index.ts +++ b/libs/remix-debug/src/index.ts @@ -11,8 +11,8 @@ import * as sourceMappingDecoder from './source/sourceMappingDecoder' import * as traceHelper from './trace/traceHelper' const storage = { - StorageViewer: StorageViewer, - StorageResolver: StorageResolver + StorageViewer: StorageViewer, + StorageResolver: StorageResolver } /* Use of breakPointManager : @@ -23,19 +23,19 @@ const storage = { this.debugger.setBreakpointManager(breakPointManager) */ export { - init, - traceHelper, - sourceMappingDecoder, - EthDebugger, - TransactionDebugger, - /** + init, + traceHelper, + sourceMappingDecoder, + EthDebugger, + TransactionDebugger, + /** * constructor * * @param {Object} _debugger - type of EthDebugger * @return {Function} _locationToRowConverter - function implemented by editor which return a column/line position for a char source location */ - BreakpointManager, - SolidityDecoder, - storage, - CmdLine + BreakpointManager, + SolidityDecoder, + storage, + CmdLine } \ No newline at end of file diff --git a/libs/remix-debug/src/init.ts b/libs/remix-debug/src/init.ts index 700c9f5822..b829dc477b 100644 --- a/libs/remix-debug/src/init.ts +++ b/libs/remix-debug/src/init.ts @@ -2,72 +2,72 @@ import Web3 from 'web3' export function loadWeb3 (url) { - if (!url) url = 'http://localhost:8545' - const web3 = new Web3() - web3.setProvider(new Web3.providers.HttpProvider(url)) - extend(web3) - return web3 + if (!url) url = 'http://localhost:8545' + const web3 = new Web3() + web3.setProvider(new Web3.providers.HttpProvider(url)) + extend(web3) + return web3 } export function extendWeb3 (web3) { - extend(web3) + extend(web3) } export function setProvider (web3, url) { - web3.setProvider(new web3.providers.HttpProvider(url)) + web3.setProvider(new web3.providers.HttpProvider(url)) } export function web3DebugNode (network) { - const web3DebugNodes = { - Main: 'https://rpc.archivenode.io/e50zmkroshle2e2e50zm0044i7ao04ym', - Rinkeby: 'https://remix-rinkeby.ethdevops.io', - Ropsten: 'https://remix-ropsten.ethdevops.io', - Goerli: 'https://remix-goerli.ethdevops.io', - Sepolia: 'https://remix-sepolia.ethdevops.io' - } - if (web3DebugNodes[network]) { - return loadWeb3(web3DebugNodes[network]) - } - return null + const web3DebugNodes = { + Main: 'https://rpc.archivenode.io/e50zmkroshle2e2e50zm0044i7ao04ym', + Rinkeby: 'https://remix-rinkeby.ethdevops.io', + Ropsten: 'https://remix-ropsten.ethdevops.io', + Goerli: 'https://remix-goerli.ethdevops.io', + Sepolia: 'https://remix-sepolia.ethdevops.io' + } + if (web3DebugNodes[network]) { + return loadWeb3(web3DebugNodes[network]) + } + return null } export function extend (web3) { - if (!web3.extend) { - return - } - // DEBUG - const methods = [] - if (!(web3.debug && web3.debug.preimage)) { - methods.push(new web3.extend.Method({ - name: 'preimage', - call: 'debug_preimage', - inputFormatter: [null], - params: 1 - })) - } + if (!web3.extend) { + return + } + // DEBUG + const methods = [] + if (!(web3.debug && web3.debug.preimage)) { + methods.push(new web3.extend.Method({ + name: 'preimage', + call: 'debug_preimage', + inputFormatter: [null], + params: 1 + })) + } - if (!(web3.debug && web3.debug.traceTransaction)) { - methods.push(new web3.extend.Method({ - name: 'traceTransaction', - call: 'debug_traceTransaction', - inputFormatter: [null, null], - params: 2 - })) - } + if (!(web3.debug && web3.debug.traceTransaction)) { + methods.push(new web3.extend.Method({ + name: 'traceTransaction', + call: 'debug_traceTransaction', + inputFormatter: [null, null], + params: 2 + })) + } - if (!(web3.debug && web3.debug.storageRangeAt)) { - methods.push(new web3.extend.Method({ - name: 'storageRangeAt', - call: 'debug_storageRangeAt', - inputFormatter: [null, null, null, null, null], - params: 5 - })) - } - if (methods.length > 0) { - web3.extend({ - property: 'debug', - methods: methods, - properties: [] - }) - } + if (!(web3.debug && web3.debug.storageRangeAt)) { + methods.push(new web3.extend.Method({ + name: 'storageRangeAt', + call: 'debug_storageRangeAt', + inputFormatter: [null, null, null, null, null], + params: 5 + })) + } + if (methods.length > 0) { + web3.extend({ + property: 'debug', + methods: methods, + properties: [] + }) + } } diff --git a/libs/remix-debug/src/solidity-decoder/astHelper.ts b/libs/remix-debug/src/solidity-decoder/astHelper.ts index d3ecfa0c16..d072684475 100644 --- a/libs/remix-debug/src/solidity-decoder/astHelper.ts +++ b/libs/remix-debug/src/solidity-decoder/astHelper.ts @@ -8,22 +8,22 @@ import { AstWalker } from '@remix-project/remix-astwalker' * @return {Object} - returns a mapping from AST node ids to AST nodes for the contracts */ export function extractContractDefinitions (sourcesList) { - const ret = { - contractsById: {}, - contractsByName: {}, - sourcesByContract: {} - } - const walker = new AstWalker() - for (const k in sourcesList) { - walker.walkFull(sourcesList[k].ast, (node) => { - if (node.nodeType === 'ContractDefinition') { - ret.contractsById[node.id] = node - ret.sourcesByContract[node.id] = k - ret.contractsByName[k + ':' + node.name] = node - } - }) - } - return ret + const ret = { + contractsById: {}, + contractsByName: {}, + sourcesByContract: {} + } + const walker = new AstWalker() + for (const k in sourcesList) { + walker.walkFull(sourcesList[k].ast, (node) => { + if (node.nodeType === 'ContractDefinition') { + ret.contractsById[node.id] = node + ret.sourcesByContract[node.id] = k + ret.contractsByName[k + ':' + node.name] = node + } + }) + } + return ret } /** @@ -33,18 +33,18 @@ export function extractContractDefinitions (sourcesList) { * @return {Object} - returns a list of node */ export function extractOrphanDefinitions (sourcesList) { - const ret = [] - for (const k in sourcesList) { - const ast = sourcesList[k].ast - if (ast.nodes && ast.nodes.length) { - for (const node of ast.nodes) { - if (node.nodeType !== 'ContractDefinition') { - ret.push(node) - } - } + const ret = [] + for (const k in sourcesList) { + const ast = sourcesList[k].ast + if (ast.nodes && ast.nodes.length) { + for (const node of ast.nodes) { + if (node.nodeType !== 'ContractDefinition') { + ret.push(node) } + } } - return ret + } + return ret } /** @@ -55,7 +55,7 @@ export function extractOrphanDefinitions (sourcesList) { * @return {Array} - array of base contracts in derived to base order as AST nodes. */ export function getLinearizedBaseContracts (id, contractsById) { - return contractsById[id].linearizedBaseContracts.map(function (id) { return contractsById[id] }) + return contractsById[id].linearizedBaseContracts.map(function (id) { return contractsById[id] }) } /** @@ -68,28 +68,28 @@ export function getLinearizedBaseContracts (id, contractsById) { * stateVariables - list of all the variable declaration of the @arg contractName */ export function extractStateDefinitions (contractName, sourcesList, contracts) { - if (!contracts) { - contracts = extractContractDefinitions(sourcesList) + if (!contracts) { + contracts = extractContractDefinitions(sourcesList) + } + const node = contracts.contractsByName[contractName] + if (!node) { + return null + } + const stateItems = extractOrphanDefinitions(sourcesList) + const stateVar = [] + const baseContracts = getLinearizedBaseContracts(node.id, contracts.contractsById) + baseContracts.reverse() + for (const k in baseContracts) { + const ctr = baseContracts[k] + for (const i in ctr.nodes) { + const item = ctr.nodes[i] + stateItems.push(item) + if (item.nodeType === 'VariableDeclaration') { + stateVar.push(item) + } } - const node = contracts.contractsByName[contractName] - if (!node) { - return null - } - const stateItems = extractOrphanDefinitions(sourcesList) - const stateVar = [] - const baseContracts = getLinearizedBaseContracts(node.id, contracts.contractsById) - baseContracts.reverse() - for (const k in baseContracts) { - const ctr = baseContracts[k] - for (const i in ctr.nodes) { - const item = ctr.nodes[i] - stateItems.push(item) - if (item.nodeType === 'VariableDeclaration') { - stateVar.push(item) - } - } - } - return { stateDefinitions: stateItems, stateVariables: stateVar } + } + return { stateDefinitions: stateItems, stateVariables: stateVar } } /** @@ -100,17 +100,17 @@ export function extractStateDefinitions (contractName, sourcesList, contracts) { * @return {Object} - returns a mapping between contract name and contract state */ export function extractStatesDefinitions (sourcesList, contracts) { - if (!contracts) { - contracts = extractContractDefinitions(sourcesList) - } - const ret = {} - for (const contract in contracts.contractsById) { - const name = contracts.contractsById[contract].name - const source = contracts.sourcesByContract[contract] - const fullName = source + ':' + name - const state = extractStateDefinitions(fullName, sourcesList, contracts) - ret[fullName] = state - ret[name] = state // solc < 0.4.9 - } - return ret + if (!contracts) { + contracts = extractContractDefinitions(sourcesList) + } + const ret = {} + for (const contract in contracts.contractsById) { + const name = contracts.contractsById[contract].name + const source = contracts.sourcesByContract[contract] + const fullName = source + ':' + name + const state = extractStateDefinitions(fullName, sourcesList, contracts) + ret[fullName] = state + ret[name] = state // solc < 0.4.9 + } + return ret } diff --git a/libs/remix-debug/src/solidity-decoder/decodeInfo.ts b/libs/remix-debug/src/solidity-decoder/decodeInfo.ts index 23945164b4..74098b0277 100644 --- a/libs/remix-debug/src/solidity-decoder/decodeInfo.ts +++ b/libs/remix-debug/src/solidity-decoder/decodeInfo.ts @@ -21,18 +21,18 @@ import { extractLocation, removeLocation } from './types/util' * @return {Object} returns decoded info about the current type: { storageBytes, typeName} */ function mapping (type, stateDefinitions, contractName) { - const match = type.match(/mapping\((.*?)=>(.*)\)$/) - const keyTypeName = match[1].trim() - const valueTypeName = match[2].trim() + const match = type.match(/mapping\((.*?)=>(.*)\)$/) + const keyTypeName = match[1].trim() + const valueTypeName = match[2].trim() - const keyType = parseType(keyTypeName, stateDefinitions, contractName, 'storage') - const valueType = parseType(valueTypeName, stateDefinitions, contractName, 'storage') + const keyType = parseType(keyTypeName, stateDefinitions, contractName, 'storage') + const valueType = parseType(valueTypeName, stateDefinitions, contractName, 'storage') - const underlyingTypes = { - keyType: keyType, - valueType: valueType - } - return new MappingType(underlyingTypes, 'location', removeLocation(type)) + const underlyingTypes = { + keyType: keyType, + valueType: valueType + } + return new MappingType(underlyingTypes, 'location', removeLocation(type)) } /** @@ -42,9 +42,9 @@ function mapping (type, stateDefinitions, contractName) { * @return {Object} returns decoded info about the current type: { storageBytes, typeName} */ function uint (type) { - type = type === 'uint' ? 'uint256' : type - const storageBytes = parseInt(type.replace('uint', '')) / 8 - return new UintType(storageBytes) + type = type === 'uint' ? 'uint256' : type + const storageBytes = parseInt(type.replace('uint', '')) / 8 + return new UintType(storageBytes) } /** @@ -54,9 +54,9 @@ function uint (type) { * @return {Object} returns decoded info about the current type: { storageBytes, typeName} */ function int (type) { - type = type === 'int' ? 'int256' : type - const storageBytes = parseInt(type.replace('int', '')) / 8 - return new IntType(storageBytes) + type = type === 'int' ? 'int256' : type + const storageBytes = parseInt(type.replace('int', '')) / 8 + return new IntType(storageBytes) } /** @@ -66,7 +66,7 @@ function int (type) { * @return {Object} returns decoded info about the current type: { storageBytes, typeName} */ function address (type) { - return new AddressType() + return new AddressType() } /** @@ -76,11 +76,11 @@ function address (type) { * @return {Object} returns decoded info about the current type: { storageBytes, typeName} */ function bool (type) { - return new BoolType() + return new BoolType() } function functionType (type, stateDefinitions, contractName, location) { - return new FunctionType(type, stateDefinitions, contractName, location) + return new FunctionType(type, stateDefinitions, contractName, location) } /** @@ -93,14 +93,14 @@ function functionType (type, stateDefinitions, contractName, location) { * @return {Object} returns decoded info about the current type: { storageBytes, typeName} */ function dynamicByteArray (type, stateDefinitions, contractName, location) { - if (!location) { - location = extractLocation(type) - } - if (location) { - return new BytesType(location) - } else { - return null - } + if (!location) { + location = extractLocation(type) + } + if (location) { + return new BytesType(location) + } else { + return null + } } /** @@ -110,8 +110,8 @@ function dynamicByteArray (type, stateDefinitions, contractName, location) { * @return {Object} returns decoded info about the current type: { storageBytes, typeName} */ function fixedByteArray (type) { - const storageBytes = parseInt(type.replace('bytes', '')) - return new BytesXType(storageBytes) + const storageBytes = parseInt(type.replace('bytes', '')) + return new BytesXType(storageBytes) } /** @@ -124,14 +124,14 @@ function fixedByteArray (type) { * @return {Object} returns decoded info about the current type: { storageBytes, typeName} */ function stringType (type, stateDefinitions, contractName, location) { - if (!location) { - location = extractLocation(type) - } - if (location) { - return new StringType(location) - } else { - return null - } + if (!location) { + location = extractLocation(type) + } + if (location) { + return new StringType(location) + } else { + return null + } } /** @@ -144,21 +144,21 @@ function stringType (type, stateDefinitions, contractName, location) { * @return {Object} returns decoded info about the current type: { storageBytes, typeName, arraySize, subArray} */ function array (type, stateDefinitions, contractName, location) { - const match = type.match(/(.*)\[(.*?)\]( storage ref| storage pointer| memory| calldata)?$/) - if (!match) { - console.log('unable to parse type ' + type) - return null - } - if (!location) { - location = match[3].trim() - } - const arraySize = match[2] === '' ? 'dynamic' : parseInt(match[2]) - const underlyingType = parseType(match[1], stateDefinitions, contractName, location) - if (underlyingType === null) { - console.log('unable to parse type ' + type) - return null - } - return new ArrayType(underlyingType, arraySize, location) + const match = type.match(/(.*)\[(.*?)\]( storage ref| storage pointer| memory| calldata)?$/) + if (!match) { + console.log('unable to parse type ' + type) + return null + } + if (!location) { + location = match[3].trim() + } + const arraySize = match[2] === '' ? 'dynamic' : parseInt(match[2]) + const underlyingType = parseType(match[1], stateDefinitions, contractName, location) + if (underlyingType === null) { + console.log('unable to parse type ' + type) + return null + } + return new ArrayType(underlyingType, arraySize, location) } /** @@ -170,13 +170,13 @@ function array (type, stateDefinitions, contractName, location) { * @return {Object} returns decoded info about the current type: { storageBytes, typeName, enum} */ function enumType (type, stateDefinitions, contractName) { - const match = type.match(/enum (.*)/) - const enumDef = getEnum(match[1], stateDefinitions, contractName) - if (enumDef === null) { - console.log('unable to retrieve decode info of ' + type) - return null - } - return new EnumType(enumDef) + const match = type.match(/enum (.*)/) + const enumDef = getEnum(match[1], stateDefinitions, contractName) + if (enumDef === null) { + console.log('unable to retrieve decode info of ' + type) + return null + } + return new EnumType(enumDef) } /** @@ -189,17 +189,17 @@ function enumType (type, stateDefinitions, contractName) { * @return {Object} returns decoded info about the current type: { storageBytes, typeName, members} */ function struct (type, stateDefinitions, contractName, location) { - const match = type.match(/struct (\S*?)( storage ref| storage pointer| memory| calldata)?$/) - if (match) { - if (!location) { - location = match[2].trim() - } - const memberDetails = getStructMembers(match[1], stateDefinitions, contractName, location) // type is used to extract the ast struct definition - if (!memberDetails) return null - return new StructType(memberDetails, location, match[1]) - } else { - return null + const match = type.match(/struct (\S*?)( storage ref| storage pointer| memory| calldata)?$/) + if (match) { + if (!location) { + location = match[2].trim() } + const memberDetails = getStructMembers(match[1], stateDefinitions, contractName, location) // type is used to extract the ast struct definition + if (!memberDetails) return null + return new StructType(memberDetails, location, match[1]) + } else { + return null + } } /** @@ -211,21 +211,21 @@ function struct (type, stateDefinitions, contractName, location) { * @return {Array} - containing all value declaration of the current enum type */ function getEnum (type, stateDefinitions, contractName) { - const split = type.split('.') - if (!split.length) { - type = contractName + '.' + type - } else { - contractName = split[0] - } - const state = stateDefinitions[contractName] - if (state) { - for (const dec of state.stateDefinitions) { - if (dec && dec.name && type === contractName + '.' + dec.name) { - return dec - } - } + const split = type.split('.') + if (!split.length) { + type = contractName + '.' + type + } else { + contractName = split[0] + } + const state = stateDefinitions[contractName] + if (state) { + for (const dec of state.stateDefinitions) { + if (dec && dec.name && type === contractName + '.' + dec.name) { + return dec + } } - return null + } + return null } /** @@ -238,26 +238,26 @@ function getEnum (type, stateDefinitions, contractName) { * @return {Array} containing all members of the current struct type */ function getStructMembers (type, stateDefinitions, contractName, location) { - if (type.indexOf('.') === -1) { - type = contractName + '.' + type - } - contractName = type.split('.')[0] - const state = stateDefinitions[contractName] - if (state) { - for (const dec of state.stateDefinitions) { - if (dec.nodeType === 'StructDefinition' && type === contractName + '.' + dec.name) { - const offsets = computeOffsets(dec.members, stateDefinitions, contractName, location) - if (!offsets) { - return null - } - return { - members: offsets.typesOffsets, - storageSlots: offsets.endLocation.slot - } - } + if (type.indexOf('.') === -1) { + type = contractName + '.' + type + } + contractName = type.split('.')[0] + const state = stateDefinitions[contractName] + if (state) { + for (const dec of state.stateDefinitions) { + if (dec.nodeType === 'StructDefinition' && type === contractName + '.' + dec.name) { + const offsets = computeOffsets(dec.members, stateDefinitions, contractName, location) + if (!offsets) { + return null } + return { + members: offsets.typesOffsets, + storageSlots: offsets.endLocation.slot + } + } } - return null + } + return null } /** @@ -267,18 +267,18 @@ function getStructMembers (type, stateDefinitions, contractName, location) { * @return {String} returns the token type (used to instanciate the right decoder) (uint[2] storage ref[2] will return 'array', uint256 will return uintX) */ function typeClass (fullType) { - fullType = removeLocation(fullType) - if (fullType.lastIndexOf(']') === fullType.length - 1) { - return 'array' - } - if (fullType.indexOf('mapping') === 0) { - return 'mapping' - } - if (fullType.indexOf(' ') !== -1) { - fullType = fullType.split(' ')[0] - } - const char = fullType.indexOf('bytes') === 0 ? 'X' : '' - return fullType.replace(/[0-9]+/g, char) + fullType = removeLocation(fullType) + if (fullType.lastIndexOf(']') === fullType.length - 1) { + return 'array' + } + if (fullType.indexOf('mapping') === 0) { + return 'mapping' + } + if (fullType.indexOf(' ') !== -1) { + fullType = fullType.split(' ')[0] + } + const char = fullType.indexOf('bytes') === 0 ? 'X' : '' + return fullType.replace(/[0-9]+/g, char) } /** @@ -291,31 +291,31 @@ function typeClass (fullType) { * @return {Object} - return the corresponding decoder or null on error */ function parseType (type, stateDefinitions, contractName, location) { - const decodeInfos = { - contract: address, - address: address, - array: array, - bool: bool, - bytes: dynamicByteArray, - bytesX: fixedByteArray, - enum: enumType, - string: stringType, - struct: struct, - int: int, - uint: uint, - mapping: mapping, - function: functionType - } - const currentType = typeClass(type) - if (currentType === null) { - console.log('unable to retrieve decode info of ' + type) - return null - } - if (decodeInfos[currentType]) { - return decodeInfos[currentType](type, stateDefinitions, contractName, location) - } else { - return null - } + const decodeInfos = { + contract: address, + address: address, + array: array, + bool: bool, + bytes: dynamicByteArray, + bytesX: fixedByteArray, + enum: enumType, + string: stringType, + struct: struct, + int: int, + uint: uint, + mapping: mapping, + function: functionType + } + const currentType = typeClass(type) + if (currentType === null) { + console.log('unable to retrieve decode info of ' + type) + return null + } + if (decodeInfos[currentType]) { + return decodeInfos[currentType](type, stateDefinitions, contractName, location) + } else { + return null + } } /** @@ -328,64 +328,64 @@ function parseType (type, stateDefinitions, contractName, location) { * @return {Array} - return an array of types item: {name, type, location}. location defines the byte offset and slot offset */ function computeOffsets (types, stateDefinitions, contractName, location) { - const ret = [] - const storagelocation = { - offset: 0, - slot: 0 - } - for (const i in types) { - const variable = types[i] - const type = parseType(variable.typeDescriptions.typeString, stateDefinitions, contractName, location) - if (!type) { - console.log('unable to retrieve decode info of ' + variable.typeDescriptions.typeString) - return null - } - const immutable = variable.mutability === 'immutable' - const hasStorageSlots = !immutable && !variable.constant - if (hasStorageSlots && storagelocation.offset + type.storageBytes > 32) { - storagelocation.slot++ - storagelocation.offset = 0 - } - ret.push({ - name: variable.name, - type: type, - constant: variable.constant, - immutable, - storagelocation: { - offset: !hasStorageSlots ? 0 : storagelocation.offset, - slot: !hasStorageSlots ? 0 : storagelocation.slot - }, - variable - }) - if (hasStorageSlots) { - if (type.storageSlots === 1 && storagelocation.offset + type.storageBytes <= 32) { - storagelocation.offset += type.storageBytes - } else { - storagelocation.slot += type.storageSlots - storagelocation.offset = 0 - } - } + const ret = [] + const storagelocation = { + offset: 0, + slot: 0 + } + for (const i in types) { + const variable = types[i] + const type = parseType(variable.typeDescriptions.typeString, stateDefinitions, contractName, location) + if (!type) { + console.log('unable to retrieve decode info of ' + variable.typeDescriptions.typeString) + return null } - if (storagelocation.offset > 0) { - storagelocation.slot++ + const immutable = variable.mutability === 'immutable' + const hasStorageSlots = !immutable && !variable.constant + if (hasStorageSlots && storagelocation.offset + type.storageBytes > 32) { + storagelocation.slot++ + storagelocation.offset = 0 } - return { - typesOffsets: ret, - endLocation: storagelocation + ret.push({ + name: variable.name, + type: type, + constant: variable.constant, + immutable, + storagelocation: { + offset: !hasStorageSlots ? 0 : storagelocation.offset, + slot: !hasStorageSlots ? 0 : storagelocation.slot + }, + variable + }) + if (hasStorageSlots) { + if (type.storageSlots === 1 && storagelocation.offset + type.storageBytes <= 32) { + storagelocation.offset += type.storageBytes + } else { + storagelocation.slot += type.storageSlots + storagelocation.offset = 0 + } } + } + if (storagelocation.offset > 0) { + storagelocation.slot++ + } + return { + typesOffsets: ret, + endLocation: storagelocation + } } export { - parseType, - computeOffsets, - uint as Uint, - address as Address, - bool as Bool, - dynamicByteArray as DynamicByteArray, - fixedByteArray as FixedByteArray, - int as Int, - stringType as String, - array as Array, - enumType as Enum, - struct as Struct + parseType, + computeOffsets, + uint as Uint, + address as Address, + bool as Bool, + dynamicByteArray as DynamicByteArray, + fixedByteArray as FixedByteArray, + int as Int, + stringType as String, + array as Array, + enumType as Enum, + struct as Struct } diff --git a/libs/remix-debug/src/solidity-decoder/internalCallTree.ts b/libs/remix-debug/src/solidity-decoder/internalCallTree.ts index 4233ec9cbb..49a8d66bb7 100644 --- a/libs/remix-debug/src/solidity-decoder/internalCallTree.ts +++ b/libs/remix-debug/src/solidity-decoder/internalCallTree.ts @@ -23,34 +23,34 @@ export type StepDetail = { * Triggers `callTreeBuildFailed` event when tree fails to build */ export class InternalCallTree { - includeLocalVariables - debugWithGeneratedSources - event - solidityProxy - traceManager - sourceLocationTracker - scopes - scopeStarts - functionCallStack - functionDefinitionsByScope - variableDeclarationByFile - functionDefinitionByFile - astWalker - reducedTrace - locationAndOpcodePerVMTraceIndex: { + includeLocalVariables + debugWithGeneratedSources + event + solidityProxy + traceManager + sourceLocationTracker + scopes + scopeStarts + functionCallStack + functionDefinitionsByScope + variableDeclarationByFile + functionDefinitionByFile + astWalker + reducedTrace + locationAndOpcodePerVMTraceIndex: { [Key: number]: any } - gasCostPerLine - offsetToLineColumnConverter - pendingConstructorExecutionAt: number - pendingConstructorId: number - pendingConstructor - constructorsStartExecution - variables: { + gasCostPerLine + offsetToLineColumnConverter + pendingConstructorExecutionAt: number + pendingConstructorId: number + pendingConstructor + constructorsStartExecution + variables: { [Key: number]: any } - /** + /** * constructor * * @param {Object} debuggerEvent - event declared by the debugger (EthDebugger) @@ -59,466 +59,466 @@ export class InternalCallTree { * @param {Object} codeManager - code manager * @param {Object} opts - { includeLocalVariables, debugWithGeneratedSources } */ - constructor (debuggerEvent, traceManager, solidityProxy, codeManager, opts, offsetToLineColumnConverter?) { - this.includeLocalVariables = opts.includeLocalVariables - this.debugWithGeneratedSources = opts.debugWithGeneratedSources - this.event = new EventManager() - this.solidityProxy = solidityProxy - this.traceManager = traceManager - this.offsetToLineColumnConverter = offsetToLineColumnConverter - this.sourceLocationTracker = new SourceLocationTracker(codeManager, { debugWithGeneratedSources: opts.debugWithGeneratedSources }) - debuggerEvent.register('newTraceLoaded', async (trace) => { - const time = Date.now() - this.reset() - // each recursive call to buildTree represent a new context (either call, delegatecall, internal function) - const calledAddress = traceManager.getCurrentCalledAddressAt(0) - const isCreation = isContractCreation(calledAddress) + constructor (debuggerEvent, traceManager, solidityProxy, codeManager, opts, offsetToLineColumnConverter?) { + this.includeLocalVariables = opts.includeLocalVariables + this.debugWithGeneratedSources = opts.debugWithGeneratedSources + this.event = new EventManager() + this.solidityProxy = solidityProxy + this.traceManager = traceManager + this.offsetToLineColumnConverter = offsetToLineColumnConverter + this.sourceLocationTracker = new SourceLocationTracker(codeManager, { debugWithGeneratedSources: opts.debugWithGeneratedSources }) + debuggerEvent.register('newTraceLoaded', async (trace) => { + const time = Date.now() + this.reset() + // each recursive call to buildTree represent a new context (either call, delegatecall, internal function) + const calledAddress = traceManager.getCurrentCalledAddressAt(0) + const isCreation = isContractCreation(calledAddress) - const scopeId = '1' - this.scopeStarts[0] = scopeId - this.scopes[scopeId] = { firstStep: 0, locals: {}, isCreation, gasCost: 0 } + const scopeId = '1' + this.scopeStarts[0] = scopeId + this.scopes[scopeId] = { firstStep: 0, locals: {}, isCreation, gasCost: 0 } - const compResult = await this.solidityProxy.compilationResult(calledAddress) - if (!compResult) { - this.event.trigger('noCallTreeAvailable', []) + const compResult = await this.solidityProxy.compilationResult(calledAddress) + if (!compResult) { + this.event.trigger('noCallTreeAvailable', []) + } else { + try { + buildTree(this, 0, scopeId, isCreation).then((result) => { + if (result.error) { + this.event.trigger('callTreeBuildFailed', [result.error]) } else { - try { - buildTree(this, 0, scopeId, isCreation).then((result) => { - if (result.error) { - this.event.trigger('callTreeBuildFailed', [result.error]) - } else { - createReducedTrace(this, traceManager.trace.length - 1) - console.log('call tree build lasts ', (Date.now() - time) / 1000) - this.event.trigger('callTreeReady', [this.scopes, this.scopeStarts]) - } - }, (reason) => { - console.log('analyzing trace falls ' + reason) - this.event.trigger('callTreeNotReady', [reason]) - }) - } catch (e) { - console.log(e) - } + createReducedTrace(this, traceManager.trace.length - 1) + console.log('call tree build lasts ', (Date.now() - time) / 1000) + this.event.trigger('callTreeReady', [this.scopes, this.scopeStarts]) } - }) - } + }, (reason) => { + console.log('analyzing trace falls ' + reason) + this.event.trigger('callTreeNotReady', [reason]) + }) + } catch (e) { + console.log(e) + } + } + }) + } - /** + /** * reset tree * */ - reset () { - /* + reset () { + /* scopes: map of scopes defined by range in the vmtrace {firstStep, lastStep, locals}. Keys represent the level of deepness (scopeId) scopeId : .. */ - this.scopes = {} - /* + this.scopes = {} + /* scopeStart: represent start of a new scope. Keys are index in the vmtrace, values are scopeId */ - this.sourceLocationTracker.clearCache() - this.functionCallStack = [] - this.functionDefinitionsByScope = {} - this.scopeStarts = {} - this.gasCostPerLine = {} - this.variableDeclarationByFile = {} - this.functionDefinitionByFile = {} - this.astWalker = new AstWalker() - this.reducedTrace = [] - this.locationAndOpcodePerVMTraceIndex = {} - this.pendingConstructorExecutionAt = -1 - this.pendingConstructorId = -1 - this.constructorsStartExecution = {} - this.pendingConstructor = null - this.variables = {} - } + this.sourceLocationTracker.clearCache() + this.functionCallStack = [] + this.functionDefinitionsByScope = {} + this.scopeStarts = {} + this.gasCostPerLine = {} + this.variableDeclarationByFile = {} + this.functionDefinitionByFile = {} + this.astWalker = new AstWalker() + this.reducedTrace = [] + this.locationAndOpcodePerVMTraceIndex = {} + this.pendingConstructorExecutionAt = -1 + this.pendingConstructorId = -1 + this.constructorsStartExecution = {} + this.pendingConstructor = null + this.variables = {} + } - /** + /** * find the scope given @arg vmTraceIndex * * @param {Int} vmtraceIndex - index on the vm trace */ - findScope (vmtraceIndex) { - let scopeId = this.findScopeId(vmtraceIndex) - if (scopeId !== '' && !scopeId) return null - let scope = this.scopes[scopeId] - while (scope.lastStep && scope.lastStep < vmtraceIndex && scope.firstStep > 0) { - scopeId = this.parentScope(scopeId) - scope = this.scopes[scopeId] - } - return scope + findScope (vmtraceIndex) { + let scopeId = this.findScopeId(vmtraceIndex) + if (scopeId !== '' && !scopeId) return null + let scope = this.scopes[scopeId] + while (scope.lastStep && scope.lastStep < vmtraceIndex && scope.firstStep > 0) { + scopeId = this.parentScope(scopeId) + scope = this.scopes[scopeId] } + return scope + } - parentScope (scopeId) { - if (scopeId.indexOf('.') === -1) return '' - return scopeId.replace(/(\.\d+)$/, '') - } + parentScope (scopeId) { + if (scopeId.indexOf('.') === -1) return '' + return scopeId.replace(/(\.\d+)$/, '') + } - findScopeId (vmtraceIndex) { - const scopes = Object.keys(this.scopeStarts) - if (!scopes.length) return null - const scopeStart = util.findLowerBoundValue(vmtraceIndex, scopes) - return this.scopeStarts[scopeStart] - } + findScopeId (vmtraceIndex) { + const scopes = Object.keys(this.scopeStarts) + if (!scopes.length) return null + const scopeStart = util.findLowerBoundValue(vmtraceIndex, scopes) + return this.scopeStarts[scopeStart] + } - retrieveFunctionsStack (vmtraceIndex) { - const scope = this.findScope(vmtraceIndex) - if (!scope) return [] - let scopeId = this.scopeStarts[scope.firstStep] - const scopeDetail = this.scopes[scopeId] - const functions = [] - if (!scopeId) return functions - let i = 0 - // eslint-disable-next-line no-constant-condition - while (true) { - i += 1 - if (i > 1000) throw new Error('retrieFunctionStack: recursion too deep') - const functionDefinition = this.functionDefinitionsByScope[scopeId] - if (functionDefinition !== undefined) { - functions.push({ ...functionDefinition, ...scopeDetail }) - } - const parent = this.parentScope(scopeId) - if (!parent) break - else scopeId = parent - } - return functions + retrieveFunctionsStack (vmtraceIndex) { + const scope = this.findScope(vmtraceIndex) + if (!scope) return [] + let scopeId = this.scopeStarts[scope.firstStep] + const scopeDetail = this.scopes[scopeId] + const functions = [] + if (!scopeId) return functions + let i = 0 + // eslint-disable-next-line no-constant-condition + while (true) { + i += 1 + if (i > 1000) throw new Error('retrieFunctionStack: recursion too deep') + const functionDefinition = this.functionDefinitionsByScope[scopeId] + if (functionDefinition !== undefined) { + functions.push({ ...functionDefinition, ...scopeDetail }) + } + const parent = this.parentScope(scopeId) + if (!parent) break + else scopeId = parent } + return functions + } - async extractSourceLocation (step: number, address?: string) { - try { - if (!address) address = this.traceManager.getCurrentCalledAddressAt(step) - const compilationResult = await this.solidityProxy.compilationResult(address) - return await this.sourceLocationTracker.getSourceLocationFromVMTraceIndex(address, step, compilationResult.data.contracts) - } catch (error) { - throw new Error('InternalCallTree - Cannot retrieve sourcelocation for step ' + step + ' ' + error) - } + async extractSourceLocation (step: number, address?: string) { + try { + if (!address) address = this.traceManager.getCurrentCalledAddressAt(step) + const compilationResult = await this.solidityProxy.compilationResult(address) + return await this.sourceLocationTracker.getSourceLocationFromVMTraceIndex(address, step, compilationResult.data.contracts) + } catch (error) { + throw new Error('InternalCallTree - Cannot retrieve sourcelocation for step ' + step + ' ' + error) } + } - async extractValidSourceLocation (step: number, address?: string) { - try { - if (!address) address = this.traceManager.getCurrentCalledAddressAt(step) - const compilationResult = await this.solidityProxy.compilationResult(address) - return await this.sourceLocationTracker.getValidSourceLocationFromVMTraceIndex(address, step, compilationResult.data.contracts) - } catch (error) { - throw new Error('InternalCallTree - Cannot retrieve valid sourcelocation for step ' + step + ' ' + error) - } + async extractValidSourceLocation (step: number, address?: string) { + try { + if (!address) address = this.traceManager.getCurrentCalledAddressAt(step) + const compilationResult = await this.solidityProxy.compilationResult(address) + return await this.sourceLocationTracker.getValidSourceLocationFromVMTraceIndex(address, step, compilationResult.data.contracts) + } catch (error) { + throw new Error('InternalCallTree - Cannot retrieve valid sourcelocation for step ' + step + ' ' + error) } + } - async getValidSourceLocationFromVMTraceIndexFromCache (address: string, step: number, contracts: any) { - return await this.sourceLocationTracker.getValidSourceLocationFromVMTraceIndexFromCache(address, step, contracts, this.locationAndOpcodePerVMTraceIndex) - } + async getValidSourceLocationFromVMTraceIndexFromCache (address: string, step: number, contracts: any) { + return await this.sourceLocationTracker.getValidSourceLocationFromVMTraceIndexFromCache(address, step, contracts, this.locationAndOpcodePerVMTraceIndex) + } - async getGasCostPerLine(file: number, line: number) { - if (this.gasCostPerLine[file] && this.gasCostPerLine[file][line]) { - return this.gasCostPerLine[file][line] - } - throw new Error('Could not find gas cost per line') + async getGasCostPerLine(file: number, line: number) { + if (this.gasCostPerLine[file] && this.gasCostPerLine[file][line]) { + return this.gasCostPerLine[file][line] } + throw new Error('Could not find gas cost per line') + } - getLocalVariableById (id: number) { - return this.variables[id] - } + getLocalVariableById (id: number) { + return this.variables[id] + } } async function buildTree (tree, step, scopeId, isCreation, functionDefinition?, contractObj?, sourceLocation?, validSourceLocation?) { - let subScope = 1 - if (functionDefinition) { - const address = tree.traceManager.getCurrentCalledAddressAt(step) - await registerFunctionParameters(tree, functionDefinition, step, scopeId, contractObj, validSourceLocation, address) - } + let subScope = 1 + if (functionDefinition) { + const address = tree.traceManager.getCurrentCalledAddressAt(step) + await registerFunctionParameters(tree, functionDefinition, step, scopeId, contractObj, validSourceLocation, address) + } - function callDepthChange (step, trace) { - if (step + 1 < trace.length) { - return trace[step].depth !== trace[step + 1].depth - } - return false + function callDepthChange (step, trace) { + if (step + 1 < trace.length) { + return trace[step].depth !== trace[step + 1].depth } + return false + } - function includedSource (source, included) { - return (included.start !== -1 && + function includedSource (source, included) { + return (included.start !== -1 && included.length !== -1 && included.file !== -1 && included.start >= source.start && included.start + included.length <= source.start + source.length && included.file === source.file) - } + } - let currentSourceLocation = sourceLocation || { start: -1, length: -1, file: -1, jump: '-' } - let previousSourceLocation = currentSourceLocation - let previousValidSourceLocation = validSourceLocation || currentSourceLocation - let compilationResult - let currentAddress = '' - while (step < tree.traceManager.trace.length) { - let sourceLocation - let validSourceLocation - let address + let currentSourceLocation = sourceLocation || { start: -1, length: -1, file: -1, jump: '-' } + let previousSourceLocation = currentSourceLocation + let previousValidSourceLocation = validSourceLocation || currentSourceLocation + let compilationResult + let currentAddress = '' + while (step < tree.traceManager.trace.length) { + let sourceLocation + let validSourceLocation + let address - try { - address = tree.traceManager.getCurrentCalledAddressAt(step) - sourceLocation = await tree.extractSourceLocation(step, address) + try { + address = tree.traceManager.getCurrentCalledAddressAt(step) + sourceLocation = await tree.extractSourceLocation(step, address) - if (!includedSource(sourceLocation, currentSourceLocation)) { - tree.reducedTrace.push(step) - currentSourceLocation = sourceLocation - } - if (currentAddress !== address) { - compilationResult = await tree.solidityProxy.compilationResult(address) - currentAddress = address - } - const amountOfSources = tree.sourceLocationTracker.getTotalAmountOfSources(address, compilationResult.data.contracts) - if (tree.sourceLocationTracker.isInvalidSourceLocation(currentSourceLocation, amountOfSources)) { // file is -1 or greater than amount of sources - validSourceLocation = previousValidSourceLocation - } else - validSourceLocation = currentSourceLocation + if (!includedSource(sourceLocation, currentSourceLocation)) { + tree.reducedTrace.push(step) + currentSourceLocation = sourceLocation + } + if (currentAddress !== address) { + compilationResult = await tree.solidityProxy.compilationResult(address) + currentAddress = address + } + const amountOfSources = tree.sourceLocationTracker.getTotalAmountOfSources(address, compilationResult.data.contracts) + if (tree.sourceLocationTracker.isInvalidSourceLocation(currentSourceLocation, amountOfSources)) { // file is -1 or greater than amount of sources + validSourceLocation = previousValidSourceLocation + } else + validSourceLocation = currentSourceLocation - } catch (e) { - return { outStep: step, error: 'InternalCallTree - Error resolving source location. ' + step + ' ' + e } - } - if (!sourceLocation) { - return { outStep: step, error: 'InternalCallTree - No source Location. ' + step } - } - const stepDetail: StepDetail = tree.traceManager.trace[step] - const nextStepDetail: StepDetail = tree.traceManager.trace[step + 1] - if (stepDetail && nextStepDetail) { - stepDetail.gasCost = parseInt(stepDetail.gas as string) - parseInt(nextStepDetail.gas as string) - } + } catch (e) { + return { outStep: step, error: 'InternalCallTree - Error resolving source location. ' + step + ' ' + e } + } + if (!sourceLocation) { + return { outStep: step, error: 'InternalCallTree - No source Location. ' + step } + } + const stepDetail: StepDetail = tree.traceManager.trace[step] + const nextStepDetail: StepDetail = tree.traceManager.trace[step + 1] + if (stepDetail && nextStepDetail) { + stepDetail.gasCost = parseInt(stepDetail.gas as string) - parseInt(nextStepDetail.gas as string) + } - // gas per line - let lineColumnPos - if (tree.offsetToLineColumnConverter) { - try { - const generatedSources = tree.sourceLocationTracker.getGeneratedSourcesFromAddress(address) - const astSources = Object.assign({}, compilationResult.data.sources) - const sources = Object.assign({}, compilationResult.source.sources) - if (generatedSources) { - for (const genSource of generatedSources) { - astSources[genSource.name] = { id: genSource.id, ast: genSource.ast } - sources[genSource.name] = { content: genSource.contents } - } - } + // gas per line + let lineColumnPos + if (tree.offsetToLineColumnConverter) { + try { + const generatedSources = tree.sourceLocationTracker.getGeneratedSourcesFromAddress(address) + const astSources = Object.assign({}, compilationResult.data.sources) + const sources = Object.assign({}, compilationResult.source.sources) + if (generatedSources) { + for (const genSource of generatedSources) { + astSources[genSource.name] = { id: genSource.id, ast: genSource.ast } + sources[genSource.name] = { content: genSource.contents } + } + } - lineColumnPos = await tree.offsetToLineColumnConverter.offsetToLineColumn(validSourceLocation, validSourceLocation.file, sources, astSources) - if (!tree.gasCostPerLine[validSourceLocation.file]) tree.gasCostPerLine[validSourceLocation.file] = {} - if (!tree.gasCostPerLine[validSourceLocation.file][lineColumnPos.start.line]) { - tree.gasCostPerLine[validSourceLocation.file][lineColumnPos.start.line] = { - gasCost: 0, - indexes: [] - } - } - tree.gasCostPerLine[validSourceLocation.file][lineColumnPos.start.line].gasCost += stepDetail.gasCost - tree.gasCostPerLine[validSourceLocation.file][lineColumnPos.start.line].indexes.push(step) - } catch (e) { - console.log(e) - } + lineColumnPos = await tree.offsetToLineColumnConverter.offsetToLineColumn(validSourceLocation, validSourceLocation.file, sources, astSources) + if (!tree.gasCostPerLine[validSourceLocation.file]) tree.gasCostPerLine[validSourceLocation.file] = {} + if (!tree.gasCostPerLine[validSourceLocation.file][lineColumnPos.start.line]) { + tree.gasCostPerLine[validSourceLocation.file][lineColumnPos.start.line] = { + gasCost: 0, + indexes: [] + } } + tree.gasCostPerLine[validSourceLocation.file][lineColumnPos.start.line].gasCost += stepDetail.gasCost + tree.gasCostPerLine[validSourceLocation.file][lineColumnPos.start.line].indexes.push(step) + } catch (e) { + console.log(e) + } + } - tree.locationAndOpcodePerVMTraceIndex[step] = { sourceLocation, stepDetail, lineColumnPos, contractAddress: address } - tree.scopes[scopeId].gasCost += stepDetail.gasCost + tree.locationAndOpcodePerVMTraceIndex[step] = { sourceLocation, stepDetail, lineColumnPos, contractAddress: address } + tree.scopes[scopeId].gasCost += stepDetail.gasCost - const contractObj = await tree.solidityProxy.contractObjectAtAddress(address) - const generatedSources = getGeneratedSources(tree, scopeId, contractObj) - const functionDefinition = await resolveFunctionDefinition(tree, sourceLocation, generatedSources, address) + const contractObj = await tree.solidityProxy.contractObjectAtAddress(address) + const generatedSources = getGeneratedSources(tree, scopeId, contractObj) + const functionDefinition = await resolveFunctionDefinition(tree, sourceLocation, generatedSources, address) - const isInternalTxInstrn = isCallInstruction(stepDetail) - const isCreateInstrn = isCreateInstruction(stepDetail) - // we are checking if we are jumping in a new CALL or in an internal function + const isInternalTxInstrn = isCallInstruction(stepDetail) + const isCreateInstrn = isCreateInstruction(stepDetail) + // we are checking if we are jumping in a new CALL or in an internal function - const constructorExecutionStarts = tree.pendingConstructorExecutionAt > -1 && tree.pendingConstructorExecutionAt < validSourceLocation.start - if (functionDefinition && functionDefinition.kind === 'constructor' && tree.pendingConstructorExecutionAt === -1 && !tree.constructorsStartExecution[functionDefinition.id]) { - tree.pendingConstructorExecutionAt = validSourceLocation.start - tree.pendingConstructorId = functionDefinition.id - tree.pendingConstructor = functionDefinition - // from now on we'll be waiting for a change in the source location which will mark the beginning of the constructor execution. - // constructorsStartExecution allows to keep track on which constructor has already been executed. + const constructorExecutionStarts = tree.pendingConstructorExecutionAt > -1 && tree.pendingConstructorExecutionAt < validSourceLocation.start + if (functionDefinition && functionDefinition.kind === 'constructor' && tree.pendingConstructorExecutionAt === -1 && !tree.constructorsStartExecution[functionDefinition.id]) { + tree.pendingConstructorExecutionAt = validSourceLocation.start + tree.pendingConstructorId = functionDefinition.id + tree.pendingConstructor = functionDefinition + // from now on we'll be waiting for a change in the source location which will mark the beginning of the constructor execution. + // constructorsStartExecution allows to keep track on which constructor has already been executed. + } + const internalfunctionCall = functionDefinition && previousSourceLocation.jump === 'i' + if (constructorExecutionStarts || isInternalTxInstrn || internalfunctionCall) { + try { + const newScopeId = scopeId === '' ? subScope.toString() : scopeId + '.' + subScope + tree.scopeStarts[step] = newScopeId + tree.scopes[newScopeId] = { firstStep: step, locals: {}, isCreation, gasCost: 0 } + // for the ctor we we are at the start of its trace, we have to replay this step in order to catch all the locals: + const nextStep = constructorExecutionStarts ? step : step + 1 + if (constructorExecutionStarts) { + tree.constructorsStartExecution[tree.pendingConstructorId] = tree.pendingConstructorExecutionAt + tree.pendingConstructorExecutionAt = -1 + tree.pendingConstructorId = -1 + await registerFunctionParameters(tree, tree.pendingConstructor, step, newScopeId, contractObj, previousValidSourceLocation, address) + tree.pendingConstructor = null } - const internalfunctionCall = functionDefinition && previousSourceLocation.jump === 'i' - if (constructorExecutionStarts || isInternalTxInstrn || internalfunctionCall) { - try { - const newScopeId = scopeId === '' ? subScope.toString() : scopeId + '.' + subScope - tree.scopeStarts[step] = newScopeId - tree.scopes[newScopeId] = { firstStep: step, locals: {}, isCreation, gasCost: 0 } - // for the ctor we we are at the start of its trace, we have to replay this step in order to catch all the locals: - const nextStep = constructorExecutionStarts ? step : step + 1 - if (constructorExecutionStarts) { - tree.constructorsStartExecution[tree.pendingConstructorId] = tree.pendingConstructorExecutionAt - tree.pendingConstructorExecutionAt = -1 - tree.pendingConstructorId = -1 - await registerFunctionParameters(tree, tree.pendingConstructor, step, newScopeId, contractObj, previousValidSourceLocation, address) - tree.pendingConstructor = null - } - const externalCallResult = await buildTree(tree, nextStep, newScopeId, isCreateInstrn, functionDefinition, contractObj, sourceLocation, validSourceLocation) - if (externalCallResult.error) { - return { outStep: step, error: 'InternalCallTree - ' + externalCallResult.error } - } else { - step = externalCallResult.outStep - subScope++ - } - } catch (e) { - return { outStep: step, error: 'InternalCallTree - ' + e.message } - } - } else if (callDepthChange(step, tree.traceManager.trace) || (sourceLocation.jump === 'o' && functionDefinition)) { - // if not, we might be returning from a CALL or internal function. This is what is checked here. - tree.scopes[scopeId].lastStep = step - return { outStep: step + 1 } + const externalCallResult = await buildTree(tree, nextStep, newScopeId, isCreateInstrn, functionDefinition, contractObj, sourceLocation, validSourceLocation) + if (externalCallResult.error) { + return { outStep: step, error: 'InternalCallTree - ' + externalCallResult.error } } else { - // if not, we are in the current scope. - // We check in `includeVariableDeclaration` if there is a new local variable in scope for this specific `step` - if (tree.includeLocalVariables) { - await includeVariableDeclaration(tree, step, sourceLocation, scopeId, contractObj, generatedSources, address) - } - previousSourceLocation = sourceLocation - previousValidSourceLocation = validSourceLocation - step++ + step = externalCallResult.outStep + subScope++ } + } catch (e) { + return { outStep: step, error: 'InternalCallTree - ' + e.message } + } + } else if (callDepthChange(step, tree.traceManager.trace) || (sourceLocation.jump === 'o' && functionDefinition)) { + // if not, we might be returning from a CALL or internal function. This is what is checked here. + tree.scopes[scopeId].lastStep = step + return { outStep: step + 1 } + } else { + // if not, we are in the current scope. + // We check in `includeVariableDeclaration` if there is a new local variable in scope for this specific `step` + if (tree.includeLocalVariables) { + await includeVariableDeclaration(tree, step, sourceLocation, scopeId, contractObj, generatedSources, address) + } + previousSourceLocation = sourceLocation + previousValidSourceLocation = validSourceLocation + step++ } - return { outStep: step } + } + return { outStep: step } } // the reduced trace contain an entry only if that correspond to a new source location function createReducedTrace (tree, index) { - tree.reducedTrace.push(index) + tree.reducedTrace.push(index) } function getGeneratedSources (tree, scopeId, contractObj) { - if (tree.debugWithGeneratedSources && contractObj && tree.scopes[scopeId]) { - return tree.scopes[scopeId].isCreation ? contractObj.contract.evm.bytecode.generatedSources : contractObj.contract.evm.deployedBytecode.generatedSources - } - return null + if (tree.debugWithGeneratedSources && contractObj && tree.scopes[scopeId]) { + return tree.scopes[scopeId].isCreation ? contractObj.contract.evm.bytecode.generatedSources : contractObj.contract.evm.deployedBytecode.generatedSources + } + return null } async function registerFunctionParameters (tree, functionDefinition, step, scopeId, contractObj, sourceLocation, address) { - tree.functionCallStack.push(step) - const functionDefinitionAndInputs = { functionDefinition, inputs: [] } - // means: the previous location was a function definition && JUMPDEST - // => we are at the beginning of the function and input/output are setup - try { - const stack = tree.traceManager.getStackAt(step) - const states = await tree.solidityProxy.extractStatesDefinitions(address) - if (functionDefinition.parameters) { - const inputs = functionDefinition.parameters - const outputs = functionDefinition.returnParameters - // input params - if (inputs && inputs.parameters) { - functionDefinitionAndInputs.inputs = addParams(inputs, tree, scopeId, states, contractObj, sourceLocation, stack.length, inputs.parameters.length, -1) - } - // output params - if (outputs) addParams(outputs, tree, scopeId, states, contractObj, sourceLocation, stack.length, 0, 1) - } - } catch (error) { - console.log(error) + tree.functionCallStack.push(step) + const functionDefinitionAndInputs = { functionDefinition, inputs: [] } + // means: the previous location was a function definition && JUMPDEST + // => we are at the beginning of the function and input/output are setup + try { + const stack = tree.traceManager.getStackAt(step) + const states = await tree.solidityProxy.extractStatesDefinitions(address) + if (functionDefinition.parameters) { + const inputs = functionDefinition.parameters + const outputs = functionDefinition.returnParameters + // input params + if (inputs && inputs.parameters) { + functionDefinitionAndInputs.inputs = addParams(inputs, tree, scopeId, states, contractObj, sourceLocation, stack.length, inputs.parameters.length, -1) + } + // output params + if (outputs) addParams(outputs, tree, scopeId, states, contractObj, sourceLocation, stack.length, 0, 1) } + } catch (error) { + console.log(error) + } - tree.functionDefinitionsByScope[scopeId] = functionDefinitionAndInputs + tree.functionDefinitionsByScope[scopeId] = functionDefinitionAndInputs } async function includeVariableDeclaration (tree, step, sourceLocation, scopeId, contractObj, generatedSources, address) { - let states = null - const variableDeclarations = await resolveVariableDeclaration(tree, sourceLocation, generatedSources, address) - // using the vm trace step, the current source location and the ast, - // we check if the current vm trace step target a new ast node of type VariableDeclaration - // that way we know that there is a new local variable from here. - if (variableDeclarations && variableDeclarations.length) { - for (const variableDeclaration of variableDeclarations) { - if (variableDeclaration && !tree.scopes[scopeId].locals[variableDeclaration.name]) { - try { - const stack = tree.traceManager.getStackAt(step) - // the stack length at this point is where the value of the new local variable will be stored. - // so, either this is the direct value, or the offset in memory. That depends on the type. - if (variableDeclaration.name !== '') { - states = await tree.solidityProxy.extractStatesDefinitions(address) - let location = extractLocationFromAstVariable(variableDeclaration) - location = location === 'default' ? 'storage' : location - // we push the new local variable in our tree - const newVar = { - name: variableDeclaration.name, - type: parseType(variableDeclaration.typeDescriptions.typeString, states, contractObj.name, location), - stackDepth: stack.length, - sourceLocation: sourceLocation - } - tree.scopes[scopeId].locals[variableDeclaration.name] = newVar - tree.variables[variableDeclaration.id] = newVar - } - } catch (error) { - console.log(error) - } + let states = null + const variableDeclarations = await resolveVariableDeclaration(tree, sourceLocation, generatedSources, address) + // using the vm trace step, the current source location and the ast, + // we check if the current vm trace step target a new ast node of type VariableDeclaration + // that way we know that there is a new local variable from here. + if (variableDeclarations && variableDeclarations.length) { + for (const variableDeclaration of variableDeclarations) { + if (variableDeclaration && !tree.scopes[scopeId].locals[variableDeclaration.name]) { + try { + const stack = tree.traceManager.getStackAt(step) + // the stack length at this point is where the value of the new local variable will be stored. + // so, either this is the direct value, or the offset in memory. That depends on the type. + if (variableDeclaration.name !== '') { + states = await tree.solidityProxy.extractStatesDefinitions(address) + let location = extractLocationFromAstVariable(variableDeclaration) + location = location === 'default' ? 'storage' : location + // we push the new local variable in our tree + const newVar = { + name: variableDeclaration.name, + type: parseType(variableDeclaration.typeDescriptions.typeString, states, contractObj.name, location), + stackDepth: stack.length, + sourceLocation: sourceLocation } + tree.scopes[scopeId].locals[variableDeclaration.name] = newVar + tree.variables[variableDeclaration.id] = newVar + } + } catch (error) { + console.log(error) } + } } + } } // this extract all the variable declaration for a given ast and file // and keep this in a cache async function resolveVariableDeclaration (tree, sourceLocation, generatedSources, address) { - if (!tree.variableDeclarationByFile[sourceLocation.file]) { - const ast = await tree.solidityProxy.ast(sourceLocation, generatedSources, address) - if (ast) { - tree.variableDeclarationByFile[sourceLocation.file] = extractVariableDeclarations(ast, tree.astWalker) - } else { - return null - } + if (!tree.variableDeclarationByFile[sourceLocation.file]) { + const ast = await tree.solidityProxy.ast(sourceLocation, generatedSources, address) + if (ast) { + tree.variableDeclarationByFile[sourceLocation.file] = extractVariableDeclarations(ast, tree.astWalker) + } else { + return null } - return tree.variableDeclarationByFile[sourceLocation.file][sourceLocation.start + ':' + sourceLocation.length + ':' + sourceLocation.file] + } + return tree.variableDeclarationByFile[sourceLocation.file][sourceLocation.start + ':' + sourceLocation.length + ':' + sourceLocation.file] } // this extract all the function definition for a given ast and file // and keep this in a cache async function resolveFunctionDefinition (tree, sourceLocation, generatedSources, address) { - if (!tree.functionDefinitionByFile[sourceLocation.file]) { - const ast = await tree.solidityProxy.ast(sourceLocation, generatedSources, address) - if (ast) { - tree.functionDefinitionByFile[sourceLocation.file] = extractFunctionDefinitions(ast, tree.astWalker) - } else { - return null - } + if (!tree.functionDefinitionByFile[sourceLocation.file]) { + const ast = await tree.solidityProxy.ast(sourceLocation, generatedSources, address) + if (ast) { + tree.functionDefinitionByFile[sourceLocation.file] = extractFunctionDefinitions(ast, tree.astWalker) + } else { + return null } - return tree.functionDefinitionByFile[sourceLocation.file][sourceLocation.start + ':' + sourceLocation.length + ':' + sourceLocation.file] + } + return tree.functionDefinitionByFile[sourceLocation.file][sourceLocation.start + ':' + sourceLocation.length + ':' + sourceLocation.file] } function extractVariableDeclarations (ast, astWalker) { - const ret = {} - astWalker.walkFull(ast, (node) => { - if (node.nodeType === 'VariableDeclaration' || node.nodeType === 'YulVariableDeclaration') { - ret[node.src] = [node] - } - const hasChild = node.initialValue && (node.nodeType === 'VariableDeclarationStatement' || node.nodeType === 'YulVariableDeclarationStatement') - if (hasChild) ret[node.initialValue.src] = node.declarations - }) - return ret + const ret = {} + astWalker.walkFull(ast, (node) => { + if (node.nodeType === 'VariableDeclaration' || node.nodeType === 'YulVariableDeclaration') { + ret[node.src] = [node] + } + const hasChild = node.initialValue && (node.nodeType === 'VariableDeclarationStatement' || node.nodeType === 'YulVariableDeclarationStatement') + if (hasChild) ret[node.initialValue.src] = node.declarations + }) + return ret } function extractFunctionDefinitions (ast, astWalker) { - const ret = {} - astWalker.walkFull(ast, (node) => { - if (node.nodeType === 'FunctionDefinition' || node.nodeType === 'YulFunctionDefinition') { - ret[node.src] = node - } - }) - return ret + const ret = {} + astWalker.walkFull(ast, (node) => { + if (node.nodeType === 'FunctionDefinition' || node.nodeType === 'YulFunctionDefinition') { + ret[node.src] = node + } + }) + return ret } function addParams (parameterList, tree, scopeId, states, contractObj, sourceLocation, stackLength, stackPosition, dir) { - const contractName = contractObj.name - const params = [] - for (const inputParam in parameterList.parameters) { - const param = parameterList.parameters[inputParam] - const stackDepth = stackLength + (dir * stackPosition) - if (stackDepth >= 0) { - let location = extractLocationFromAstVariable(param) - location = location === 'default' ? 'memory' : location - const attributesName = param.name === '' ? `$${inputParam}` : param.name - const newParam = { - name: attributesName, - type: parseType(param.typeDescriptions.typeString, states, contractName, location), - stackDepth: stackDepth, - sourceLocation: sourceLocation, - abi: contractObj.contract.abi, - isParameter: true - } - tree.scopes[scopeId].locals[attributesName] = newParam - params.push(attributesName) - if (!tree.variables[param.id]) tree.variables[param.id] = newParam - } - stackPosition += dir + const contractName = contractObj.name + const params = [] + for (const inputParam in parameterList.parameters) { + const param = parameterList.parameters[inputParam] + const stackDepth = stackLength + (dir * stackPosition) + if (stackDepth >= 0) { + let location = extractLocationFromAstVariable(param) + location = location === 'default' ? 'memory' : location + const attributesName = param.name === '' ? `$${inputParam}` : param.name + const newParam = { + name: attributesName, + type: parseType(param.typeDescriptions.typeString, states, contractName, location), + stackDepth: stackDepth, + sourceLocation: sourceLocation, + abi: contractObj.contract.abi, + isParameter: true + } + tree.scopes[scopeId].locals[attributesName] = newParam + params.push(attributesName) + if (!tree.variables[param.id]) tree.variables[param.id] = newParam } - return params + stackPosition += dir + } + return params } diff --git a/libs/remix-debug/src/solidity-decoder/localDecoder.ts b/libs/remix-debug/src/solidity-decoder/localDecoder.ts index d7619ce12b..9a781c4c29 100644 --- a/libs/remix-debug/src/solidity-decoder/localDecoder.ts +++ b/libs/remix-debug/src/solidity-decoder/localDecoder.ts @@ -1,36 +1,36 @@ 'use strict' export async function solidityLocals (vmtraceIndex, internalTreeCall, stack, memory, storageResolver, calldata, currentSourceLocation, cursor) { - const scope = internalTreeCall.findScope(vmtraceIndex) - if (!scope) { - const error = { message: 'Can\'t display locals. reason: compilation result might not have been provided' } - throw error + const scope = internalTreeCall.findScope(vmtraceIndex) + if (!scope) { + const error = { message: 'Can\'t display locals. reason: compilation result might not have been provided' } + throw error + } + const locals = {} + memory = formatMemory(memory) + let anonymousIncr = 1 + for (const local in scope.locals) { + const variable = scope.locals[local] + if (variable.stackDepth < stack.length && (variable.sourceLocation.start <= currentSourceLocation.start || variable.isParameter)) { + let name = variable.name + if (name.indexOf('$') !== -1) { + name = '<' + anonymousIncr + '>' + anonymousIncr++ + } + try { + locals[name] = await variable.type.decodeFromStack(variable.stackDepth, stack, memory, storageResolver, calldata, cursor, variable) + } catch (e) { + console.log(e) + locals[name] = { error: '', type: variable && variable.type && variable.type.typeName || 'unknown' } + } } - const locals = {} - memory = formatMemory(memory) - let anonymousIncr = 1 - for (const local in scope.locals) { - const variable = scope.locals[local] - if (variable.stackDepth < stack.length && (variable.sourceLocation.start <= currentSourceLocation.start || variable.isParameter)) { - let name = variable.name - if (name.indexOf('$') !== -1) { - name = '<' + anonymousIncr + '>' - anonymousIncr++ - } - try { - locals[name] = await variable.type.decodeFromStack(variable.stackDepth, stack, memory, storageResolver, calldata, cursor, variable) - } catch (e) { - console.log(e) - locals[name] = { error: '', type: variable && variable.type && variable.type.typeName || 'unknown' } - } - } - } - return locals + } + return locals } function formatMemory (memory) { - if (memory instanceof Array) { - memory = memory.join('').replace(/0x/g, '') - } - return memory + if (memory instanceof Array) { + memory = memory.join('').replace(/0x/g, '') + } + return memory } diff --git a/libs/remix-debug/src/solidity-decoder/solidityProxy.ts b/libs/remix-debug/src/solidity-decoder/solidityProxy.ts index 462dad4d05..b248168ad3 100644 --- a/libs/remix-debug/src/solidity-decoder/solidityProxy.ts +++ b/libs/remix-debug/src/solidity-decoder/solidityProxy.ts @@ -5,102 +5,102 @@ import { extractStateVariables } from './stateDecoder' import { extractContractDefinitions, extractStatesDefinitions } from './astHelper' export class SolidityProxy { - cache - getCurrentCalledAddressAt - getCode - sources - contracts - compilationResult - sourcesCode + cache + getCurrentCalledAddressAt + getCode + sources + contracts + compilationResult + sourcesCode - constructor ({ getCurrentCalledAddressAt, getCode, compilationResult }) { - this.cache = new Cache() - this.getCurrentCalledAddressAt = getCurrentCalledAddressAt - this.getCode = getCode - this.compilationResult = compilationResult - } + constructor ({ getCurrentCalledAddressAt, getCode, compilationResult }) { + this.cache = new Cache() + this.getCurrentCalledAddressAt = getCurrentCalledAddressAt + this.getCode = getCode + this.compilationResult = compilationResult + } - /** + /** * reset the cache and apply a new @arg compilationResult * */ - reset () { - this.cache.reset() - } + reset () { + this.cache.reset() + } - /** + /** * retrieve the compiled contract name at the @arg vmTraceIndex (cached) * * @param {Int} vmTraceIndex - index in the vm trave where to resolve the executed contract name * @return {Object} a contract object */ - async contractObjectAt (vmTraceIndex: number) { - const address = this.getCurrentCalledAddressAt(vmTraceIndex) - return this.contractObjectAtAddress(address) - } + async contractObjectAt (vmTraceIndex: number) { + const address = this.getCurrentCalledAddressAt(vmTraceIndex) + return this.contractObjectAtAddress(address) + } - /** + /** * retrieve the compiled contract name at the @arg address (cached) * * @param {String} address - address of a contract * @return {Object} a contract object */ - async contractObjectAtAddress (address: string) { - if (this.cache.contractObjectByAddress[address]) { - return this.cache.contractObjectByAddress[address] - } - const code = await this.getCode(address) - const compilationResult = await this.compilationResult(address) - const contract = contractObjectFromCode(compilationResult.data.contracts, code.bytecode, address) - this.cache.contractObjectByAddress[address] = contract - return contract + async contractObjectAtAddress (address: string) { + if (this.cache.contractObjectByAddress[address]) { + return this.cache.contractObjectByAddress[address] } + const code = await this.getCode(address) + const compilationResult = await this.compilationResult(address) + const contract = contractObjectFromCode(compilationResult.data.contracts, code.bytecode, address) + this.cache.contractObjectByAddress[address] = contract + return contract + } - /** + /** * extract the state variables of the given compiled @arg address (cached) * * @param {String} address - address of the contract to retrieve state variables from * @return {Object} - returns state variables of @args address */ - async extractStatesDefinitions (address: string) { - const compilationResult = await this.compilationResult(address) - if (!this.cache.contractDeclarations[address]) { - this.cache.contractDeclarations[address] = extractContractDefinitions(compilationResult.data.sources) - } - if (!this.cache.statesDefinitions[address]) { - this.cache.statesDefinitions[address] = extractStatesDefinitions(compilationResult.data.sources, this.cache.contractDeclarations[address]) - } - return this.cache.statesDefinitions[address] + async extractStatesDefinitions (address: string) { + const compilationResult = await this.compilationResult(address) + if (!this.cache.contractDeclarations[address]) { + this.cache.contractDeclarations[address] = extractContractDefinitions(compilationResult.data.sources) } + if (!this.cache.statesDefinitions[address]) { + this.cache.statesDefinitions[address] = extractStatesDefinitions(compilationResult.data.sources, this.cache.contractDeclarations[address]) + } + return this.cache.statesDefinitions[address] + } - /** + /** * extract the state variables of the given compiled @arg contractName (cached) * * @param {String} contractName - name of the contract to retrieve state variables from * @param {String} address - contract address * @return {Object} - returns state variables of @args contractName */ - async extractStateVariables (contractName, address) { - if (!this.cache.stateVariablesByContractName[contractName]) { - const compilationResult = await this.compilationResult(address) - this.cache.stateVariablesByContractName[contractName] = extractStateVariables(contractName, compilationResult.data.sources) - } - return this.cache.stateVariablesByContractName[contractName] + async extractStateVariables (contractName, address) { + if (!this.cache.stateVariablesByContractName[contractName]) { + const compilationResult = await this.compilationResult(address) + this.cache.stateVariablesByContractName[contractName] = extractStateVariables(contractName, compilationResult.data.sources) } + return this.cache.stateVariablesByContractName[contractName] + } - /** + /** * extract the state variables of the given compiled @arg vmtraceIndex (cached) * * @param {Int} vmTraceIndex - index in the vm trave where to resolve the state variables * @param {String} address - contract address * @return {Object} - returns state variables of @args vmTraceIndex */ - async extractStateVariablesAt (vmtraceIndex, address) { - const contract = await this.contractObjectAt(vmtraceIndex) - return await this.extractStateVariables(contract.name, address) - } + async extractStateVariablesAt (vmtraceIndex, address) { + const contract = await this.contractObjectAt(vmtraceIndex) + return await this.extractStateVariables(contract.name, address) + } - /** + /** * get the AST of the file declare in the @arg sourceLocation * * @param {Object} sourceLocation - source location containing the 'file' to retrieve the AST from @@ -108,58 +108,58 @@ export class SolidityProxy { * @param {String} address - contract address * @return {Object} - AST of the current file */ - async ast (sourceLocation, generatedSources, address) { - const compilationResult = await this.compilationResult(address) - const file = this.fileNameFromIndex(sourceLocation.file, compilationResult.data) - if (!file && generatedSources && generatedSources.length) { - for (const source of generatedSources) { - if (source.id === sourceLocation.file) return source.ast - } - } else if (compilationResult.data.sources && compilationResult.data.sources[file]) { - return compilationResult.data.sources[file].ast - } - return null + async ast (sourceLocation, generatedSources, address) { + const compilationResult = await this.compilationResult(address) + const file = this.fileNameFromIndex(sourceLocation.file, compilationResult.data) + if (!file && generatedSources && generatedSources.length) { + for (const source of generatedSources) { + if (source.id === sourceLocation.file) return source.ast + } + } else if (compilationResult.data.sources && compilationResult.data.sources[file]) { + return compilationResult.data.sources[file].ast } + return null + } - /** + /** * get the filename refering to the index from the compilation result * * @param {Int} index - index of the filename * @param {Object} compilationResult - current compilation result * @return {String} - filename */ - fileNameFromIndex (index, compilationResult) { - return Object.keys(compilationResult.contracts)[index] - } + fileNameFromIndex (index, compilationResult) { + return Object.keys(compilationResult.contracts)[index] + } } function contractObjectFromCode (contracts, code, address) { - const isCreation = isContractCreation(address) - for (const file in contracts) { - for (const contract in contracts[file]) { - const bytecode = isCreation ? contracts[file][contract].evm.bytecode.object : contracts[file][contract].evm.deployedBytecode.object - if (util.compareByteCode(code, '0x' + bytecode)) { - return { name: contract, contract: contracts[file][contract] } - } - } + const isCreation = isContractCreation(address) + for (const file in contracts) { + for (const contract in contracts[file]) { + const bytecode = isCreation ? contracts[file][contract].evm.bytecode.object : contracts[file][contract].evm.deployedBytecode.object + if (util.compareByteCode(code, '0x' + bytecode)) { + return { name: contract, contract: contracts[file][contract] } + } } - return null + } + return null } class Cache { - contractObjectByAddress - stateVariablesByContractName - contractDeclarations - statesDefinitions + contractObjectByAddress + stateVariablesByContractName + contractDeclarations + statesDefinitions - constructor () { - this.reset() - } + constructor () { + this.reset() + } - reset () { - this.contractObjectByAddress = {} - this.stateVariablesByContractName = {} - this.contractDeclarations = {} - this.statesDefinitions = {} - } + reset () { + this.contractObjectByAddress = {} + this.stateVariablesByContractName = {} + this.contractDeclarations = {} + this.statesDefinitions = {} + } } diff --git a/libs/remix-debug/src/solidity-decoder/stateDecoder.ts b/libs/remix-debug/src/solidity-decoder/stateDecoder.ts index 531c7abee7..b186e52659 100644 --- a/libs/remix-debug/src/solidity-decoder/stateDecoder.ts +++ b/libs/remix-debug/src/solidity-decoder/stateDecoder.ts @@ -9,26 +9,26 @@ import { computeOffsets } from './decodeInfo' * @return {Map} - decoded state variable */ export async function decodeState (stateVars, storageResolver) { - const ret = {} - for (const k in stateVars) { - const stateVar = stateVars[k] - try { - const decoded = await stateVar.type.decodeFromStorage(stateVar.storagelocation, storageResolver) - decoded.constant = stateVar.constant - decoded.immutable = stateVar.immutable - if (decoded.constant) { - decoded.value = '' - } - if (decoded.immutable) { - decoded.value = '' - } - ret[stateVar.name] = decoded - } catch (e) { - console.log(e) - ret[stateVar.name] = { error: '' } - } + const ret = {} + for (const k in stateVars) { + const stateVar = stateVars[k] + try { + const decoded = await stateVar.type.decodeFromStorage(stateVar.storagelocation, storageResolver) + decoded.constant = stateVar.constant + decoded.immutable = stateVar.immutable + if (decoded.constant) { + decoded.value = '' + } + if (decoded.immutable) { + decoded.value = '' + } + ret[stateVar.name] = decoded + } catch (e) { + console.log(e) + ret[stateVar.name] = { error: '' } } - return ret + } + return ret } /** @@ -39,16 +39,16 @@ export async function decodeState (stateVars, storageResolver) { * @return {Object} - return the location of all contract variables in the storage */ export function extractStateVariables (contractName, sourcesList) { - const states = extractStatesDefinitions(sourcesList, null) - if (!states[contractName]) { - return [] - } - const types = states[contractName].stateVariables - const offsets = computeOffsets(types, states, contractName, 'storage') - if (!offsets) { - return [] // TODO should maybe return an error - } - return offsets.typesOffsets + const states = extractStatesDefinitions(sourcesList, null) + if (!states[contractName]) { + return [] + } + const types = states[contractName].stateVariables + const offsets = computeOffsets(types, states, contractName, 'storage') + if (!offsets) { + return [] // TODO should maybe return an error + } + return offsets.typesOffsets } /** @@ -60,10 +60,10 @@ export function extractStateVariables (contractName, sourcesList) { * @return {Map} - return the state of the contract */ export async function solidityState (storageResolver, astList, contractName) { - const stateVars = extractStateVariables(contractName, astList) - try { - return await decodeState(stateVars, storageResolver) - } catch (e) { - return { error: '' } - } + const stateVars = extractStateVariables(contractName, astList) + try { + return await decodeState(stateVars, storageResolver) + } catch (e) { + return { error: '' } + } } diff --git a/libs/remix-debug/src/solidity-decoder/types/Address.ts b/libs/remix-debug/src/solidity-decoder/types/Address.ts index ffe03c1c9f..bcb82f2db5 100644 --- a/libs/remix-debug/src/solidity-decoder/types/Address.ts +++ b/libs/remix-debug/src/solidity-decoder/types/Address.ts @@ -3,14 +3,14 @@ import { extractHexByteSlice } from './util' import { ValueType } from './ValueType' export class Address extends ValueType { - constructor () { - super(1, 20, 'address') - } + constructor () { + super(1, 20, 'address') + } - decodeValue (value) { - if (!value) { - return '0x0000000000000000000000000000000000000000' - } - return '0x' + extractHexByteSlice(value, this.storageBytes, 0).toUpperCase() + decodeValue (value) { + if (!value) { + return '0x0000000000000000000000000000000000000000' } + return '0x' + extractHexByteSlice(value, this.storageBytes, 0).toUpperCase() + } } diff --git a/libs/remix-debug/src/solidity-decoder/types/ArrayType.ts b/libs/remix-debug/src/solidity-decoder/types/ArrayType.ts index 29f98ab736..791e09b13c 100644 --- a/libs/remix-debug/src/solidity-decoder/types/ArrayType.ts +++ b/libs/remix-debug/src/solidity-decoder/types/ArrayType.ts @@ -5,103 +5,103 @@ import { RefType } from './RefType' const sha3256 = util.sha3_256 export class ArrayType extends RefType { - underlyingType - arraySize + underlyingType + arraySize - constructor (underlyingType, arraySize, location) { - let storageSlots = null - if (arraySize === 'dynamic') { - storageSlots = 1 - } else { - if (underlyingType.storageBytes < 32) { - const itemPerSlot = Math.floor(32 / underlyingType.storageBytes) - storageSlots = Math.ceil(arraySize / itemPerSlot) - } else { - storageSlots = arraySize * underlyingType.storageSlots - } - } - const size = arraySize !== 'dynamic' ? arraySize : '' - super(storageSlots, 32, underlyingType.typeName + '[' + size + ']', location) - this.underlyingType = underlyingType - this.arraySize = arraySize + constructor (underlyingType, arraySize, location) { + let storageSlots = null + if (arraySize === 'dynamic') { + storageSlots = 1 + } else { + if (underlyingType.storageBytes < 32) { + const itemPerSlot = Math.floor(32 / underlyingType.storageBytes) + storageSlots = Math.ceil(arraySize / itemPerSlot) + } else { + storageSlots = arraySize * underlyingType.storageSlots + } } + const size = arraySize !== 'dynamic' ? arraySize : '' + super(storageSlots, 32, underlyingType.typeName + '[' + size + ']', location) + this.underlyingType = underlyingType + this.arraySize = arraySize + } - async decodeFromStorage (location, storageResolver) { - const ret = [] - let size = null - let slotValue - try { - slotValue = await extractHexValue(location, storageResolver, this.storageBytes) - } catch (e) { - console.log(e) - return { - error: '', - type: this.typeName - } - } - const currentLocation = { - offset: 0, - slot: location.slot - } - if (this.arraySize === 'dynamic') { - size = toBN('0x' + slotValue) - currentLocation.slot = sha3256(location.slot) - } else { - size = toBN(this.arraySize) + async decodeFromStorage (location, storageResolver) { + const ret = [] + let size = null + let slotValue + try { + slotValue = await extractHexValue(location, storageResolver, this.storageBytes) + } catch (e) { + console.log(e) + return { + error: '', + type: this.typeName + } + } + const currentLocation = { + offset: 0, + slot: location.slot + } + if (this.arraySize === 'dynamic') { + size = toBN('0x' + slotValue) + currentLocation.slot = sha3256(location.slot) + } else { + size = toBN(this.arraySize) + } + const k = toBN(0) + for (; k.lt(size) && k.ltn(300); k.iaddn(1)) { + try { + ret.push(await this.underlyingType.decodeFromStorage(currentLocation, storageResolver)) + } catch (e) { + return { + error: '', + type: this.typeName } - const k = toBN(0) - for (; k.lt(size) && k.ltn(300); k.iaddn(1)) { - try { - ret.push(await this.underlyingType.decodeFromStorage(currentLocation, storageResolver)) - } catch (e) { - return { - error: '', - type: this.typeName - } - } - if (this.underlyingType.storageSlots === 1 && location.offset + this.underlyingType.storageBytes <= 32) { - currentLocation.offset += this.underlyingType.storageBytes - if (currentLocation.offset + this.underlyingType.storageBytes > 32) { - currentLocation.offset = 0 - currentLocation.slot = '0x' + add(currentLocation.slot, 1).toString(16) - } - } else { - currentLocation.slot = '0x' + add(currentLocation.slot, this.underlyingType.storageSlots).toString(16) - currentLocation.offset = 0 - } + } + if (this.underlyingType.storageSlots === 1 && location.offset + this.underlyingType.storageBytes <= 32) { + currentLocation.offset += this.underlyingType.storageBytes + if (currentLocation.offset + this.underlyingType.storageBytes > 32) { + currentLocation.offset = 0 + currentLocation.slot = '0x' + add(currentLocation.slot, 1).toString(16) } - return { value: ret, length: '0x' + size.toString(16), type: this.typeName } + } else { + currentLocation.slot = '0x' + add(currentLocation.slot, this.underlyingType.storageSlots).toString(16) + currentLocation.offset = 0 + } } + return { value: ret, length: '0x' + size.toString(16), type: this.typeName } + } - decodeFromMemoryInternal (offset, memory, skip) { - const ret = [] - let length = this.arraySize - if (this.arraySize === 'dynamic') { - length = memory.substr(2 * offset, 64) - length = parseInt(length, 16) - offset = offset + 32 - } - if (isNaN(length)) { - return { - error: '', - type: 'Error' - } - } - if (!skip) skip = 0 - if (skip) offset = offset + (32 * skip) - let limit = length - skip - if (limit > 10) limit = 10 - for (let k = 0; k < limit; k++) { - const contentOffset = offset - ret.push(this.underlyingType.decodeFromMemory(contentOffset, memory)) - offset += 32 - } - return { - value: ret, - length: '0x' + length.toString(16), - type: this.typeName, - cursor: skip + limit, - hasNext: length > (skip + limit) - } + decodeFromMemoryInternal (offset, memory, skip) { + const ret = [] + let length = this.arraySize + if (this.arraySize === 'dynamic') { + length = memory.substr(2 * offset, 64) + length = parseInt(length, 16) + offset = offset + 32 + } + if (isNaN(length)) { + return { + error: '', + type: 'Error' + } + } + if (!skip) skip = 0 + if (skip) offset = offset + (32 * skip) + let limit = length - skip + if (limit > 10) limit = 10 + for (let k = 0; k < limit; k++) { + const contentOffset = offset + ret.push(this.underlyingType.decodeFromMemory(contentOffset, memory)) + offset += 32 + } + return { + value: ret, + length: '0x' + length.toString(16), + type: this.typeName, + cursor: skip + limit, + hasNext: length > (skip + limit) } + } } diff --git a/libs/remix-debug/src/solidity-decoder/types/Bool.ts b/libs/remix-debug/src/solidity-decoder/types/Bool.ts index 8c067c011b..4c998a6f47 100644 --- a/libs/remix-debug/src/solidity-decoder/types/Bool.ts +++ b/libs/remix-debug/src/solidity-decoder/types/Bool.ts @@ -3,15 +3,15 @@ import { ValueType } from './ValueType' import { extractHexByteSlice } from './util' export class Bool extends ValueType { - constructor () { - super(1, 1, 'bool') - } + constructor () { + super(1, 1, 'bool') + } - decodeValue (value) { - if (!value) { - return false - } - value = extractHexByteSlice(value, this.storageBytes, 0) - return value !== '00' + decodeValue (value) { + if (!value) { + return false } + value = extractHexByteSlice(value, this.storageBytes, 0) + return value !== '00' + } } diff --git a/libs/remix-debug/src/solidity-decoder/types/DynamicByteArray.ts b/libs/remix-debug/src/solidity-decoder/types/DynamicByteArray.ts index 2f5fcb6c80..bfb967a2a0 100644 --- a/libs/remix-debug/src/solidity-decoder/types/DynamicByteArray.ts +++ b/libs/remix-debug/src/solidity-decoder/types/DynamicByteArray.ts @@ -6,51 +6,51 @@ import { RefType } from './RefType' const sha3256 = util.sha3_256 export class DynamicByteArray extends RefType { - constructor (location) { - super(1, 32, 'bytes', location) - } + constructor (location) { + super(1, 32, 'bytes', location) + } - async decodeFromStorage (location, storageResolver) { - let value = '0x0' + async decodeFromStorage (location, storageResolver) { + let value = '0x0' + try { + value = await extractHexValue(location, storageResolver, this.storageBytes) + } catch (e) { + console.log(e) + return { error: '', type: this.typeName } + } + const length = new BN(value, 16) + if (length.testn(0)) { + let dataPos = new BN(sha3256(location.slot).replace('0x', ''), 16) + let ret = '' + let currentSlot = '0x' + try { + currentSlot = await readFromStorage(dataPos, storageResolver) + } catch (e) { + console.log(e) + return { error: '', type: this.typeName } + } + while (length.gt(new BN(ret.length)) && ret.length < 32000) { + currentSlot = currentSlot.replace('0x', '') + ret += currentSlot + dataPos = dataPos.add(new BN(1)) try { - value = await extractHexValue(location, storageResolver, this.storageBytes) + currentSlot = await readFromStorage(dataPos, storageResolver) } catch (e) { - console.log(e) - return { error: '', type: this.typeName } - } - const length = new BN(value, 16) - if (length.testn(0)) { - let dataPos = new BN(sha3256(location.slot).replace('0x', ''), 16) - let ret = '' - let currentSlot = '0x' - try { - currentSlot = await readFromStorage(dataPos, storageResolver) - } catch (e) { - console.log(e) - return { error: '', type: this.typeName } - } - while (length.gt(new BN(ret.length)) && ret.length < 32000) { - currentSlot = currentSlot.replace('0x', '') - ret += currentSlot - dataPos = dataPos.add(new BN(1)) - try { - currentSlot = await readFromStorage(dataPos, storageResolver) - } catch (e) { - console.log(e) - return { error: '', type: this.typeName } - } - } - return { value: '0x' + ret.replace(/(00)+$/, ''), length: '0x' + length.toString(16), type: this.typeName } - } else { - const size = parseInt(value.substr(value.length - 2, 2), 16) / 2 - return { value: '0x' + value.substr(0, size * 2), length: '0x' + size.toString(16), type: this.typeName } + console.log(e) + return { error: '', type: this.typeName } } + } + return { value: '0x' + ret.replace(/(00)+$/, ''), length: '0x' + length.toString(16), type: this.typeName } + } else { + const size = parseInt(value.substr(value.length - 2, 2), 16) / 2 + return { value: '0x' + value.substr(0, size * 2), length: '0x' + size.toString(16), type: this.typeName } } + } - decodeFromMemoryInternal (offset, memory) { - offset = 2 * offset - let length = memory.substr(offset, 64) - length = 2 * parseInt(length, 16) - return { length: '0x' + length.toString(16), value: '0x' + memory.substr(offset + 64, length), type: this.typeName } - } + decodeFromMemoryInternal (offset, memory) { + offset = 2 * offset + let length = memory.substr(offset, 64) + length = 2 * parseInt(length, 16) + return { length: '0x' + length.toString(16), value: '0x' + memory.substr(offset + 64, length), type: this.typeName } + } } diff --git a/libs/remix-debug/src/solidity-decoder/types/Enum.ts b/libs/remix-debug/src/solidity-decoder/types/Enum.ts index 412533f17f..89e7ff09cd 100644 --- a/libs/remix-debug/src/solidity-decoder/types/Enum.ts +++ b/libs/remix-debug/src/solidity-decoder/types/Enum.ts @@ -2,27 +2,27 @@ import { ValueType } from './ValueType' export class Enum extends ValueType { - enumDef + enumDef - constructor (enumDef) { - let storageBytes = 0 - let length = enumDef.members.length - while (length > 1) { - length = length / 256 - storageBytes++ - } - super(1, storageBytes, 'enum') - this.enumDef = enumDef + constructor (enumDef) { + let storageBytes = 0 + let length = enumDef.members.length + while (length > 1) { + length = length / 256 + storageBytes++ } + super(1, storageBytes, 'enum') + this.enumDef = enumDef + } - decodeValue (value) { - if (!value) { - return this.enumDef.members[0].name - } - value = parseInt(value, 16) - if (this.enumDef.members.length > value) { - return this.enumDef.members[value].name - } - return 'INVALID_ENUM<' + value + '>' + decodeValue (value) { + if (!value) { + return this.enumDef.members[0].name } + value = parseInt(value, 16) + if (this.enumDef.members.length > value) { + return this.enumDef.members[value].name + } + return 'INVALID_ENUM<' + value + '>' + } } diff --git a/libs/remix-debug/src/solidity-decoder/types/FixedByteArray.ts b/libs/remix-debug/src/solidity-decoder/types/FixedByteArray.ts index a6704dc962..7dce850038 100644 --- a/libs/remix-debug/src/solidity-decoder/types/FixedByteArray.ts +++ b/libs/remix-debug/src/solidity-decoder/types/FixedByteArray.ts @@ -2,11 +2,11 @@ import { ValueType } from './ValueType' export class FixedByteArray extends ValueType { - constructor (storageBytes) { - super(1, storageBytes, 'bytes' + storageBytes) - } + constructor (storageBytes) { + super(1, storageBytes, 'bytes' + storageBytes) + } - decodeValue (value) { - return '0x' + value.substr(0, 2 * this.storageBytes).toUpperCase() - } + decodeValue (value) { + return '0x' + value.substr(0, 2 * this.storageBytes).toUpperCase() + } } diff --git a/libs/remix-debug/src/solidity-decoder/types/FunctionType.ts b/libs/remix-debug/src/solidity-decoder/types/FunctionType.ts index ffa4b9f1c6..dfda87be3c 100644 --- a/libs/remix-debug/src/solidity-decoder/types/FunctionType.ts +++ b/libs/remix-debug/src/solidity-decoder/types/FunctionType.ts @@ -2,11 +2,11 @@ import { ValueType } from './ValueType' export class FunctionType extends ValueType { - constructor (type, stateDefinitions, contractName, location) { - super(1, 8, 'function') - } + constructor (type, stateDefinitions, contractName, location) { + super(1, 8, 'function') + } - decodeValue (value) { - return 'at program counter ' + value - } + decodeValue (value) { + return 'at program counter ' + value + } } diff --git a/libs/remix-debug/src/solidity-decoder/types/Int.ts b/libs/remix-debug/src/solidity-decoder/types/Int.ts index ed35f79292..78cc6e2da3 100644 --- a/libs/remix-debug/src/solidity-decoder/types/Int.ts +++ b/libs/remix-debug/src/solidity-decoder/types/Int.ts @@ -3,12 +3,12 @@ import { extractHexByteSlice, decodeIntFromHex } from './util' import { ValueType } from './ValueType' export class Int extends ValueType { - constructor (storageBytes) { - super(1, storageBytes, 'int' + storageBytes * 8) - } + constructor (storageBytes) { + super(1, storageBytes, 'int' + storageBytes * 8) + } - decodeValue (value) { - value = extractHexByteSlice(value, this.storageBytes, 0) - return decodeIntFromHex(value, this.storageBytes, true) - } + decodeValue (value) { + value = extractHexByteSlice(value, this.storageBytes, 0) + return decodeIntFromHex(value, this.storageBytes, true) + } } diff --git a/libs/remix-debug/src/solidity-decoder/types/Mapping.ts b/libs/remix-debug/src/solidity-decoder/types/Mapping.ts index 870c0b01a8..fd7e79331b 100644 --- a/libs/remix-debug/src/solidity-decoder/types/Mapping.ts +++ b/libs/remix-debug/src/solidity-decoder/types/Mapping.ts @@ -6,78 +6,78 @@ import { toBuffer, setLengthLeft, bufferToHex, addHexPrefix } from '@ethereumjs/ import BN from 'bn.js' export class Mapping extends RefType { - keyType - valueType - initialDecodedState + keyType + valueType + initialDecodedState - constructor (underlyingTypes, location, fullType) { - super(1, 32, fullType, 'storage') - this.keyType = underlyingTypes.keyType - this.valueType = underlyingTypes.valueType - this.initialDecodedState = null - } + constructor (underlyingTypes, location, fullType) { + super(1, 32, fullType, 'storage') + this.keyType = underlyingTypes.keyType + this.valueType = underlyingTypes.valueType + this.initialDecodedState = null + } - async decodeFromStorage (location, storageResolver) { - const corrections = this.valueType.members ? this.valueType.members.map((value) => { return value.storagelocation }) : [] - if (!this.initialDecodedState) { // cache the decoded initial storage - let mappingsInitialPreimages - try { - mappingsInitialPreimages = await storageResolver.initialMappingsLocation(corrections) - this.initialDecodedState = await this.decodeMappingsLocation(mappingsInitialPreimages, location, storageResolver) - } catch (e) { - return { - value: e.message, - type: this.typeName - } - } + async decodeFromStorage (location, storageResolver) { + const corrections = this.valueType.members ? this.valueType.members.map((value) => { return value.storagelocation }) : [] + if (!this.initialDecodedState) { // cache the decoded initial storage + let mappingsInitialPreimages + try { + mappingsInitialPreimages = await storageResolver.initialMappingsLocation(corrections) + this.initialDecodedState = await this.decodeMappingsLocation(mappingsInitialPreimages, location, storageResolver) + } catch (e) { + return { + value: e.message, + type: this.typeName } - const mappingPreimages = await storageResolver.mappingsLocation(corrections) - let ret = await this.decodeMappingsLocation(mappingPreimages, location, storageResolver) // fetch mapping storage changes - ret = Object.assign({}, this.initialDecodedState, ret) // merge changes - return { value: ret, type: this.typeName } + } } + const mappingPreimages = await storageResolver.mappingsLocation(corrections) + let ret = await this.decodeMappingsLocation(mappingPreimages, location, storageResolver) // fetch mapping storage changes + ret = Object.assign({}, this.initialDecodedState, ret) // merge changes + return { value: ret, type: this.typeName } + } - decodeFromMemoryInternal (offset, memory) { - // mappings can only exist in storage and not in memory - // so this should never be called - return { value: '', length: '0x0', type: this.typeName } - } + decodeFromMemoryInternal (offset, memory) { + // mappings can only exist in storage and not in memory + // so this should never be called + return { value: '', length: '0x0', type: this.typeName } + } - async decodeMappingsLocation (preimages, location, storageResolver) { - const mapSlot = normalizeHex(bufferToHex(location.slot)) - if (!preimages[mapSlot]) { - return {} - } - const ret = {} - for (const i in preimages[mapSlot]) { - const mapLocation = getMappingLocation(i, location.slot) - const globalLocation = { - offset: location.offset, - slot: mapLocation - } - ret[i] = await this.valueType.decodeFromStorage(globalLocation, storageResolver) - } - return ret + async decodeMappingsLocation (preimages, location, storageResolver) { + const mapSlot = normalizeHex(bufferToHex(location.slot)) + if (!preimages[mapSlot]) { + return {} + } + const ret = {} + for (const i in preimages[mapSlot]) { + const mapLocation = getMappingLocation(i, location.slot) + const globalLocation = { + offset: location.offset, + slot: mapLocation + } + ret[i] = await this.valueType.decodeFromStorage(globalLocation, storageResolver) } + return ret + } } function getMappingLocation (key, position) { - // mapping storage location decribed at http://solidity.readthedocs.io/en/develop/miscellaneous.html#layout-of-state-variables-in-storage - // > the value corresponding to a mapping key k is located at keccak256(k . p) where . is concatenation. + // mapping storage location decribed at http://solidity.readthedocs.io/en/develop/miscellaneous.html#layout-of-state-variables-in-storage + // > the value corresponding to a mapping key k is located at keccak256(k . p) where . is concatenation. - // key should be a hex string, and position an int - const mappingK = toBuffer(addHexPrefix(key)) - let mappingP = toBuffer(addHexPrefix(position)) - mappingP = setLengthLeft(mappingP, 32) - const mappingKeyBuf = concatTypedArrays(mappingK, mappingP) - const mappingStorageLocation: Buffer = hash.keccak(mappingKeyBuf) - const mappingStorageLocationinBn: BN = new BN(mappingStorageLocation, 16) - return mappingStorageLocationinBn + // key should be a hex string, and position an int + const mappingK = toBuffer(addHexPrefix(key)) + let mappingP = toBuffer(addHexPrefix(position)) + mappingP = setLengthLeft(mappingP, 32) + const mappingKeyBuf = concatTypedArrays(mappingK, mappingP) + const mappingStorageLocation: Buffer = hash.keccak(mappingKeyBuf) + const mappingStorageLocationinBn: BN = new BN(mappingStorageLocation, 16) + return mappingStorageLocationinBn } function concatTypedArrays (a, b) { // a, b TypedArray of same type - const c = new (a.constructor)(a.length + b.length) - c.set(a, 0) - c.set(b, a.length) - return c + const c = new (a.constructor)(a.length + b.length) + c.set(a, 0) + c.set(b, a.length) + return c } diff --git a/libs/remix-debug/src/solidity-decoder/types/RefType.ts b/libs/remix-debug/src/solidity-decoder/types/RefType.ts index 3e51915077..d97ca107fe 100644 --- a/libs/remix-debug/src/solidity-decoder/types/RefType.ts +++ b/libs/remix-debug/src/solidity-decoder/types/RefType.ts @@ -3,30 +3,30 @@ import { ethers } from 'ethers' import { toBN } from './util' export class RefType { - location - storageSlots - storageBytes - typeName - basicType - underlyingType + location + storageSlots + storageBytes + typeName + basicType + underlyingType - constructor (storageSlots, storageBytes, typeName, location) { - this.location = location - this.storageSlots = storageSlots - this.storageBytes = storageBytes - this.typeName = typeName - this.basicType = 'RefType' - } + constructor (storageSlots, storageBytes, typeName, location) { + this.location = location + this.storageSlots = storageSlots + this.storageBytes = storageBytes + this.typeName = typeName + this.basicType = 'RefType' + } - decodeFromStorage (input1? : any, input2? : any) { - throw new Error('This method is abstract') - } + decodeFromStorage (input1? : any, input2? : any) { + throw new Error('This method is abstract') + } - decodeFromMemoryInternal (input1? : any, input2? : any, input3?: any) { - throw new Error('This method is abstract') - } + decodeFromMemoryInternal (input1? : any, input2? : any, input3?: any) { + throw new Error('This method is abstract') + } - /** + /** * decode the type from the stack * * @param {Int} stackDepth - position of the type in the stack @@ -35,102 +35,102 @@ export class RefType { * @param {Object} - storageResolver * @return {Object} decoded value */ - async decodeFromStack (stackDepth, stack, memory, storageResolver, calldata, cursor, variableDetails?): Promise { - if (stack.length - 1 < stackDepth) { - return { error: '', type: this.typeName } - } - let offset = stack[stack.length - 1 - stackDepth] - if (this.isInStorage()) { - offset = toBN(offset) - try { - return await this.decodeFromStorage({ offset: 0, slot: offset }, storageResolver) - } catch (e) { - console.log(e) - return { error: '', type: this.typeName } - } - } else if (this.isInMemory()) { - offset = parseInt(offset, 16) - return this.decodeFromMemoryInternal(offset, memory, cursor) - } else if (this.isInCallData()) { - return this._decodeFromCallData(variableDetails, calldata) - } else { - return { error: '', type: this.typeName } - } + async decodeFromStack (stackDepth, stack, memory, storageResolver, calldata, cursor, variableDetails?): Promise { + if (stack.length - 1 < stackDepth) { + return { error: '', type: this.typeName } } + let offset = stack[stack.length - 1 - stackDepth] + if (this.isInStorage()) { + offset = toBN(offset) + try { + return await this.decodeFromStorage({ offset: 0, slot: offset }, storageResolver) + } catch (e) { + console.log(e) + return { error: '', type: this.typeName } + } + } else if (this.isInMemory()) { + offset = parseInt(offset, 16) + return this.decodeFromMemoryInternal(offset, memory, cursor) + } else if (this.isInCallData()) { + return this._decodeFromCallData(variableDetails, calldata) + } else { + return { error: '', type: this.typeName } + } + } - _decodeFromCallData (variableDetails, calldata) { - calldata = calldata.length > 0 ? calldata[0] : '0x' - const ethersAbi = new ethers.utils.Interface(variableDetails.abi) - const fnSign = calldata.substr(0, 10) - const decodedData = ethersAbi.decodeFunctionData(ethersAbi.getFunction(fnSign), calldata) - const decodedValue = decodedData[variableDetails.name] - const isArray = Array.isArray(decodedValue) - if (isArray) { - return this._decodeCallDataArray(decodedValue, this) - } - return { - length: isArray ? '0x' + decodedValue.length.toString(16) : undefined, - value: decodedValue, - type: this.typeName - } + _decodeFromCallData (variableDetails, calldata) { + calldata = calldata.length > 0 ? calldata[0] : '0x' + const ethersAbi = new ethers.utils.Interface(variableDetails.abi) + const fnSign = calldata.substr(0, 10) + const decodedData = ethersAbi.decodeFunctionData(ethersAbi.getFunction(fnSign), calldata) + const decodedValue = decodedData[variableDetails.name] + const isArray = Array.isArray(decodedValue) + if (isArray) { + return this._decodeCallDataArray(decodedValue, this) + } + return { + length: isArray ? '0x' + decodedValue.length.toString(16) : undefined, + value: decodedValue, + type: this.typeName } + } - _decodeCallDataArray (value, type) { - const isArray = Array.isArray(value) - if (isArray) { - value = value.map((el) => { - return this._decodeCallDataArray(el, this.underlyingType) - }) - return { - length: value.length.toString(16), - value: value, - type: type.typeName - } - } else { - return { - value: value.toString(), - type: (type.underlyingType && type.underlyingType.typeName) || type.typeName - } - } + _decodeCallDataArray (value, type) { + const isArray = Array.isArray(value) + if (isArray) { + value = value.map((el) => { + return this._decodeCallDataArray(el, this.underlyingType) + }) + return { + length: value.length.toString(16), + value: value, + type: type.typeName + } + } else { + return { + value: value.toString(), + type: (type.underlyingType && type.underlyingType.typeName) || type.typeName + } } + } - /** + /** * decode the type from the memory * * @param {Int} offset - position of the ref of the type in memory * @param {String} memory - memory * @return {Object} decoded value */ - decodeFromMemory (offset, memory) { - offset = memory.substr(2 * offset, 64) - offset = parseInt(offset, 16) - return this.decodeFromMemoryInternal(offset, memory) - } + decodeFromMemory (offset, memory) { + offset = memory.substr(2 * offset, 64) + offset = parseInt(offset, 16) + return this.decodeFromMemoryInternal(offset, memory) + } - /** + /** * current type defined in storage * * @return {Bool} - return true if the type is defined in the storage */ - isInStorage () { - return this.location.indexOf('storage') === 0 - } + isInStorage () { + return this.location.indexOf('storage') === 0 + } - /** + /** * current type defined in memory * * @return {Bool} - return true if the type is defined in the memory */ - isInMemory () { - return this.location.indexOf('memory') === 0 - } + isInMemory () { + return this.location.indexOf('memory') === 0 + } - /** + /** * current type defined in storage * * @return {Bool} - return true if the type is defined in the storage */ - isInCallData () { - return this.location.indexOf('calldata') === 0 - } + isInCallData () { + return this.location.indexOf('calldata') === 0 + } } diff --git a/libs/remix-debug/src/solidity-decoder/types/StringType.ts b/libs/remix-debug/src/solidity-decoder/types/StringType.ts index 84c6483e8e..6e30395500 100644 --- a/libs/remix-debug/src/solidity-decoder/types/StringType.ts +++ b/libs/remix-debug/src/solidity-decoder/types/StringType.ts @@ -2,51 +2,51 @@ import { DynamicByteArray } from './DynamicByteArray' export class StringType extends DynamicByteArray { - typeName + typeName - constructor (location) { - super(location) - this.typeName = 'string' - } + constructor (location) { + super(location) + this.typeName = 'string' + } - async decodeFromStorage (location, storageResolver) { - let decoded: any = '0x' - try { - decoded = await super.decodeFromStorage(location, storageResolver) - } catch (e) { - console.log(e) - return { error: '' } - } - return format(decoded) + async decodeFromStorage (location, storageResolver) { + let decoded: any = '0x' + try { + decoded = await super.decodeFromStorage(location, storageResolver) + } catch (e) { + console.log(e) + return { error: '' } } + return format(decoded) + } - async decodeFromStack (stackDepth, stack, memory, storageResolver, calldata, cursor, variableDetails?) { - try { - return await super.decodeFromStack(stackDepth, stack, memory, storageResolver, calldata, cursor, variableDetails) - } catch (e) { - console.log(e) - return { error: '', type: this.typeName } - } + async decodeFromStack (stackDepth, stack, memory, storageResolver, calldata, cursor, variableDetails?) { + try { + return await super.decodeFromStack(stackDepth, stack, memory, storageResolver, calldata, cursor, variableDetails) + } catch (e) { + console.log(e) + return { error: '', type: this.typeName } } + } - decodeFromMemoryInternal (offset, memory) { - const decoded = super.decodeFromMemoryInternal(offset, memory) - return format(decoded) - } + decodeFromMemoryInternal (offset, memory) { + const decoded = super.decodeFromMemoryInternal(offset, memory) + return format(decoded) + } } function format (decoded) { - if (decoded.error) { - return decoded - } - let value = decoded.value - value = value.replace('0x', '').replace(/(..)/g, '%$1') - const ret = { length: decoded.length, raw: decoded.value, type: 'string' } - try { - ret['value'] = decodeURIComponent(value) - } catch (e) { - ret['error'] = 'Invalid UTF8 encoding' - ret.raw = decoded.value - } - return ret + if (decoded.error) { + return decoded + } + let value = decoded.value + value = value.replace('0x', '').replace(/(..)/g, '%$1') + const ret = { length: decoded.length, raw: decoded.value, type: 'string' } + try { + ret['value'] = decodeURIComponent(value) + } catch (e) { + ret['error'] = 'Invalid UTF8 encoding' + ret.raw = decoded.value + } + return ret } diff --git a/libs/remix-debug/src/solidity-decoder/types/Struct.ts b/libs/remix-debug/src/solidity-decoder/types/Struct.ts index b11f12f533..738609cd87 100644 --- a/libs/remix-debug/src/solidity-decoder/types/Struct.ts +++ b/libs/remix-debug/src/solidity-decoder/types/Struct.ts @@ -4,38 +4,38 @@ import { RefType } from './RefType' import { Mapping } from './Mapping' export class Struct extends RefType { - members + members - constructor (memberDetails, location, fullType) { - super(memberDetails.storageSlots, 32, 'struct ' + fullType, location) - this.members = memberDetails.members - } + constructor (memberDetails, location, fullType) { + super(memberDetails.storageSlots, 32, 'struct ' + fullType, location) + this.members = memberDetails.members + } - async decodeFromStorage (location, storageResolver) { - const ret = {} - for (const item of this.members) { - const globalLocation = { - offset: location.offset + item.storagelocation.offset, - slot: add(location.slot, item.storagelocation.slot) - } - try { - ret[item.name] = await item.type.decodeFromStorage(globalLocation, storageResolver) - } catch (e) { - console.log(e) - ret[item.name] = { error: '' } - } - } - return { value: ret, type: this.typeName } + async decodeFromStorage (location, storageResolver) { + const ret = {} + for (const item of this.members) { + const globalLocation = { + offset: location.offset + item.storagelocation.offset, + slot: add(location.slot, item.storagelocation.slot) + } + try { + ret[item.name] = await item.type.decodeFromStorage(globalLocation, storageResolver) + } catch (e) { + console.log(e) + ret[item.name] = { error: '' } + } } + return { value: ret, type: this.typeName } + } - decodeFromMemoryInternal (offset, memory) { - const ret = {} - this.members.map((item, i) => { - const contentOffset = offset - const member = item.type.decodeFromMemory(contentOffset, memory) - ret[item.name] = member - if (!(item.type instanceof Mapping)) offset += 32 - }) - return { value: ret, type: this.typeName } - } + decodeFromMemoryInternal (offset, memory) { + const ret = {} + this.members.map((item, i) => { + const contentOffset = offset + const member = item.type.decodeFromMemory(contentOffset, memory) + ret[item.name] = member + if (!(item.type instanceof Mapping)) offset += 32 + }) + return { value: ret, type: this.typeName } + } } diff --git a/libs/remix-debug/src/solidity-decoder/types/Uint.ts b/libs/remix-debug/src/solidity-decoder/types/Uint.ts index 8e27fc4621..794434461d 100644 --- a/libs/remix-debug/src/solidity-decoder/types/Uint.ts +++ b/libs/remix-debug/src/solidity-decoder/types/Uint.ts @@ -3,12 +3,12 @@ import { extractHexByteSlice, decodeIntFromHex } from './util' import { ValueType } from './ValueType' export class Uint extends ValueType { - constructor (storageBytes) { - super(1, storageBytes, 'uint' + storageBytes * 8) - } + constructor (storageBytes) { + super(1, storageBytes, 'uint' + storageBytes * 8) + } - decodeValue (value) { - value = extractHexByteSlice(value, this.storageBytes, 0) - return decodeIntFromHex(value, this.storageBytes, false) - } + decodeValue (value) { + value = extractHexByteSlice(value, this.storageBytes, 0) + return decodeIntFromHex(value, this.storageBytes, false) + } } diff --git a/libs/remix-debug/src/solidity-decoder/types/ValueType.ts b/libs/remix-debug/src/solidity-decoder/types/ValueType.ts index 2aeae677fe..c682f06771 100644 --- a/libs/remix-debug/src/solidity-decoder/types/ValueType.ts +++ b/libs/remix-debug/src/solidity-decoder/types/ValueType.ts @@ -2,40 +2,40 @@ import { extractHexValue } from './util' export class ValueType { - storageSlots - storageBytes - typeName - basicType + storageSlots + storageBytes + typeName + basicType - constructor (storageSlots, storageBytes, typeName) { - this.storageSlots = storageSlots - this.storageBytes = storageBytes - this.typeName = typeName - this.basicType = 'ValueType' - } + constructor (storageSlots, storageBytes, typeName) { + this.storageSlots = storageSlots + this.storageBytes = storageBytes + this.typeName = typeName + this.basicType = 'ValueType' + } - decodeValue (input? : any) { - throw new Error('This method is abstract') - } + decodeValue (input? : any) { + throw new Error('This method is abstract') + } - /** + /** * decode the type with the @arg location from the storage * * @param {Object} location - containing offset and slot * @param {Object} storageResolver - resolve storage queries * @return {Object} - decoded value */ - async decodeFromStorage (location, storageResolver) { - try { - const value = await extractHexValue(location, storageResolver, this.storageBytes) - return { value: this.decodeValue(value), type: this.typeName } - } catch (e) { - console.log(e) - return { error: '', type: this.typeName } - } + async decodeFromStorage (location, storageResolver) { + try { + const value = await extractHexValue(location, storageResolver, this.storageBytes) + return { value: this.decodeValue(value), type: this.typeName } + } catch (e) { + console.log(e) + return { error: '', type: this.typeName } } + } - /** + /** * decode the type from the stack * * @param {Int} stackDepth - position of the type in the stack @@ -43,25 +43,25 @@ export class ValueType { * @param {String} - memory * @return {Object} - decoded value */ - async decodeFromStack (stackDepth, stack, memory, storageResolver, calldata, cursor, variableDetails?) { - let value - if (stackDepth >= stack.length) { - value = this.decodeValue('') - } else { - value = this.decodeValue(stack[stack.length - 1 - stackDepth].replace('0x', '')) - } - return { value, type: this.typeName } + async decodeFromStack (stackDepth, stack, memory, storageResolver, calldata, cursor, variableDetails?) { + let value + if (stackDepth >= stack.length) { + value = this.decodeValue('') + } else { + value = this.decodeValue(stack[stack.length - 1 - stackDepth].replace('0x', '')) } + return { value, type: this.typeName } + } - /** + /** * decode the type with the @arg offset location from the memory * * @param {Int} stackDepth - position of the type in the stack * @return {String} - memory * @return {Object} - decoded value */ - decodeFromMemory (offset, memory) { - const value = memory.substr(2 * offset, 64) - return { value: this.decodeValue(value), type: this.typeName } - } + decodeFromMemory (offset, memory) { + const value = memory.substr(2 * offset, 64) + return { value: this.decodeValue(value), type: this.typeName } + } } diff --git a/libs/remix-debug/src/solidity-decoder/types/util.ts b/libs/remix-debug/src/solidity-decoder/types/util.ts index bd6a7f4bd1..34233eb97b 100644 --- a/libs/remix-debug/src/solidity-decoder/types/util.ts +++ b/libs/remix-debug/src/solidity-decoder/types/util.ts @@ -3,26 +3,26 @@ import { bufferToHex, unpadHexString } from '@ethereumjs/util' import BN from 'bn.js' export function decodeIntFromHex (value, byteLength, signed) { - let bigNumber = new BN(value, 16) - if (signed) { - bigNumber = bigNumber.fromTwos(8 * byteLength) - } - return bigNumber.toString(10) + let bigNumber = new BN(value, 16) + if (signed) { + bigNumber = bigNumber.fromTwos(8 * byteLength) + } + return bigNumber.toString(10) } export function readFromStorage (slot, storageResolver): Promise { - const hexSlot = '0x' + normalizeHex(bufferToHex(slot)) - return new Promise((resolve, reject) => { - storageResolver.storageSlot(hexSlot, (error, slot) => { - if (error) { - return reject(error) - } - if (!slot) { - slot = { key: slot, value: '' } - } - return resolve(normalizeHex(slot.value)) - }) + const hexSlot = '0x' + normalizeHex(bufferToHex(slot)) + return new Promise((resolve, reject) => { + storageResolver.storageSlot(hexSlot, (error, slot) => { + if (error) { + return reject(error) + } + if (!slot) { + slot = { key: slot, value: '' } + } + return resolve(normalizeHex(slot.value)) }) + }) } /** @@ -33,8 +33,8 @@ export function readFromStorage (slot, storageResolver): Promise { * @param {Int} offsetFromLSB - byte distance from the right end slot value to the right end of the byte slice */ export function extractHexByteSlice (slotValue, byteLength, offsetFromLSB) { - const offset = slotValue.length - 2 * offsetFromLSB - 2 * byteLength - return slotValue.substr(offset, 2 * byteLength) + const offset = slotValue.length - 2 * offsetFromLSB - 2 * byteLength + return slotValue.substr(offset, 2 * byteLength) } /** @@ -45,61 +45,61 @@ export function extractHexByteSlice (slotValue, byteLength, offsetFromLSB) { * @param {Int} byteLength - Length of the byte slice to extract */ export async function extractHexValue (location, storageResolver, byteLength) { - let slotvalue - try { - slotvalue = await readFromStorage(location.slot, storageResolver) - } catch (e) { - return '' - } - return extractHexByteSlice(slotvalue, byteLength, location.offset) + let slotvalue + try { + slotvalue = await readFromStorage(location.slot, storageResolver) + } catch (e) { + return '' + } + return extractHexByteSlice(slotvalue, byteLength, location.offset) } export function toBN (value) { - if (value instanceof BN) { - return value - } else if (value.match && value.match(/^(0x)?([a-f0-9]*)$/)) { - value = unpadHexString(value) - value = value.replace('0x', '') - value = new BN(value === '' ? '0' : value, 16) - } else if (!isNaN(value)) { - value = new BN(value) - } + if (value instanceof BN) { return value + } else if (value.match && value.match(/^(0x)?([a-f0-9]*)$/)) { + value = unpadHexString(value) + value = value.replace('0x', '') + value = new BN(value === '' ? '0' : value, 16) + } else if (!isNaN(value)) { + value = new BN(value) + } + return value } export function add (value1, value2) { - return toBN(value1).add(toBN(value2)) + return toBN(value1).add(toBN(value2)) } export function sub (value1, value2) { - return toBN(value1).sub(toBN(value2)) + return toBN(value1).sub(toBN(value2)) } export function removeLocation (type) { - return type.replace(/( storage ref| storage pointer| memory| calldata)/g, '') + return type.replace(/( storage ref| storage pointer| memory| calldata)/g, '') } export function extractLocation (type) { - const match = type.match(/( storage ref| storage pointer| memory| calldata)?$/) - if (match[1] !== '') { - return match[1].trim() - } - return null + const match = type.match(/( storage ref| storage pointer| memory| calldata)?$/) + if (match[1] !== '') { + return match[1].trim() + } + return null } export function extractLocationFromAstVariable (node) { - if (node.storageLocation !== 'default') { - return node.storageLocation - } else if (node.stateVariable) { - return 'storage' - } - return 'default' // local variables => storage, function parameters & return values => memory, state => storage + if (node.storageLocation !== 'default') { + return node.storageLocation + } else if (node.stateVariable) { + return 'storage' + } + return 'default' // local variables => storage, function parameters & return values => memory, state => storage } export function normalizeHex (hex): string { - hex = hex.replace('0x', '') - if (hex.length < 64) { - return (new Array(64 - hex.length + 1).join('0')) + hex - } - return hex + hex = hex.replace('0x', '') + if (hex.length < 64) { + return (new Array(64 - hex.length + 1).join('0')) + hex + } + return hex } diff --git a/libs/remix-debug/src/source/offsetToLineColumnConverter.ts b/libs/remix-debug/src/source/offsetToLineColumnConverter.ts index a1f296c76f..589b7ae292 100644 --- a/libs/remix-debug/src/source/offsetToLineColumnConverter.ts +++ b/libs/remix-debug/src/source/offsetToLineColumnConverter.ts @@ -2,44 +2,44 @@ import { getLinebreakPositions, convertOffsetToLineColumn } from './sourceMappingDecoder' export class OffsetToColumnConverter { - lineBreakPositionsByContent - sourceMappingDecoder - offsetConvertion + lineBreakPositionsByContent + sourceMappingDecoder + offsetConvertion - constructor (compilerEvent) { - this.lineBreakPositionsByContent = {} - this.offsetConvertion = {} - if (compilerEvent) { - compilerEvent.register('compilationFinished', (success, data, source, input, version) => { - this.clear() - }) - } + constructor (compilerEvent) { + this.lineBreakPositionsByContent = {} + this.offsetConvertion = {} + if (compilerEvent) { + compilerEvent.register('compilationFinished', (success, data, source, input, version) => { + this.clear() + }) } + } - offsetToLineColumn (rawLocation, file, sources, asts) { - if (!this.lineBreakPositionsByContent[file]) { - for (const filename in asts) { - const source = asts[filename] - // source id was string before. in newer versions it has been changed to an integer so we need to check the type here - if (typeof source.id === 'string') source.id = parseInt(source.id, 10) - if (source.id === file) { - this.lineBreakPositionsByContent[file] = getLinebreakPositions(sources[filename].content) - break - } - } - } - const token = `${rawLocation.start}:${rawLocation.length}:${file}` - if (this.offsetConvertion[token]) { - return this.offsetConvertion[token] - } else { - const convertion = convertOffsetToLineColumn(rawLocation, this.lineBreakPositionsByContent[file]) - this.offsetConvertion[token] = convertion - return convertion + offsetToLineColumn (rawLocation, file, sources, asts) { + if (!this.lineBreakPositionsByContent[file]) { + for (const filename in asts) { + const source = asts[filename] + // source id was string before. in newer versions it has been changed to an integer so we need to check the type here + if (typeof source.id === 'string') source.id = parseInt(source.id, 10) + if (source.id === file) { + this.lineBreakPositionsByContent[file] = getLinebreakPositions(sources[filename].content) + break } + } } - - clear () { - this.lineBreakPositionsByContent = {} - this.offsetConvertion = {} + const token = `${rawLocation.start}:${rawLocation.length}:${file}` + if (this.offsetConvertion[token]) { + return this.offsetConvertion[token] + } else { + const convertion = convertOffsetToLineColumn(rawLocation, this.lineBreakPositionsByContent[file]) + this.offsetConvertion[token] = convertion + return convertion } + } + + clear () { + this.lineBreakPositionsByContent = {} + this.offsetConvertion = {} + } } diff --git a/libs/remix-debug/src/source/sourceLocationTracker.ts b/libs/remix-debug/src/source/sourceLocationTracker.ts index c7502db960..190176b480 100644 --- a/libs/remix-debug/src/source/sourceLocationTracker.ts +++ b/libs/remix-debug/src/source/sourceLocationTracker.ts @@ -8,149 +8,149 @@ import { util } from '@remix-project/remix-lib' * Process the source code location for the current executing bytecode */ export class SourceLocationTracker { - opts - codeManager - event - sourceMapByAddress + opts + codeManager + event + sourceMapByAddress - constructor (_codeManager, { debugWithGeneratedSources }) { - this.opts = { - debugWithGeneratedSources: debugWithGeneratedSources || false - } - this.codeManager = _codeManager - this.event = new EventManager() - this.sourceMapByAddress = {} + constructor (_codeManager, { debugWithGeneratedSources }) { + this.opts = { + debugWithGeneratedSources: debugWithGeneratedSources || false } + this.codeManager = _codeManager + this.event = new EventManager() + this.sourceMapByAddress = {} + } - /** + /** * Return the source location associated with the given @arg index (instruction index) * * @param {String} address - contract address from which the source location is retrieved * @param {Int} index - index in the instruction list from where the source location is retrieved * @param {Object} contractDetails - AST of compiled contracts */ - async getSourceLocationFromInstructionIndex (address, index, contracts) { - const sourceMap = await this.extractSourceMap(this, this.codeManager, address, contracts) - return atIndex(index, sourceMap['map']) - } + async getSourceLocationFromInstructionIndex (address, index, contracts) { + const sourceMap = await this.extractSourceMap(this, this.codeManager, address, contracts) + return atIndex(index, sourceMap['map']) + } - /** + /** * Return the source location associated with the given @arg vmTraceIndex * * @param {String} address - contract address from which the source location is retrieved * @param {Int} vmtraceStepIndex - index of the current code in the vmtrace * @param {Object} contractDetails - AST of compiled contracts */ - async getSourceLocationFromVMTraceIndex (address, vmtraceStepIndex, contracts) { - const sourceMap = await this.extractSourceMap(this, this.codeManager, address, contracts) - const index = this.codeManager.getInstructionIndex(address, vmtraceStepIndex) - return atIndex(index, sourceMap['map']) - } + async getSourceLocationFromVMTraceIndex (address, vmtraceStepIndex, contracts) { + const sourceMap = await this.extractSourceMap(this, this.codeManager, address, contracts) + const index = this.codeManager.getInstructionIndex(address, vmtraceStepIndex) + return atIndex(index, sourceMap['map']) + } - /** + /** * Returns the generated sources from a specific @arg address * * @param {String} address - contract address from which has generated sources * @param {Object} generatedSources - Object containing the sourceid, ast and the source code. */ - getGeneratedSourcesFromAddress (address) { - if (!this.opts.debugWithGeneratedSources) return null - if (this.sourceMapByAddress[address]) return this.sourceMapByAddress[address].generatedSources - return null - } + getGeneratedSourcesFromAddress (address) { + if (!this.opts.debugWithGeneratedSources) return null + if (this.sourceMapByAddress[address]) return this.sourceMapByAddress[address].generatedSources + return null + } - /** + /** * Returns the total amount of sources from a specific @arg address and @arg contracts * * @param {String} address - contract address from which has generated sources * @param {Object} contracts - AST of compiled contracts */ - getTotalAmountOfSources (address, contracts) { - let sourcesLength = Object.keys(contracts).length - const generatedSources = this.getGeneratedSourcesFromAddress(address) - if (generatedSources) sourcesLength = sourcesLength + Object.keys(generatedSources).length - return sourcesLength - } + getTotalAmountOfSources (address, contracts) { + let sourcesLength = Object.keys(contracts).length + const generatedSources = this.getGeneratedSourcesFromAddress(address) + if (generatedSources) sourcesLength = sourcesLength + Object.keys(generatedSources).length + return sourcesLength + } - /** + /** * Return a valid source location associated with the given @arg vmTraceIndex * * @param {String} address - contract address from which the source location is retrieved * @param {Int} vmtraceStepIndex - index of the current code in the vmtrace * @param {Object} contractDetails - AST of compiled contracts */ - async getValidSourceLocationFromVMTraceIndex (address, vmtraceStepIndex, contracts) { - const amountOfSources = this.getTotalAmountOfSources(address, contracts) - let map: Record = { file: -1 } - /* + async getValidSourceLocationFromVMTraceIndex (address, vmtraceStepIndex, contracts) { + const amountOfSources = this.getTotalAmountOfSources(address, contracts) + let map: Record = { file: -1 } + /* (map.file === -1) this indicates that it isn't associated with a known source code (map.file > amountOfSources - 1) this indicates the current file index exceed the total number of files. this happens when generated sources should not be considered. */ - while (vmtraceStepIndex >= 0 && this.isInvalidSourceLocation(map, amountOfSources)) { - map = await this.getSourceLocationFromVMTraceIndex(address, vmtraceStepIndex, contracts) - vmtraceStepIndex = vmtraceStepIndex - 1 - } - return map + while (vmtraceStepIndex >= 0 && this.isInvalidSourceLocation(map, amountOfSources)) { + map = await this.getSourceLocationFromVMTraceIndex(address, vmtraceStepIndex, contracts) + vmtraceStepIndex = vmtraceStepIndex - 1 } + return map + } - isInvalidSourceLocation (sourceLocation, amountOfSources) { - return sourceLocation.file === -1 || sourceLocation.file > amountOfSources - 1 - } + isInvalidSourceLocation (sourceLocation, amountOfSources) { + return sourceLocation.file === -1 || sourceLocation.file > amountOfSources - 1 + } - async getValidSourceLocationFromVMTraceIndexFromCache (address: string, vmtraceStepIndex: number, contracts: any, cache: Map) { - const amountOfSources = this.getTotalAmountOfSources(address, contracts) - let map: any = { file: -1 } - /* + async getValidSourceLocationFromVMTraceIndexFromCache (address: string, vmtraceStepIndex: number, contracts: any, cache: Map) { + const amountOfSources = this.getTotalAmountOfSources(address, contracts) + let map: any = { file: -1 } + /* (map.file === -1) this indicates that it isn't associated with a known source code (map.file > amountOfSources - 1) this indicates the current file index exceed the total number of files. this happens when generated sources should not be considered. */ - const originStep = cache[vmtraceStepIndex] - while (vmtraceStepIndex >= 0 && (map.file === -1 || map.file > amountOfSources - 1)) { - map = cache[vmtraceStepIndex].sourceLocation - vmtraceStepIndex = vmtraceStepIndex - 1 - originStep.sourceLocation = map - } - return originStep + const originStep = cache[vmtraceStepIndex] + while (vmtraceStepIndex >= 0 && (map.file === -1 || map.file > amountOfSources - 1)) { + map = cache[vmtraceStepIndex].sourceLocation + vmtraceStepIndex = vmtraceStepIndex - 1 + originStep.sourceLocation = map } + return originStep + } - clearCache () { - this.sourceMapByAddress = {} - } + clearCache () { + this.sourceMapByAddress = {} + } - private getSourceMap (address, code, contracts) { - const isCreation = isContractCreation(address) - let bytes - for (const file in contracts) { - for (const contract in contracts[file]) { - const bytecode = contracts[file][contract].evm.bytecode - const deployedBytecode = contracts[file][contract].evm.deployedBytecode - if (!deployedBytecode) continue + private getSourceMap (address, code, contracts) { + const isCreation = isContractCreation(address) + let bytes + for (const file in contracts) { + for (const contract in contracts[file]) { + const bytecode = contracts[file][contract].evm.bytecode + const deployedBytecode = contracts[file][contract].evm.deployedBytecode + if (!deployedBytecode) continue - bytes = isCreation ? bytecode.object : deployedBytecode.object - if (util.compareByteCode(code, '0x' + bytes)) { - const generatedSources = isCreation ? bytecode.generatedSources : deployedBytecode.generatedSources - const map = isCreation ? bytecode.sourceMap : deployedBytecode.sourceMap - return { generatedSources, map } - } - } + bytes = isCreation ? bytecode.object : deployedBytecode.object + if (util.compareByteCode(code, '0x' + bytes)) { + const generatedSources = isCreation ? bytecode.generatedSources : deployedBytecode.generatedSources + const map = isCreation ? bytecode.sourceMap : deployedBytecode.sourceMap + return { generatedSources, map } } - return null + } } + return null + } - private extractSourceMap (self, codeManager, address, contracts) { - return new Promise((resolve, reject) => { - if (self.sourceMapByAddress[address]) return resolve(self.sourceMapByAddress[address]) + private extractSourceMap (self, codeManager, address, contracts) { + return new Promise((resolve, reject) => { + if (self.sourceMapByAddress[address]) return resolve(self.sourceMapByAddress[address]) - codeManager.getCode(address).then((result) => { - const sourceMap = this.getSourceMap(address, result.bytecode, contracts) - if (sourceMap) { - if (!isContractCreation(address)) self.sourceMapByAddress[address] = sourceMap - return resolve(sourceMap) - } - reject(new Error('no sourcemap associated with the code ' + address)) - }).catch(reject) - }) - } + codeManager.getCode(address).then((result) => { + const sourceMap = this.getSourceMap(address, result.bytecode, contracts) + if (sourceMap) { + if (!isContractCreation(address)) self.sourceMapByAddress[address] = sourceMap + return resolve(sourceMap) + } + reject(new Error('no sourcemap associated with the code ' + address)) + }).catch(reject) + }) + } } diff --git a/libs/remix-debug/src/source/sourceMappingDecoder.ts b/libs/remix-debug/src/source/sourceMappingDecoder.ts index 04b3bcb9ba..3ad94eaf4a 100644 --- a/libs/remix-debug/src/source/sourceMappingDecoder.ts +++ b/libs/remix-debug/src/source/sourceMappingDecoder.ts @@ -14,14 +14,14 @@ import { util } from '@remix-project/remix-lib' * @return {Object} returns the decompressed source mapping {start, length, file} */ export function decode (value) { - if (value) { - value = value.split(':') - return { - start: parseInt(value[0]), - length: parseInt(value[1]), - file: parseInt(value[2]) - } + if (value) { + value = value.split(':') + return { + start: parseInt(value[0]), + length: parseInt(value[1]), + file: parseInt(value[2]) } + } } /** @@ -31,19 +31,19 @@ export function decode (value) { * @return {Array} returns the decompressed source mapping. Array of {start, length, file, jump} */ export function decompressAll (mapping) { - const map = mapping.split(';') - const ret = [] - for (const k in map) { - const compressed = map[k].split(':') - const sourceMap = { - start: compressed[0] ? parseInt(compressed[0]) : ret[ret.length - 1].start, - length: compressed[1] ? parseInt(compressed[1]) : ret[ret.length - 1].length, - file: compressed[2] ? parseInt(compressed[2]) : ret[ret.length - 1].file, - jump: compressed[3] ? compressed[3] : ret[ret.length - 1].jump - } - ret.push(sourceMap) + const map = mapping.split(';') + const ret = [] + for (const k in map) { + const compressed = map[k].split(':') + const sourceMap = { + start: compressed[0] ? parseInt(compressed[0]) : ret[ret.length - 1].start, + length: compressed[1] ? parseInt(compressed[1]) : ret[ret.length - 1].length, + file: compressed[2] ? parseInt(compressed[2]) : ret[ret.length - 1].file, + jump: compressed[3] ? compressed[3] : ret[ret.length - 1].jump } - return ret + ret.push(sourceMap) + } + return ret } /** @@ -53,11 +53,11 @@ export function decompressAll (mapping) { * @return {Array} returns an array containing offset of line breaks */ export function getLinebreakPositions (source) { - const ret = [] - for (let pos = source.indexOf('\n'); pos >= 0; pos = source.indexOf('\n', pos + 1)) { - ret.push(pos) - } - return ret + const ret = [] + for (let pos = source.indexOf('\n'); pos >= 0; pos = source.indexOf('\n', pos + 1)) { + ret.push(pos) + } + return ret } /** @@ -68,35 +68,35 @@ export function getLinebreakPositions (source) { * @return {Object} returns an object {start: {line, column}, end: {line, column}} (line/column count start at 0) */ export function convertOffsetToLineColumn (sourceLocation, lineBreakPositions) { - if (sourceLocation.start >= 0 && sourceLocation.length >= 0) { - return { - start: convertFromCharPosition(sourceLocation.start, lineBreakPositions), - end: convertFromCharPosition(sourceLocation.start + sourceLocation.length, lineBreakPositions) - } + if (sourceLocation.start >= 0 && sourceLocation.length >= 0) { + return { + start: convertFromCharPosition(sourceLocation.start, lineBreakPositions), + end: convertFromCharPosition(sourceLocation.start + sourceLocation.length, lineBreakPositions) } - return { start: null, end: null } + } + return { start: null, end: null } } function convertFromCharPosition (pos, lineBreakPositions) { - let line = util.findLowerBound(pos, lineBreakPositions) - if (lineBreakPositions[line] !== pos) { - line = line + 1 - } - const beginColumn = line === 0 ? 0 : (lineBreakPositions[line - 1] + 1) - const column = pos - beginColumn - return { line, column } + let line = util.findLowerBound(pos, lineBreakPositions) + if (lineBreakPositions[line] !== pos) { + line = line + 1 + } + const beginColumn = line === 0 ? 0 : (lineBreakPositions[line - 1] + 1) + const column = pos - beginColumn + return { line, column } } function sourceLocationFromAstNode (astNode) { - if (astNode.src) { - const split = astNode.src.split(':') - return { - start: parseInt(split[0]), - length: parseInt(split[1]), - file: parseInt(split[2]) - } + if (astNode.src) { + const split = astNode.src.split(':') + return { + start: parseInt(split[0]), + length: parseInt(split[1]), + file: parseInt(split[2]) } - return null + } + return null } /** @@ -108,26 +108,26 @@ function sourceLocationFromAstNode (astNode) { * @param {Object} ast - ast given by the compilation result */ export function findNodeAtInstructionIndex (astNodeType, instIndex, sourceMap, ast) { - const sourceLocation = atIndex(instIndex, sourceMap) - return findNodeAtSourceLocation(astNodeType, sourceLocation, ast) + const sourceLocation = atIndex(instIndex, sourceMap) + return findNodeAtSourceLocation(astNodeType, sourceLocation, ast) } function findNodeAtSourceLocation (astNodeType, sourceLocation, ast) { - const astWalker = new AstWalker() - let found = null - const callback = function (node) { - const nodeLocation = sourceLocationFromAstNode(node) - if (!nodeLocation) { - return - } - if (nodeLocation.start <= sourceLocation.start && nodeLocation.start + nodeLocation.length >= sourceLocation.start + sourceLocation.length) { - if (astNodeType === node.nodeType) { - found = node - } - } + const astWalker = new AstWalker() + let found = null + const callback = function (node) { + const nodeLocation = sourceLocationFromAstNode(node) + if (!nodeLocation) { + return } - astWalker.walkFull(ast.ast, callback) - return found + if (nodeLocation.start <= sourceLocation.start && nodeLocation.start + nodeLocation.length >= sourceLocation.start + sourceLocation.length) { + if (astNodeType === node.nodeType) { + found = node + } + } + } + astWalker.walkFull(ast.ast, callback) + return found } /** @@ -139,21 +139,21 @@ function findNodeAtSourceLocation (astNodeType, sourceLocation, ast) { */ export function nodesAtPosition (astNodeType, position, ast) { - const astWalker = new AstWalker() - const found = [] - const callback = function (node) { - const nodeLocation = sourceLocationFromAstNode(node) - if (!nodeLocation) { - return - } - if (nodeLocation.start <= position && nodeLocation.start + nodeLocation.length >= position) { - if (!astNodeType || astNodeType === node.nodeType) { - found.push(node) - } - } + const astWalker = new AstWalker() + const found = [] + const callback = function (node) { + const nodeLocation = sourceLocationFromAstNode(node) + if (!nodeLocation) { + return + } + if (nodeLocation.start <= position && nodeLocation.start + nodeLocation.length >= position) { + if (!astNodeType || astNodeType === node.nodeType) { + found.push(node) + } } - astWalker.walkFull(ast.ast, callback) - return found + } + astWalker.walkFull(ast.ast, callback) + return found } /** @@ -172,32 +172,32 @@ export function nodesAtPosition (astNodeType, position, ast) { * @return Object { start, length, file, jump } */ export function atIndex (index, mapping) { - const ret = {} - const map = mapping.split(';') - if (index >= map.length) { - index = map.length - 1 + const ret = {} + const map = mapping.split(';') + if (index >= map.length) { + index = map.length - 1 + } + for (let k = index; k >= 0; k--) { + let current = map[k] + if (!current.length) { + continue + } + current = current.split(':') + if (ret['start'] === undefined && current[0] && current[0] !== '-1' && current[0].length) { + ret['start'] = parseInt(current[0]) + } + if (ret['length'] === undefined && current[1] && current[1] !== '-1' && current[1].length) { + ret['length'] = parseInt(current[1]) + } + if (ret['file'] === undefined && current[2] && current[2].length) { + ret['file'] = parseInt(current[2]) + } + if (ret['jump'] === undefined && current[3] && current[3].length) { + ret['jump'] = current[3] } - for (let k = index; k >= 0; k--) { - let current = map[k] - if (!current.length) { - continue - } - current = current.split(':') - if (ret['start'] === undefined && current[0] && current[0] !== '-1' && current[0].length) { - ret['start'] = parseInt(current[0]) - } - if (ret['length'] === undefined && current[1] && current[1] !== '-1' && current[1].length) { - ret['length'] = parseInt(current[1]) - } - if (ret['file'] === undefined && current[2] && current[2].length) { - ret['file'] = parseInt(current[2]) - } - if (ret['jump'] === undefined && current[3] && current[3].length) { - ret['jump'] = current[3] - } - if (ret['start'] !== undefined && ret['length'] !== undefined && ret['file'] !== undefined && ret['jump'] !== undefined) { - break - } + if (ret['start'] !== undefined && ret['length'] !== undefined && ret['file'] !== undefined && ret['jump'] !== undefined) { + break } - return ret + } + return ret } diff --git a/libs/remix-debug/src/storage/mappingPreimages.ts b/libs/remix-debug/src/storage/mappingPreimages.ts index fceb13b89e..641ddb04b7 100644 --- a/libs/remix-debug/src/storage/mappingPreimages.ts +++ b/libs/remix-debug/src/storage/mappingPreimages.ts @@ -10,31 +10,31 @@ import { sub } from '../solidity-decoder/types/util' * @return {Map} - solidity mapping location (e.g { "" : { "": preimageOf1 }, { "": preimageOf2 }, ... }) */ export async function decodeMappingsKeys (web3, storage, corrections) { - const ret = {} - if (!corrections.length) corrections.push({ offset: 0, slot: 0 }) - for (const hashedLoc in storage) { - let preimage - try { - const key = storage[hashedLoc].key - for (const k in corrections) { - const corrected = sub(key, corrections[k].slot).toString(16) - preimage = await getPreimage(web3, '0x' + corrected) - if (preimage) break - } - } catch (e) {} // eslint-disable-line no-empty - if (preimage) { - // got preimage! - // get mapping position (i.e. storage slot), its the last 32 bytes - const slotByteOffset = preimage.length - 64 - const mappingSlot = preimage.substr(slotByteOffset) - const mappingKey = preimage.substr(0, slotByteOffset) - if (!ret[mappingSlot]) { - ret[mappingSlot] = {} - } - ret[mappingSlot][mappingKey] = preimage - } + const ret = {} + if (!corrections.length) corrections.push({ offset: 0, slot: 0 }) + for (const hashedLoc in storage) { + let preimage + try { + const key = storage[hashedLoc].key + for (const k in corrections) { + const corrected = sub(key, corrections[k].slot).toString(16) + preimage = await getPreimage(web3, '0x' + corrected) + if (preimage) break + } + } catch (e) {} // eslint-disable-line no-empty + if (preimage) { + // got preimage! + // get mapping position (i.e. storage slot), its the last 32 bytes + const slotByteOffset = preimage.length - 64 + const mappingSlot = preimage.substr(slotByteOffset) + const mappingKey = preimage.substr(0, slotByteOffset) + if (!ret[mappingSlot]) { + ret[mappingSlot] = {} + } + ret[mappingSlot][mappingKey] = preimage } - return ret + } + return ret } /** @@ -44,12 +44,12 @@ export async function decodeMappingsKeys (web3, storage, corrections) { * @return {String} - preimage of the given key */ function getPreimage (web3, key) { - return new Promise((resolve, reject) => { - web3.debug.preimage(key.indexOf('0x') === 0 ? key : '0x' + key, (error, preimage) => { - if (error) { - return resolve(null) - } - resolve(preimage) - }) + return new Promise((resolve, reject) => { + web3.debug.preimage(key.indexOf('0x') === 0 ? key : '0x' + key, (error, preimage) => { + if (error) { + return resolve(null) + } + resolve(preimage) }) + }) } diff --git a/libs/remix-debug/src/storage/storageResolver.ts b/libs/remix-debug/src/storage/storageResolver.ts index c5522f37f9..f46069e328 100644 --- a/libs/remix-debug/src/storage/storageResolver.ts +++ b/libs/remix-debug/src/storage/storageResolver.ts @@ -7,21 +7,21 @@ import { decodeMappingsKeys } from './mappingPreimages' * (TODO: one instance need to be shared over all the components) */ export class StorageResolver { - storageByAddress - preimagesMappingByAddress - maxSize - web3 - zeroSlot + storageByAddress + preimagesMappingByAddress + maxSize + web3 + zeroSlot - constructor (options) { - this.storageByAddress = {} - this.preimagesMappingByAddress = {} - this.maxSize = 100 - this.web3 = options.web3 - this.zeroSlot = '0x0000000000000000000000000000000000000000000000000000000000000000' - } + constructor (options) { + this.storageByAddress = {} + this.preimagesMappingByAddress = {} + this.maxSize = 100 + this.web3 = options.web3 + this.zeroSlot = '0x0000000000000000000000000000000000000000000000000000000000000000' + } - /** + /** * returns the storage for the given context (address and vm trace index) * returns the range 0x0 => this.maxSize * @@ -30,11 +30,11 @@ export class StorageResolver { * @param {String} - address - lookup address * @param {Function} - callback - contains a map: [hashedKey] = {key, hashedKey, value} */ - storageRange (tx, stepIndex, address) { - return this.storageRangeInternal(this, this.zeroSlot, tx, stepIndex, address) - } + storageRange (tx, stepIndex, address) { + return this.storageRangeInternal(this, this.zeroSlot, tx, stepIndex, address) + } - /** + /** * compute the mappgings type locations for the current address (cached for a debugging session) * note: that only retrieve the first 100 items. * @@ -44,17 +44,17 @@ export class StorageResolver { * @param {Array} corrections - used in case the calculated sha3 has been modifyed before SSTORE (notably used for struct in mapping). * @return {Function} - callback */ - async initialPreimagesMappings (tx, stepIndex, address, corrections) { - if (this.preimagesMappingByAddress[address]) { - return this.preimagesMappingByAddress[address] - } - const storage = await this.storageRange(tx, stepIndex, address) - const mappings = decodeMappingsKeys(this.web3, storage, corrections) - this.preimagesMappingByAddress[address] = mappings - return mappings + async initialPreimagesMappings (tx, stepIndex, address, corrections) { + if (this.preimagesMappingByAddress[address]) { + return this.preimagesMappingByAddress[address] } + const storage = await this.storageRange(tx, stepIndex, address) + const mappings = decodeMappingsKeys(this.web3, storage, corrections) + this.preimagesMappingByAddress[address] = mappings + return mappings + } - /** + /** * return a slot value for the given context (address and vm trace index) * * @param {String} - slot - slot key @@ -63,90 +63,90 @@ export class StorageResolver { * @param {String} - address - lookup address * @param {Function} - callback - {key, hashedKey, value} - */ - async storageSlot (slot, tx, stepIndex, address) { - const storage = await this.storageRangeInternal(this, slot, tx, stepIndex, address) - return (storage[slot] !== undefined ? storage[slot] : null) - } + async storageSlot (slot, tx, stepIndex, address) { + const storage = await this.storageRangeInternal(this, slot, tx, stepIndex, address) + return (storage[slot] !== undefined ? storage[slot] : null) + } - /** + /** * return True if the storage at @arg address is complete * * @param {String} address - contract address * @return {Bool} - return True if the storage at @arg address is complete */ - isComplete (address) { - return this.storageByAddress[address] && this.storageByAddress[address].complete - } + isComplete (address) { + return this.storageByAddress[address] && this.storageByAddress[address].complete + } - /** + /** * retrieve the storage and ensure at least @arg slot is cached. * - If @arg slot is already cached, the storage will be returned from the cache * even if the next 1000 items are not in the cache. * - If @arg slot is not cached, the corresponding value will be resolved and the next 1000 slots. */ - async storageRangeInternal (self, slotKey, tx, stepIndex, address) { - const cached = this.fromCache(self, address) - if (cached && cached.storage[slotKey]) { // we have the current slot in the cache and maybe the next 1000... - return cached.storage - } - const result = await this.storageRangeWeb3Call(tx, address, slotKey, self.maxSize) - const [storage, nextKey] = result - if (!storage[slotKey] && slotKey !== self.zeroSlot) { // we don't cache the zero slot (could lead to inconsistency) - storage[slotKey] = { key: slotKey, value: self.zeroSlot } - } - self.toCache(self, address, storage) - if (slotKey === self.zeroSlot && !nextKey) { // only working if keys are sorted !! - self.storageByAddress[address].complete = true - } - return storage + async storageRangeInternal (self, slotKey, tx, stepIndex, address) { + const cached = this.fromCache(self, address) + if (cached && cached.storage[slotKey]) { // we have the current slot in the cache and maybe the next 1000... + return cached.storage } + const result = await this.storageRangeWeb3Call(tx, address, slotKey, self.maxSize) + const [storage, nextKey] = result + if (!storage[slotKey] && slotKey !== self.zeroSlot) { // we don't cache the zero slot (could lead to inconsistency) + storage[slotKey] = { key: slotKey, value: self.zeroSlot } + } + self.toCache(self, address, storage) + if (slotKey === self.zeroSlot && !nextKey) { // only working if keys are sorted !! + self.storageByAddress[address].complete = true + } + return storage + } - /** + /** * retrieve the storage from the cache. if @arg slot is defined, return only the desired slot, if not return the entire known storage * * @param {String} address - contract address * @return {String} - either the entire known storage or a single value */ - fromCache (self, address) { - if (!self.storageByAddress[address]) { - return null - } - return self.storageByAddress[address] + fromCache (self, address) { + if (!self.storageByAddress[address]) { + return null } + return self.storageByAddress[address] + } - /** + /** * store the result of `storageRangeAtInternal` * * @param {String} address - contract address * @param {Object} storage - result of `storageRangeAtInternal`, contains {key, hashedKey, value} */ - toCache (self, address, storage) { - if (!self.storageByAddress[address]) { - self.storageByAddress[address] = {} - } - self.storageByAddress[address].storage = Object.assign(self.storageByAddress[address].storage || {}, storage) + toCache (self, address, storage) { + if (!self.storageByAddress[address]) { + self.storageByAddress[address] = {} } + self.storageByAddress[address].storage = Object.assign(self.storageByAddress[address].storage || {}, storage) + } - storageRangeWeb3Call (tx, address, start, maxSize): Promise> { - return new Promise((resolve, reject) => { - if (isContractCreation(address)) { - resolve([{}, null]) + storageRangeWeb3Call (tx, address, start, maxSize): Promise> { + return new Promise((resolve, reject) => { + if (isContractCreation(address)) { + resolve([{}, null]) + } else { + this.web3.debug.storageRangeAt( + tx.blockHash, tx.transactionIndex, + address, + start, + maxSize, + (error, result) => { + if (error) { + reject(error) + } else if (result.storage) { + resolve([result.storage, result.nextKey]) } else { - this.web3.debug.storageRangeAt( - tx.blockHash, tx.transactionIndex, - address, - start, - maxSize, - (error, result) => { - if (error) { - reject(error) - } else if (result.storage) { - resolve([result.storage, result.nextKey]) - } else { - reject(new Error('the storage has not been provided')) - } - }) + reject(new Error('the storage has not been provided')) } - }) - } + }) + } + }) + } } diff --git a/libs/remix-debug/src/storage/storageViewer.ts b/libs/remix-debug/src/storage/storageViewer.ts index ee174d3123..ae0a9cadaf 100644 --- a/libs/remix-debug/src/storage/storageViewer.ts +++ b/libs/remix-debug/src/storage/storageViewer.ts @@ -8,100 +8,100 @@ import { decodeMappingsKeys } from './mappingPreimages' * (TODO: one instance need to be shared over all the components) */ export class StorageViewer { - context - storageResolver - web3 - initialMappingsLocationPromise - currentMappingsLocationPromise - storageChanges - mappingsLocationChanges + context + storageResolver + web3 + initialMappingsLocationPromise + currentMappingsLocationPromise + storageChanges + mappingsLocationChanges - constructor (_context, _storageResolver, _traceManager) { - this.context = _context - this.storageResolver = _storageResolver - this.web3 = this.storageResolver.web3 - this.initialMappingsLocationPromise = null - this.currentMappingsLocationPromise = null - this.storageChanges = _traceManager.accumulateStorageChanges(this.context.stepIndex, this.context.address, {}) - } + constructor (_context, _storageResolver, _traceManager) { + this.context = _context + this.storageResolver = _storageResolver + this.web3 = this.storageResolver.web3 + this.initialMappingsLocationPromise = null + this.currentMappingsLocationPromise = null + this.storageChanges = _traceManager.accumulateStorageChanges(this.context.stepIndex, this.context.address, {}) + } - /** + /** * return the storage for the current context (address and vm trace index) * by default now returns the range 0 => 1000 * * @param {Function} - callback - contains a map: [hashedKey] = {key, hashedKey, value} */ - storageRange () { - return new Promise((resolve, reject) => { - this.storageResolver.storageRange(this.context.tx, this.context.stepIndex, this.context.address).then((storage) => { - resolve(Object.assign({}, storage, this.storageChanges)) - }).catch(reject) - }) - } + storageRange () { + return new Promise((resolve, reject) => { + this.storageResolver.storageRange(this.context.tx, this.context.stepIndex, this.context.address).then((storage) => { + resolve(Object.assign({}, storage, this.storageChanges)) + }).catch(reject) + }) + } - /** + /** * return a slot value for the current context (address and vm trace index) * @param {String} - slot - slot key (not hashed key!) * @param {Function} - callback - {key, hashedKey, value} - */ - storageSlot (slot, callback) { - const hashed = util.sha3_256(slot) - if (this.storageChanges[hashed]) { - return callback(null, this.storageChanges[hashed]) - } - this.storageResolver.storageSlot(hashed, this.context.tx, this.context.stepIndex, this.context.address).then((storage) => { - callback(null, storage) - }).catch(callback) + storageSlot (slot, callback) { + const hashed = util.sha3_256(slot) + if (this.storageChanges[hashed]) { + return callback(null, this.storageChanges[hashed]) } + this.storageResolver.storageSlot(hashed, this.context.tx, this.context.stepIndex, this.context.address).then((storage) => { + callback(null, storage) + }).catch(callback) + } - /** + /** * return True if the storage at @arg address is complete * * @param {String} address - contract address * @return {Bool} - return True if the storage at @arg address is complete */ - isComplete (address) { - return this.storageResolver.isComplete(address) - } + isComplete (address) { + return this.storageResolver.isComplete(address) + } - /** + /** * return all the possible mappings locations for the current context (cached) do not return state changes during the current transaction * * @param {Array} corrections - used in case the calculated sha3 has been modifyed before SSTORE (notably used for struct in mapping). */ - async initialMappingsLocation (corrections) { - if (!this.initialMappingsLocationPromise) { - this.initialMappingsLocationPromise = this.storageResolver.initialPreimagesMappings(this.context.tx, this.context.stepIndex, this.context.address, corrections) - } - return this.initialMappingsLocationPromise + async initialMappingsLocation (corrections) { + if (!this.initialMappingsLocationPromise) { + this.initialMappingsLocationPromise = this.storageResolver.initialPreimagesMappings(this.context.tx, this.context.stepIndex, this.context.address, corrections) } + return this.initialMappingsLocationPromise + } - /** + /** * return all the possible mappings locations for the current context (cached) and current mapping slot. returns state changes during the current transaction * * @param {Array} corrections - used in case the calculated sha3 has been modifyed before SSTORE (notably used for struct in mapping). */ - async mappingsLocation (corrections) { - if (!this.currentMappingsLocationPromise) { - this.currentMappingsLocationPromise = new Promise((resolve, reject) => { - const mappingsLocationChanges = this.extractMappingsLocationChanges(this.storageChanges, corrections) - return resolve(mappingsLocationChanges) - }) - } - return this.currentMappingsLocationPromise + async mappingsLocation (corrections) { + if (!this.currentMappingsLocationPromise) { + this.currentMappingsLocationPromise = new Promise((resolve, reject) => { + const mappingsLocationChanges = this.extractMappingsLocationChanges(this.storageChanges, corrections) + return resolve(mappingsLocationChanges) + }) } + return this.currentMappingsLocationPromise + } - /** + /** * retrieve mapping location changes from the storage changes. * @param {Map} storageChanges * @param {Array} corrections - used in case the calculated sha3 has been modifyed before SSTORE (notably used for struct in mapping). */ - extractMappingsLocationChanges (storageChanges, corrections) { - if (this.mappingsLocationChanges) { - return this.mappingsLocationChanges - } - const mappings = decodeMappingsKeys(this.web3, storageChanges, corrections) - this.mappingsLocationChanges = mappings - return this.mappingsLocationChanges + extractMappingsLocationChanges (storageChanges, corrections) { + if (this.mappingsLocationChanges) { + return this.mappingsLocationChanges } + const mappings = decodeMappingsKeys(this.web3, storageChanges, corrections) + this.mappingsLocationChanges = mappings + return this.mappingsLocationChanges + } } diff --git a/libs/remix-debug/src/trace/traceAnalyser.ts b/libs/remix-debug/src/trace/traceAnalyser.ts index e4183f9942..3fbc2dde20 100644 --- a/libs/remix-debug/src/trace/traceAnalyser.ts +++ b/libs/remix-debug/src/trace/traceAnalyser.ts @@ -4,152 +4,152 @@ const { toHexPaddedString } = util import * as traceHelper from './traceHelper' export class TraceAnalyser { - traceCache - trace + traceCache + trace - constructor (_cache) { - this.traceCache = _cache - this.trace = null - } + constructor (_cache) { + this.traceCache = _cache + this.trace = null + } - analyse (trace, tx) { - this.trace = trace - this.traceCache.pushStoreChanges(0, tx.to) - let context = { - storageContext: [tx.to], - currentCallIndex: 0, - lastCallIndex: 0 - } - const callStack = [tx.to] - this.traceCache.pushCall(trace[0], 0, callStack[0], callStack.slice(0)) - if (traceHelper.isContractCreation(tx.to)) { - this.traceCache.pushContractCreation(tx.to, tx.input) - } - this.buildCalldata(0, this.trace[0], tx, true) - for (let k = 0; k < this.trace.length; k++) { - const step = this.trace[k] - this.buildMemory(k, step) - context = this.buildDepth(k, step, tx, callStack, context) - context = this.buildStorage(k, step, context) - this.buildReturnValues(k, step) - } - return true + analyse (trace, tx) { + this.trace = trace + this.traceCache.pushStoreChanges(0, tx.to) + let context = { + storageContext: [tx.to], + currentCallIndex: 0, + lastCallIndex: 0 } + const callStack = [tx.to] + this.traceCache.pushCall(trace[0], 0, callStack[0], callStack.slice(0)) + if (traceHelper.isContractCreation(tx.to)) { + this.traceCache.pushContractCreation(tx.to, tx.input) + } + this.buildCalldata(0, this.trace[0], tx, true) + for (let k = 0; k < this.trace.length; k++) { + const step = this.trace[k] + this.buildMemory(k, step) + context = this.buildDepth(k, step, tx, callStack, context) + context = this.buildStorage(k, step, context) + this.buildReturnValues(k, step) + } + return true + } - buildReturnValues (index, step) { - if (traceHelper.isReturnInstruction(step)) { - let offset = 2 * parseInt(toHexPaddedString(step.stack[step.stack.length - 1]), 16) - const size = 2 * parseInt(toHexPaddedString(step.stack[step.stack.length - 2]), 16) - const memory = this.trace[this.traceCache.memoryChanges[this.traceCache.memoryChanges.length - 1]].memory - const noOfReturnParams = size / 64 - const memoryInString = memory.join('') - const returnParamsObj = [] - for (let i = 0; i < noOfReturnParams; i++) { - returnParamsObj.push('0x' + memoryInString.substring(offset, offset + 64)) - offset += 64 - } + buildReturnValues (index, step) { + if (traceHelper.isReturnInstruction(step)) { + let offset = 2 * parseInt(toHexPaddedString(step.stack[step.stack.length - 1]), 16) + const size = 2 * parseInt(toHexPaddedString(step.stack[step.stack.length - 2]), 16) + const memory = this.trace[this.traceCache.memoryChanges[this.traceCache.memoryChanges.length - 1]].memory + const noOfReturnParams = size / 64 + const memoryInString = memory.join('') + const returnParamsObj = [] + for (let i = 0; i < noOfReturnParams; i++) { + returnParamsObj.push('0x' + memoryInString.substring(offset, offset + 64)) + offset += 64 + } - this.traceCache.pushReturnValue(index, returnParamsObj) - } - if (traceHelper.isReturnInstruction(step) || traceHelper.isStopInstruction(step) || traceHelper.isRevertInstruction(step)) { - this.traceCache.pushStopIndex(index, this.traceCache.currentCall.call.address) - } + this.traceCache.pushReturnValue(index, returnParamsObj) + } + if (traceHelper.isReturnInstruction(step) || traceHelper.isStopInstruction(step) || traceHelper.isRevertInstruction(step)) { + this.traceCache.pushStopIndex(index, this.traceCache.currentCall.call.address) + } - try { - if (parseInt(step.gas) - parseInt(step.gasCost) <= 0 || step.error === 'OutOfGas') { - this.traceCache.pushOutOfGasIndex(index, this.traceCache.currentCall.call.address) - } - } catch (e) { - console.error(e) - } + try { + if (parseInt(step.gas) - parseInt(step.gasCost) <= 0 || step.error === 'OutOfGas') { + this.traceCache.pushOutOfGasIndex(index, this.traceCache.currentCall.call.address) + } + } catch (e) { + console.error(e) } + } - buildCalldata (index, step, tx, newContext) { - let calldata = '' - if (index === 0) { - calldata = tx.input - this.traceCache.pushCallDataChanges(index, calldata) - } else if (!newContext) { - const lastCall = this.traceCache.callsData[this.traceCache.callDataChanges[this.traceCache.callDataChanges.length - 2]] - this.traceCache.pushCallDataChanges(index + 1, lastCall) - } else { - const memory = this.trace[this.traceCache.memoryChanges[this.traceCache.memoryChanges.length - 1]].memory - const callStep = this.trace[index] - const stack = callStep.stack - let offset = 0 - let size = 0 - if (callStep.op === 'DELEGATECALL') { - offset = 2 * parseInt(toHexPaddedString(stack[stack.length - 3]), 16) - size = 2 * parseInt(toHexPaddedString(stack[stack.length - 4]), 16) - } else { - offset = 2 * parseInt(toHexPaddedString(stack[stack.length - 4]), 16) - size = 2 * parseInt(toHexPaddedString(stack[stack.length - 5]), 16) - } - calldata = '0x' + memory.join('').substr(offset, size) - this.traceCache.pushCallDataChanges(index + 1, calldata) - } + buildCalldata (index, step, tx, newContext) { + let calldata = '' + if (index === 0) { + calldata = tx.input + this.traceCache.pushCallDataChanges(index, calldata) + } else if (!newContext) { + const lastCall = this.traceCache.callsData[this.traceCache.callDataChanges[this.traceCache.callDataChanges.length - 2]] + this.traceCache.pushCallDataChanges(index + 1, lastCall) + } else { + const memory = this.trace[this.traceCache.memoryChanges[this.traceCache.memoryChanges.length - 1]].memory + const callStep = this.trace[index] + const stack = callStep.stack + let offset = 0 + let size = 0 + if (callStep.op === 'DELEGATECALL') { + offset = 2 * parseInt(toHexPaddedString(stack[stack.length - 3]), 16) + size = 2 * parseInt(toHexPaddedString(stack[stack.length - 4]), 16) + } else { + offset = 2 * parseInt(toHexPaddedString(stack[stack.length - 4]), 16) + size = 2 * parseInt(toHexPaddedString(stack[stack.length - 5]), 16) + } + calldata = '0x' + memory.join('').substr(offset, size) + this.traceCache.pushCallDataChanges(index + 1, calldata) } + } - buildMemory (index, step) { - if (step.memory) { - this.traceCache.pushMemoryChanges(index) - } + buildMemory (index, step) { + if (step.memory) { + this.traceCache.pushMemoryChanges(index) } + } - buildStorage (index, step, context) { - if (traceHelper.newContextStorage(step) && !traceHelper.isCallToPrecompiledContract(index, this.trace)) { - const calledAddress = traceHelper.resolveCalledAddress(index, this.trace) - if (calledAddress) { - context.storageContext.push(calledAddress) - } else { - console.log('unable to build storage changes. ' + index + ' does not match with a CALL. storage changes will be corrupted') - } - this.traceCache.pushStoreChanges(index + 1, context.storageContext[context.storageContext.length - 1]) - } else if (traceHelper.isSSTOREInstruction(step)) { - this.traceCache.pushStoreChanges(index + 1, context.storageContext[context.storageContext.length - 1], toHexPaddedString(step.stack[step.stack.length - 1]), toHexPaddedString(step.stack[step.stack.length - 2])) - } else if (traceHelper.isReturnInstruction(step) || traceHelper.isStopInstruction(step)) { - context.storageContext.pop() - this.traceCache.pushStoreChanges(index + 1, context.storageContext[context.storageContext.length - 1]) - } else if (traceHelper.isRevertInstruction(step)) { - context.storageContext.pop() - this.traceCache.resetStoreChanges() - } - return context + buildStorage (index, step, context) { + if (traceHelper.newContextStorage(step) && !traceHelper.isCallToPrecompiledContract(index, this.trace)) { + const calledAddress = traceHelper.resolveCalledAddress(index, this.trace) + if (calledAddress) { + context.storageContext.push(calledAddress) + } else { + console.log('unable to build storage changes. ' + index + ' does not match with a CALL. storage changes will be corrupted') + } + this.traceCache.pushStoreChanges(index + 1, context.storageContext[context.storageContext.length - 1]) + } else if (traceHelper.isSSTOREInstruction(step)) { + this.traceCache.pushStoreChanges(index + 1, context.storageContext[context.storageContext.length - 1], toHexPaddedString(step.stack[step.stack.length - 1]), toHexPaddedString(step.stack[step.stack.length - 2])) + } else if (traceHelper.isReturnInstruction(step) || traceHelper.isStopInstruction(step)) { + context.storageContext.pop() + this.traceCache.pushStoreChanges(index + 1, context.storageContext[context.storageContext.length - 1]) + } else if (traceHelper.isRevertInstruction(step)) { + context.storageContext.pop() + this.traceCache.resetStoreChanges() } + return context + } - buildDepth (index, step, tx, callStack, context) { - if (traceHelper.isCallInstruction(step) && !traceHelper.isCallToPrecompiledContract(index, this.trace)) { - let newAddress - if (traceHelper.isCreateInstruction(step)) { - newAddress = traceHelper.contractCreationToken(index) - callStack.push(newAddress) - const lastMemoryChange = this.traceCache.memoryChanges[this.traceCache.memoryChanges.length - 1] - this.traceCache.pushContractCreationFromMemory(index, newAddress, this.trace, lastMemoryChange) - } else { - newAddress = traceHelper.resolveCalledAddress(index, this.trace) - if (newAddress) { - callStack.push(newAddress) - } else { - console.log('unable to build depth changes. ' + index + ' does not match with a CALL. depth changes will be corrupted') - } - } - this.traceCache.pushCall(step, index + 1, newAddress, callStack.slice(0)) - this.buildCalldata(index, step, tx, true) - this.traceCache.pushSteps(index, context.currentCallIndex) - context.lastCallIndex = context.currentCallIndex - context.currentCallIndex = 0 - } else if (traceHelper.isReturnInstruction(step) || traceHelper.isStopInstruction(step) || step.error || step.invalidDepthChange) { - if (index < this.trace.length) { - callStack.pop() - this.traceCache.pushCall(step, index + 1, null, callStack.slice(0), step.error || step.invalidDepthChange) - this.buildCalldata(index, step, tx, false) - this.traceCache.pushSteps(index, context.currentCallIndex) - context.currentCallIndex = context.lastCallIndex + 1 - } + buildDepth (index, step, tx, callStack, context) { + if (traceHelper.isCallInstruction(step) && !traceHelper.isCallToPrecompiledContract(index, this.trace)) { + let newAddress + if (traceHelper.isCreateInstruction(step)) { + newAddress = traceHelper.contractCreationToken(index) + callStack.push(newAddress) + const lastMemoryChange = this.traceCache.memoryChanges[this.traceCache.memoryChanges.length - 1] + this.traceCache.pushContractCreationFromMemory(index, newAddress, this.trace, lastMemoryChange) + } else { + newAddress = traceHelper.resolveCalledAddress(index, this.trace) + if (newAddress) { + callStack.push(newAddress) } else { - this.traceCache.pushSteps(index, context.currentCallIndex) - context.currentCallIndex++ + console.log('unable to build depth changes. ' + index + ' does not match with a CALL. depth changes will be corrupted') } - return context + } + this.traceCache.pushCall(step, index + 1, newAddress, callStack.slice(0)) + this.buildCalldata(index, step, tx, true) + this.traceCache.pushSteps(index, context.currentCallIndex) + context.lastCallIndex = context.currentCallIndex + context.currentCallIndex = 0 + } else if (traceHelper.isReturnInstruction(step) || traceHelper.isStopInstruction(step) || step.error || step.invalidDepthChange) { + if (index < this.trace.length) { + callStack.pop() + this.traceCache.pushCall(step, index + 1, null, callStack.slice(0), step.error || step.invalidDepthChange) + this.buildCalldata(index, step, tx, false) + this.traceCache.pushSteps(index, context.currentCallIndex) + context.currentCallIndex = context.lastCallIndex + 1 + } + } else { + this.traceCache.pushSteps(index, context.currentCallIndex) + context.currentCallIndex++ } + return context + } } diff --git a/libs/remix-debug/src/trace/traceCache.ts b/libs/remix-debug/src/trace/traceCache.ts index 7c6f115277..23159b5e43 100644 --- a/libs/remix-debug/src/trace/traceCache.ts +++ b/libs/remix-debug/src/trace/traceCache.ts @@ -5,144 +5,144 @@ const { toHexPaddedString } = util const { sha3_256 } = util export class TraceCache { - returnValues - stopIndexes - outofgasIndexes - currentCall - callsTree - callsData - contractCreation - steps - addresses - callDataChanges - memoryChanges - storageChanges - sstore - formattedMemory - - constructor () { - this.init() + returnValues + stopIndexes + outofgasIndexes + currentCall + callsTree + callsData + contractCreation + steps + addresses + callDataChanges + memoryChanges + storageChanges + sstore + formattedMemory + + constructor () { + this.init() + } + + init () { + // ...Changes contains index in the vmtrace of the corresponding changes + + this.returnValues = {} + this.stopIndexes = [] + this.outofgasIndexes = [] + this.currentCall = null + this.callsTree = null + this.callsData = {} + this.contractCreation = {} + this.steps = {} + this.addresses = [] + this.callDataChanges = [] + this.memoryChanges = [] + this.formattedMemory = {} + this.storageChanges = [] + this.sstore = {} // all sstore occurence in the trace + } + + pushSteps (index, currentCallIndex) { + this.steps[index] = currentCallIndex + } + + pushCallDataChanges (value, calldata) { + this.callDataChanges.push(value) + this.callsData[value] = calldata + } + + pushMemoryChanges (value) { + this.memoryChanges.push(value) + } + + setFormattedMemory (stepIndex, memory) { + this.formattedMemory[stepIndex] = memory + } + + // outOfGas has been removed because gas left logging is apparently made differently + // in the vm/geth/eth. TODO add the error property (with about the error in all clients) + pushCall (step, index, address, callStack, reverted) { + const validReturnStep = step.op === 'RETURN' || step.op === 'STOP' + if ((validReturnStep || reverted) && (this.currentCall)) { + this.currentCall.call.return = index - 1 + if (!validReturnStep) { + this.currentCall.call.reverted = reverted + } + const parent = this.currentCall.parent + if (parent) this.currentCall = { call: parent.call, parent: parent.parent } + return } - - init () { - // ...Changes contains index in the vmtrace of the corresponding changes - - this.returnValues = {} - this.stopIndexes = [] - this.outofgasIndexes = [] - this.currentCall = null - this.callsTree = null - this.callsData = {} - this.contractCreation = {} - this.steps = {} - this.addresses = [] - this.callDataChanges = [] - this.memoryChanges = [] - this.formattedMemory = {} - this.storageChanges = [] - this.sstore = {} // all sstore occurence in the trace - } - - pushSteps (index, currentCallIndex) { - this.steps[index] = currentCallIndex - } - - pushCallDataChanges (value, calldata) { - this.callDataChanges.push(value) - this.callsData[value] = calldata - } - - pushMemoryChanges (value) { - this.memoryChanges.push(value) - } - - setFormattedMemory (stepIndex, memory) { - this.formattedMemory[stepIndex] = memory - } - - // outOfGas has been removed because gas left logging is apparently made differently - // in the vm/geth/eth. TODO add the error property (with about the error in all clients) - pushCall (step, index, address, callStack, reverted) { - const validReturnStep = step.op === 'RETURN' || step.op === 'STOP' - if ((validReturnStep || reverted) && (this.currentCall)) { - this.currentCall.call.return = index - 1 - if (!validReturnStep) { - this.currentCall.call.reverted = reverted - } - const parent = this.currentCall.parent - if (parent) this.currentCall = { call: parent.call, parent: parent.parent } - return - } - const call = { - op: step.op, - address: address, - callStack: callStack, - calls: {}, - start: index - } - this.addresses.push(address) - if (this.currentCall) { - this.currentCall.call.calls[index] = call - } else { - this.callsTree = { call: call } - } - this.currentCall = { call: call, parent: this.currentCall } - } - - pushOutOfGasIndex (index, address) { - this.outofgasIndexes.push({ index, address }) - } - - pushStopIndex (index, address) { - this.stopIndexes.push({ index, address }) - } - - pushReturnValue (step, value) { - this.returnValues[step] = value + const call = { + op: step.op, + address: address, + callStack: callStack, + calls: {}, + start: index } - - pushContractCreationFromMemory (index, token, trace, lastMemoryChange) { - const memory = trace[lastMemoryChange].memory - const stack = trace[index].stack - const offset = 2 * parseInt(toHexPaddedString(stack[stack.length - 2]), 16) - const size = 2 * parseInt(toHexPaddedString(stack[stack.length - 3]), 16) - this.contractCreation[token] = '0x' + memory.join('').substr(offset, size) + this.addresses.push(address) + if (this.currentCall) { + this.currentCall.call.calls[index] = call + } else { + this.callsTree = { call: call } } - - pushContractCreation (token, code) { - this.contractCreation[token] = code + this.currentCall = { call: call, parent: this.currentCall } + } + + pushOutOfGasIndex (index, address) { + this.outofgasIndexes.push({ index, address }) + } + + pushStopIndex (index, address) { + this.stopIndexes.push({ index, address }) + } + + pushReturnValue (step, value) { + this.returnValues[step] = value + } + + pushContractCreationFromMemory (index, token, trace, lastMemoryChange) { + const memory = trace[lastMemoryChange].memory + const stack = trace[index].stack + const offset = 2 * parseInt(toHexPaddedString(stack[stack.length - 2]), 16) + const size = 2 * parseInt(toHexPaddedString(stack[stack.length - 3]), 16) + this.contractCreation[token] = '0x' + memory.join('').substr(offset, size) + } + + pushContractCreation (token, code) { + this.contractCreation[token] = code + } + + resetStoreChanges (index, address, key, value) { + this.sstore = {} + this.storageChanges = [] + } + + pushStoreChanges (index, address, key, value) { + this.sstore[index] = { + address: address, + key: key, + value: value, + hashedKey: key && sha3_256(key) } - - resetStoreChanges (index, address, key, value) { - this.sstore = {} - this.storageChanges = [] - } - - pushStoreChanges (index, address, key, value) { - this.sstore[index] = { - address: address, - key: key, - value: value, - hashedKey: key && sha3_256(key) - } - this.storageChanges.push(index) - } - - accumulateStorageChanges (index, address, storage) { - const ret = Object.assign({}, storage) - for (const k in this.storageChanges) { - const changesIndex = this.storageChanges[k] - if (changesIndex > index) { - return ret - } - const sstore = this.sstore[changesIndex] - if (sstore.address === address && sstore.key) { - ret[sstore.hashedKey] = { - key: sstore.key, - value: sstore.value - } - } - } + this.storageChanges.push(index) + } + + accumulateStorageChanges (index, address, storage) { + const ret = Object.assign({}, storage) + for (const k in this.storageChanges) { + const changesIndex = this.storageChanges[k] + if (changesIndex > index) { return ret + } + const sstore = this.sstore[changesIndex] + if (sstore.address === address && sstore.key) { + ret[sstore.hashedKey] = { + key: sstore.key, + value: sstore.value + } + } } + return ret + } } diff --git a/libs/remix-debug/src/trace/traceHelper.ts b/libs/remix-debug/src/trace/traceHelper.ts index 0f49bde110..d1800b73bf 100644 --- a/libs/remix-debug/src/trace/traceHelper.ts +++ b/libs/remix-debug/src/trace/traceHelper.ts @@ -5,65 +5,65 @@ const { ui } = helpers // vmTraceIndex has to point to a CALL, CODECALL, ... export function resolveCalledAddress (vmTraceIndex, trace) { - const step = trace[vmTraceIndex] - if (isCreateInstruction(step)) { - return contractCreationToken(vmTraceIndex) - } else if (isCallInstruction(step)) { - const stack = step.stack // callcode, delegatecall, ... - return ui.normalizeHexAddress(toHexPaddedString(stack[stack.length - 2])) - } - return undefined + const step = trace[vmTraceIndex] + if (isCreateInstruction(step)) { + return contractCreationToken(vmTraceIndex) + } else if (isCallInstruction(step)) { + const stack = step.stack // callcode, delegatecall, ... + return ui.normalizeHexAddress(toHexPaddedString(stack[stack.length - 2])) + } + return undefined } export function isCallInstruction (step) { - return ['CALL', 'STATICCALL', 'CALLCODE', 'CREATE', 'DELEGATECALL', 'CREATE2'].includes(step.op) + return ['CALL', 'STATICCALL', 'CALLCODE', 'CREATE', 'DELEGATECALL', 'CREATE2'].includes(step.op) } export function isCreateInstruction (step) { - return step.op === 'CREATE' || step.op === 'CREATE2' + return step.op === 'CREATE' || step.op === 'CREATE2' } export function isReturnInstruction (step) { - return step.op === 'RETURN' + return step.op === 'RETURN' } export function isJumpDestInstruction (step) { - return step.op === 'JUMPDEST' + return step.op === 'JUMPDEST' } export function isStopInstruction (step) { - return step.op === 'STOP' + return step.op === 'STOP' } export function isRevertInstruction (step) { - return step.op === 'REVERT' + return step.op === 'REVERT' } export function isSSTOREInstruction (step) { - return step.op === 'SSTORE' + return step.op === 'SSTORE' } export function isSHA3Instruction (step) { - return step.op === 'SHA3' + return step.op === 'SHA3' } export function newContextStorage (step) { - return step.op === 'CREATE' || step.op === 'CALL' || step.op === 'CREATE2' + return step.op === 'CREATE' || step.op === 'CALL' || step.op === 'CREATE2' } export function isCallToPrecompiledContract (index, trace) { - // if stack empty => this is not a precompiled contract - const step = trace[index] - if (isCallInstruction(step)) { - return index + 1 < trace.length && trace[index + 1].stack.length !== 0 - } - return false + // if stack empty => this is not a precompiled contract + const step = trace[index] + if (isCallInstruction(step)) { + return index + 1 < trace.length && trace[index + 1].stack.length !== 0 + } + return false } export function contractCreationToken (index) { - return '(Contract Creation - Step ' + index + ')' + return '(Contract Creation - Step ' + index + ')' } export function isContractCreation (address) { - return address.indexOf('(Contract Creation - Step') !== -1 + return address.indexOf('(Contract Creation - Step') !== -1 } diff --git a/libs/remix-debug/src/trace/traceManager.ts b/libs/remix-debug/src/trace/traceManager.ts index e15f8e180b..83252c7455 100644 --- a/libs/remix-debug/src/trace/traceManager.ts +++ b/libs/remix-debug/src/trace/traceManager.ts @@ -7,319 +7,319 @@ import { TraceStepManager } from './traceStepManager' import { isCreateInstruction } from './traceHelper' export class TraceManager { - web3 - fork: string - isLoading: boolean - trace - traceCache - traceAnalyser - traceStepManager - tx - - constructor (options) { - this.web3 = options.web3 - this.isLoading = false - this.trace = null - this.traceCache = new TraceCache() - this.traceAnalyser = new TraceAnalyser(this.traceCache) - this.traceStepManager = new TraceStepManager(this.traceAnalyser) - } + web3 + fork: string + isLoading: boolean + trace + traceCache + traceAnalyser + traceStepManager + tx + + constructor (options) { + this.web3 = options.web3 + this.isLoading = false + this.trace = null + this.traceCache = new TraceCache() + this.traceAnalyser = new TraceAnalyser(this.traceCache) + this.traceStepManager = new TraceStepManager(this.traceAnalyser) + } + + // init section + async resolveTrace (tx) { + this.tx = tx + this.init() + if (!this.web3) throw new Error('web3 not loaded') + this.isLoading = true + const result = await this.getTrace(tx.hash) + try { + if (result['structLogs'].length > 0) { + this.trace = result['structLogs'] - // init section - async resolveTrace (tx) { - this.tx = tx - this.init() - if (!this.web3) throw new Error('web3 not loaded') - this.isLoading = true - const result = await this.getTrace(tx.hash) try { - if (result['structLogs'].length > 0) { - this.trace = result['structLogs'] - - try { - const networkId = await this.web3.eth.net.getId() - this.fork = execution.forkAt(networkId, tx.blockNumber) - } catch (e) { - this.fork = 'merge' - console.log(`unable to detect fork, defaulting to ${this.fork}..`) - console.error(e) - } - - this.traceAnalyser.analyse(result['structLogs'], tx) - this.isLoading = false - return true - } - const mes = tx.hash + ' is not a contract invocation or contract creation.' - console.log(mes) - this.isLoading = false - throw new Error(mes) - } catch (error) { - console.log(error) - this.isLoading = false - throw new Error(error) + const networkId = await this.web3.eth.net.getId() + this.fork = execution.forkAt(networkId, tx.blockNumber) + } catch (e) { + this.fork = 'merge' + console.log(`unable to detect fork, defaulting to ${this.fork}..`) + console.error(e) } - } - - getTrace (txHash) { - return new Promise((resolve, reject) => { - const options = { - disableStorage: true, - enableMemory: true, - disableStack: false, - fullStorage: false - } - this.web3.debug.traceTransaction(txHash, options, function (error, result) { - if (error) return reject(error) - resolve(result) - }) - }) - } - - init () { - this.trace = null - this.traceCache.init() - } - - getCurrentFork () { - return this.fork - } - - // API section - inRange (step) { - return this.isLoaded() && step >= 0 && step < this.trace.length - } - - isLoaded () { - return !this.isLoading && this.trace !== null - } - - getLength (callback) { - if (!this.trace) { - callback(new Error('no trace available'), null) - } else { - callback(null, this.trace.length) - } - } - - accumulateStorageChanges (index, address, storageOrigin) { - return this.traceCache.accumulateStorageChanges(index, address, storageOrigin) - } - - getAddresses () { - return this.traceCache.addresses - } - - getCallDataAt (stepIndex) { - try { - this.checkRequestedStep(stepIndex) - } catch (check) { - throw new Error(check) - } - const callDataChange = util.findLowerBoundValue(stepIndex, this.traceCache.callDataChanges) - if (callDataChange === null) { - throw new Error('no calldata found') - } - return [this.traceCache.callsData[callDataChange]] - } - async buildCallPath (stepIndex) { - try { - this.checkRequestedStep(stepIndex) - } catch (check) { - throw new Error(check) - } - const callsPath = util.buildCallPath(stepIndex, this.traceCache.callsTree.call) - if (callsPath === null) throw new Error('no call path built') - return callsPath - } - - getCallStackAt (stepIndex) { - try { - this.checkRequestedStep(stepIndex) - } catch (check) { - throw new Error(check) - } - const call = util.findCall(stepIndex, this.traceCache.callsTree.call) - if (call === null) { - throw new Error('no callstack found') - } - return call.callStack - } - - getStackAt (stepIndex) { - this.checkRequestedStep(stepIndex) - if (this.trace[stepIndex] && this.trace[stepIndex].stack) { // there's always a stack - if (Array.isArray(this.trace[stepIndex].stack)) { - const stack = this.trace[stepIndex].stack.slice(0) - stack.reverse() - return stack.map(el => toHexPaddedString(el)) - } else { - // it's an object coming from the VM. - // for performance reasons, - // we don't turn the stack coming from the VM into an array when the tx is executed - // but now when the app needs it. - const stack = [] - for (const prop in this.trace[stepIndex].stack) { - if (prop !== 'length') { - stack.push(toHexPaddedString(this.trace[stepIndex].stack[prop])) - } - } - stack.reverse() - return stack - } - } else { - throw new Error('no stack found') - } - } - - getLastCallChangeSince (stepIndex) { - try { - this.checkRequestedStep(stepIndex) - } catch (check) { - throw new Error(check) - } - - const callChange = util.findCall(stepIndex, this.traceCache.callsTree.call) - if (callChange === null) { - return 0 - } - return callChange - } - - getCurrentCalledAddressAt (stepIndex) { - try { - this.checkRequestedStep(stepIndex) - const resp = this.getLastCallChangeSince(stepIndex) - if (!resp) { - throw new Error('unable to get current called address. ' + stepIndex + ' does not match with a CALL') - } - return resp.address - } catch (error) { - throw new Error(error) - } - } - - getContractCreationCode (token) { - if (!this.traceCache.contractCreation[token]) { - throw new Error('no contract creation named ' + token) - } - return this.traceCache.contractCreation[token] - } - - getMemoryAt (stepIndex, format = true) { - this.checkRequestedStep(stepIndex) - const lastChanges = util.findLowerBoundValue(stepIndex, this.traceCache.memoryChanges) - if (lastChanges === null) { - throw new Error('no memory found') - } - if (!format) { - return this.trace[lastChanges].memory - } - if (this.traceCache.formattedMemory[lastChanges]) { - return this.traceCache.formattedMemory[lastChanges] - } - const memory = util.formatMemory(this.trace[lastChanges].memory) - this.traceCache.setFormattedMemory(lastChanges, memory) - return memory - } - - getCurrentPC (stepIndex) { - try { - this.checkRequestedStep(stepIndex) - } catch (check) { - throw new Error(check) - } - return this.trace[stepIndex].pc - } - - getAllStopIndexes () { - return this.traceCache.stopIndexes - } - - getAllOutofGasIndexes () { - return this.traceCache.outofgasIndexes - } - - getReturnValue (stepIndex) { - try { - this.checkRequestedStep(stepIndex) - } catch (check) { - throw new Error(check) - } - if (!this.traceCache.returnValues[stepIndex]) { - throw new Error('current step is not a return step') - } - return this.traceCache.returnValues[stepIndex] - } - - getCurrentStep (stepIndex) { - try { - this.checkRequestedStep(stepIndex) - } catch (check) { - throw new Error(check) - } - return this.traceCache.steps[stepIndex] - } - - getMemExpand (stepIndex) { - return (this.getStepProperty(stepIndex, 'memexpand') || '') - } - - getStepCost (stepIndex) { - return this.getStepProperty(stepIndex, 'gasCost') - } - - getRemainingGas (stepIndex) { - return this.getStepProperty(stepIndex, 'gas') - } - - getStepProperty (stepIndex, property) { - try { - this.checkRequestedStep(stepIndex) - } catch (check) { - throw new Error(check) - } - return this.trace[stepIndex][property] - } - - isCreationStep (stepIndex) { - return isCreateInstruction(this.trace[stepIndex]) - } - - // step section - findStepOverBack (currentStep) { - return this.traceStepManager.findStepOverBack(currentStep) - } - - findStepOverForward (currentStep) { - return this.traceStepManager.findStepOverForward(currentStep) - } - - findNextCall (currentStep) { - return this.traceStepManager.findNextCall(currentStep) - } - - findStepOut (currentStep) { - return this.traceStepManager.findStepOut(currentStep) - } - - checkRequestedStep (stepIndex) { - if (!this.trace) { - throw new Error('trace not loaded') - } else if (stepIndex >= this.trace.length) { - throw new Error('trace smaller than requested') - } - } - - waterfall (calls, stepindex, cb) { - const ret = [] - let retError = null - for (const call in calls) { - calls[call].apply(this, [stepindex, function (error, result) { - retError = error - ret.push({ error: error, value: result }) - }]) + this.traceAnalyser.analyse(result['structLogs'], tx) + this.isLoading = false + return true + } + const mes = tx.hash + ' is not a contract invocation or contract creation.' + console.log(mes) + this.isLoading = false + throw new Error(mes) + } catch (error) { + console.log(error) + this.isLoading = false + throw new Error(error) + } + } + + getTrace (txHash) { + return new Promise((resolve, reject) => { + const options = { + disableStorage: true, + enableMemory: true, + disableStack: false, + fullStorage: false + } + this.web3.debug.traceTransaction(txHash, options, function (error, result) { + if (error) return reject(error) + resolve(result) + }) + }) + } + + init () { + this.trace = null + this.traceCache.init() + } + + getCurrentFork () { + return this.fork + } + + // API section + inRange (step) { + return this.isLoaded() && step >= 0 && step < this.trace.length + } + + isLoaded () { + return !this.isLoading && this.trace !== null + } + + getLength (callback) { + if (!this.trace) { + callback(new Error('no trace available'), null) + } else { + callback(null, this.trace.length) + } + } + + accumulateStorageChanges (index, address, storageOrigin) { + return this.traceCache.accumulateStorageChanges(index, address, storageOrigin) + } + + getAddresses () { + return this.traceCache.addresses + } + + getCallDataAt (stepIndex) { + try { + this.checkRequestedStep(stepIndex) + } catch (check) { + throw new Error(check) + } + const callDataChange = util.findLowerBoundValue(stepIndex, this.traceCache.callDataChanges) + if (callDataChange === null) { + throw new Error('no calldata found') + } + return [this.traceCache.callsData[callDataChange]] + } + + async buildCallPath (stepIndex) { + try { + this.checkRequestedStep(stepIndex) + } catch (check) { + throw new Error(check) + } + const callsPath = util.buildCallPath(stepIndex, this.traceCache.callsTree.call) + if (callsPath === null) throw new Error('no call path built') + return callsPath + } + + getCallStackAt (stepIndex) { + try { + this.checkRequestedStep(stepIndex) + } catch (check) { + throw new Error(check) + } + const call = util.findCall(stepIndex, this.traceCache.callsTree.call) + if (call === null) { + throw new Error('no callstack found') + } + return call.callStack + } + + getStackAt (stepIndex) { + this.checkRequestedStep(stepIndex) + if (this.trace[stepIndex] && this.trace[stepIndex].stack) { // there's always a stack + if (Array.isArray(this.trace[stepIndex].stack)) { + const stack = this.trace[stepIndex].stack.slice(0) + stack.reverse() + return stack.map(el => toHexPaddedString(el)) + } else { + // it's an object coming from the VM. + // for performance reasons, + // we don't turn the stack coming from the VM into an array when the tx is executed + // but now when the app needs it. + const stack = [] + for (const prop in this.trace[stepIndex].stack) { + if (prop !== 'length') { + stack.push(toHexPaddedString(this.trace[stepIndex].stack[prop])) + } } - cb(retError, ret) - } + stack.reverse() + return stack + } + } else { + throw new Error('no stack found') + } + } + + getLastCallChangeSince (stepIndex) { + try { + this.checkRequestedStep(stepIndex) + } catch (check) { + throw new Error(check) + } + + const callChange = util.findCall(stepIndex, this.traceCache.callsTree.call) + if (callChange === null) { + return 0 + } + return callChange + } + + getCurrentCalledAddressAt (stepIndex) { + try { + this.checkRequestedStep(stepIndex) + const resp = this.getLastCallChangeSince(stepIndex) + if (!resp) { + throw new Error('unable to get current called address. ' + stepIndex + ' does not match with a CALL') + } + return resp.address + } catch (error) { + throw new Error(error) + } + } + + getContractCreationCode (token) { + if (!this.traceCache.contractCreation[token]) { + throw new Error('no contract creation named ' + token) + } + return this.traceCache.contractCreation[token] + } + + getMemoryAt (stepIndex, format = true) { + this.checkRequestedStep(stepIndex) + const lastChanges = util.findLowerBoundValue(stepIndex, this.traceCache.memoryChanges) + if (lastChanges === null) { + throw new Error('no memory found') + } + if (!format) { + return this.trace[lastChanges].memory + } + if (this.traceCache.formattedMemory[lastChanges]) { + return this.traceCache.formattedMemory[lastChanges] + } + const memory = util.formatMemory(this.trace[lastChanges].memory) + this.traceCache.setFormattedMemory(lastChanges, memory) + return memory + } + + getCurrentPC (stepIndex) { + try { + this.checkRequestedStep(stepIndex) + } catch (check) { + throw new Error(check) + } + return this.trace[stepIndex].pc + } + + getAllStopIndexes () { + return this.traceCache.stopIndexes + } + + getAllOutofGasIndexes () { + return this.traceCache.outofgasIndexes + } + + getReturnValue (stepIndex) { + try { + this.checkRequestedStep(stepIndex) + } catch (check) { + throw new Error(check) + } + if (!this.traceCache.returnValues[stepIndex]) { + throw new Error('current step is not a return step') + } + return this.traceCache.returnValues[stepIndex] + } + + getCurrentStep (stepIndex) { + try { + this.checkRequestedStep(stepIndex) + } catch (check) { + throw new Error(check) + } + return this.traceCache.steps[stepIndex] + } + + getMemExpand (stepIndex) { + return (this.getStepProperty(stepIndex, 'memexpand') || '') + } + + getStepCost (stepIndex) { + return this.getStepProperty(stepIndex, 'gasCost') + } + + getRemainingGas (stepIndex) { + return this.getStepProperty(stepIndex, 'gas') + } + + getStepProperty (stepIndex, property) { + try { + this.checkRequestedStep(stepIndex) + } catch (check) { + throw new Error(check) + } + return this.trace[stepIndex][property] + } + + isCreationStep (stepIndex) { + return isCreateInstruction(this.trace[stepIndex]) + } + + // step section + findStepOverBack (currentStep) { + return this.traceStepManager.findStepOverBack(currentStep) + } + + findStepOverForward (currentStep) { + return this.traceStepManager.findStepOverForward(currentStep) + } + + findNextCall (currentStep) { + return this.traceStepManager.findNextCall(currentStep) + } + + findStepOut (currentStep) { + return this.traceStepManager.findStepOut(currentStep) + } + + checkRequestedStep (stepIndex) { + if (!this.trace) { + throw new Error('trace not loaded') + } else if (stepIndex >= this.trace.length) { + throw new Error('trace smaller than requested') + } + } + + waterfall (calls, stepindex, cb) { + const ret = [] + let retError = null + for (const call in calls) { + calls[call].apply(this, [stepindex, function (error, result) { + retError = error + ret.push({ error: error, value: result }) + }]) + } + cb(retError, ret) + } } diff --git a/libs/remix-debug/src/trace/traceStepManager.ts b/libs/remix-debug/src/trace/traceStepManager.ts index bcbccfe2e1..56f2069769 100644 --- a/libs/remix-debug/src/trace/traceStepManager.ts +++ b/libs/remix-debug/src/trace/traceStepManager.ts @@ -4,53 +4,53 @@ import { isCallInstruction, isCallToPrecompiledContract, isReturnInstruction } f import { util } from '@remix-project/remix-lib' export class TraceStepManager { - traceAnalyser - - constructor (_traceAnalyser) { - this.traceAnalyser = _traceAnalyser + traceAnalyser + + constructor (_traceAnalyser) { + this.traceAnalyser = _traceAnalyser + } + + isCallInstruction (index) { + const state = this.traceAnalyser.trace[index] + return isCallInstruction(state) && !isCallToPrecompiledContract(index, this.traceAnalyser.trace) + } + + isReturnInstruction (index) { + const state = this.traceAnalyser.trace[index] + return isReturnInstruction(state) + } + + findStepOverBack (currentStep) { + if (this.isReturnInstruction(currentStep)) { + const call = util.findCall(currentStep, this.traceAnalyser.traceCache.callsTree.call) + return call.start > 0 ? call.start - 1 : 0 } + return currentStep > 0 ? currentStep - 1 : 0 + } - isCallInstruction (index) { - const state = this.traceAnalyser.trace[index] - return isCallInstruction(state) && !isCallToPrecompiledContract(index, this.traceAnalyser.trace) + findStepOverForward (currentStep) { + if (this.isCallInstruction(currentStep)) { + const call = util.findCall(currentStep + 1, this.traceAnalyser.traceCache.callsTree.call) + return call.return + 1 < this.traceAnalyser.trace.length ? call.return + 1 : this.traceAnalyser.trace.length - 1 } - - isReturnInstruction (index) { - const state = this.traceAnalyser.trace[index] - return isReturnInstruction(state) + return this.traceAnalyser.trace.length >= currentStep + 1 ? currentStep + 1 : currentStep + } + + findNextCall (currentStep) { + const call = util.findCall(currentStep, this.traceAnalyser.traceCache.callsTree.call) + const subCalls = Object.keys(call.calls) + if (subCalls.length) { + const callStart = util.findLowerBound(currentStep, subCalls) + 1 + if (subCalls.length > callStart) { + return parseInt(subCalls[callStart]) - 1 + } + return currentStep } + return currentStep + } - findStepOverBack (currentStep) { - if (this.isReturnInstruction(currentStep)) { - const call = util.findCall(currentStep, this.traceAnalyser.traceCache.callsTree.call) - return call.start > 0 ? call.start - 1 : 0 - } - return currentStep > 0 ? currentStep - 1 : 0 - } - - findStepOverForward (currentStep) { - if (this.isCallInstruction(currentStep)) { - const call = util.findCall(currentStep + 1, this.traceAnalyser.traceCache.callsTree.call) - return call.return + 1 < this.traceAnalyser.trace.length ? call.return + 1 : this.traceAnalyser.trace.length - 1 - } - return this.traceAnalyser.trace.length >= currentStep + 1 ? currentStep + 1 : currentStep - } - - findNextCall (currentStep) { - const call = util.findCall(currentStep, this.traceAnalyser.traceCache.callsTree.call) - const subCalls = Object.keys(call.calls) - if (subCalls.length) { - const callStart = util.findLowerBound(currentStep, subCalls) + 1 - if (subCalls.length > callStart) { - return parseInt(subCalls[callStart]) - 1 - } - return currentStep - } - return currentStep - } - - findStepOut (currentStep) { - const call = util.findCall(currentStep, this.traceAnalyser.traceCache.callsTree.call) - return call.return - } + findStepOut (currentStep) { + const call = util.findCall(currentStep, this.traceAnalyser.traceCache.callsTree.call) + return call.return + } } diff --git a/libs/remix-debug/test.ts b/libs/remix-debug/test.ts index 95a5a44c4c..a7880496dd 100644 --- a/libs/remix-debug/test.ts +++ b/libs/remix-debug/test.ts @@ -9,21 +9,21 @@ const filename = 'test/sol/simple_storage.sol' const shortFilename = 'simple_storage.sol' const inputJson = { - language: 'Solidity', - sources: { + language: 'Solidity', + sources: { + }, + settings: { + optimizer: { + enabled: true, + runs: 200 }, - settings: { - optimizer: { - enabled: true, - runs: 200 - }, - outputSelection: { - '*': { - '': [ 'ast' ], - '*': [ 'abi', 'metadata', 'devdoc', 'userdoc', 'evm.legacyAssembly', 'evm.bytecode', 'evm.deployedBytecode', 'evm.methodIdentifiers', 'evm.gasEstimates' ] - } - } + outputSelection: { + '*': { + '': [ 'ast' ], + '*': [ 'abi', 'metadata', 'devdoc', 'userdoc', 'evm.legacyAssembly', 'evm.bytecode', 'evm.deployedBytecode', 'evm.methodIdentifiers', 'evm.gasEstimates' ] + } } + } } inputJson.sources[shortFilename] = {content: fs.readFileSync(filename).toString()} @@ -65,7 +65,7 @@ const tx = '0xf510c4f0b1d9ee262d7b9e9e87b4262f275fe029c2c733feef7dfa1e2b1e32aa' cmdLine.startDebug(tx, shortFilename) cmdLine.events.on('source', () => { - cmdLine.getSource().forEach(console.dir) + cmdLine.getSource().forEach(console.dir) }) // }) // }) @@ -73,36 +73,36 @@ cmdLine.events.on('source', () => { const repl = require('repl') repl.start({ - prompt: '> ', - eval: (cmd, context, filename, cb) => { - const command = cmd.trim() - if (command === 'next' || command === 'n') { - cmdLine.stepOverForward(true) - } - if (command === 'previous' || command === 'p' || command === 'prev') { - cmdLine.stepOverBack(true) - } - if (command === 'step' || command === 's') { - cmdLine.stepIntoForward(true) - } - if (command === 'stepback' || command === 'sb') { - cmdLine.stepIntoBack(true) - } - if (command === 'exit' || command === 'quit') { - process.exit(0) - } - if (command === 'var local' || command === 'v l' || command === 'vl') { - cmdLine.displayLocals() - } - if (command === 'var global' || command === 'v g' || command === 'vg') { - cmdLine.displayGlobals() - } - if (command.split(' ')[0] === 'jump') { - const stepIndex = parseInt(command.split(' ')[1], 10) - cmdLine.jumpTo(stepIndex) - } - cb(null, '') + prompt: '> ', + eval: (cmd, context, filename, cb) => { + const command = cmd.trim() + if (command === 'next' || command === 'n') { + cmdLine.stepOverForward(true) } + if (command === 'previous' || command === 'p' || command === 'prev') { + cmdLine.stepOverBack(true) + } + if (command === 'step' || command === 's') { + cmdLine.stepIntoForward(true) + } + if (command === 'stepback' || command === 'sb') { + cmdLine.stepIntoBack(true) + } + if (command === 'exit' || command === 'quit') { + process.exit(0) + } + if (command === 'var local' || command === 'v l' || command === 'vl') { + cmdLine.displayLocals() + } + if (command === 'var global' || command === 'v g' || command === 'vg') { + cmdLine.displayGlobals() + } + if (command.split(' ')[0] === 'jump') { + const stepIndex = parseInt(command.split(' ')[1], 10) + cmdLine.jumpTo(stepIndex) + } + cb(null, '') + } }) module.exports = cmdLine diff --git a/libs/remix-debug/test/codeManager.ts b/libs/remix-debug/test/codeManager.ts index 3ba95be60a..ac2dffe45f 100644 --- a/libs/remix-debug/test/codeManager.ts +++ b/libs/remix-debug/test/codeManager.ts @@ -5,68 +5,68 @@ import { CodeManager } from '../src/code/codeManager' const web3Test = require('./resources/testWeb3.ts') tape('CodeManager', function (t) { - const traceManager = new TraceManager({web3: web3Test}) - const codeManager = new CodeManager(traceManager) - const contractCode = web3Test.eth.getCode('0x0d3a18d64dfe4f927832ab58d6451cecc4e517c5') - codeManager.codeResolver.cacheExecutingCode('0x0d3a18d64dfe4f927832ab58d6451cecc4e517c5', contractCode) // so a call to web3 is not necessary - const tx = web3Test.eth.getTransaction('0x20ef65b8b186ca942fcccd634f37074dde49b541c27994fc7596740ef44cfd51') - traceManager.resolveTrace(tx).then(() => { - continueTesting(t, codeManager) - }).catch(() => { - t.fail(' - traceManager.resolveTrace - failed ') - }) + const traceManager = new TraceManager({web3: web3Test}) + const codeManager = new CodeManager(traceManager) + const contractCode = web3Test.eth.getCode('0x0d3a18d64dfe4f927832ab58d6451cecc4e517c5') + codeManager.codeResolver.cacheExecutingCode('0x0d3a18d64dfe4f927832ab58d6451cecc4e517c5', contractCode) // so a call to web3 is not necessary + const tx = web3Test.eth.getTransaction('0x20ef65b8b186ca942fcccd634f37074dde49b541c27994fc7596740ef44cfd51') + traceManager.resolveTrace(tx).then(() => { + continueTesting(t, codeManager) + }).catch(() => { + t.fail(' - traceManager.resolveTrace - failed ') + }) }) function continueTesting (t, codeManager) { - t.test('CodeManager.init', function (st) { - st.end() - }) + t.test('CodeManager.init', function (st) { + st.end() + }) - t.test('CodeManager.resolveStep', function (st) { - st.plan(6) - codeManager.event.register('changed', this, function (code, address, index) { - if (index === undefined || index === null) { - st.fail(index) - } else { - st.ok(index === 6 || index === 0) - } - }) - - codeManager.event.register('changed', this, function (code, address, index) { - if (!code) { - st.fail('no codes') - } else { - st.ok(address === '0x0d3a18d64dfe4f927832ab58d6451cecc4e517c5' || address === '(Contract Creation - Step 63)') - if (address === '0x0d3a18d64dfe4f927832ab58d6451cecc4e517c5') { - console.log(address + ' ' + code[25]) - st.ok(code[25].indexOf('DUP') !== -1) - } else if (address === '(Contract Creation - Step 63)') { - console.log(address + ' ' + code[25]) - st.ok(code[25].indexOf('JUMPDEST') !== -1) - } - } - }) - const tx = web3Test.eth.getTransaction('0x20ef65b8b186ca942fcccd634f37074dde49b541c27994fc7596740ef44cfd51') - codeManager.resolveStep(0, tx) - codeManager.resolveStep(70, tx) + t.test('CodeManager.resolveStep', function (st) { + st.plan(6) + codeManager.event.register('changed', this, function (code, address, index) { + if (index === undefined || index === null) { + st.fail(index) + } else { + st.ok(index === 6 || index === 0) + } }) - t.test('CodeManager.getInstructionIndex', function (st) { - st.plan(2) - try { - const result = codeManager.getInstructionIndex('0x0d3a18d64dfe4f927832ab58d6451cecc4e517c5', 16) - console.log(result) - st.ok(result === 25) - } catch (error) { - st.fail(error) - } - - try { - const result = codeManager.getInstructionIndex('(Contract Creation - Step 63)', 70) - console.log(result) - st.ok(result === 6) - } catch (error) { - st.fail(error) + codeManager.event.register('changed', this, function (code, address, index) { + if (!code) { + st.fail('no codes') + } else { + st.ok(address === '0x0d3a18d64dfe4f927832ab58d6451cecc4e517c5' || address === '(Contract Creation - Step 63)') + if (address === '0x0d3a18d64dfe4f927832ab58d6451cecc4e517c5') { + console.log(address + ' ' + code[25]) + st.ok(code[25].indexOf('DUP') !== -1) + } else if (address === '(Contract Creation - Step 63)') { + console.log(address + ' ' + code[25]) + st.ok(code[25].indexOf('JUMPDEST') !== -1) } + } }) + const tx = web3Test.eth.getTransaction('0x20ef65b8b186ca942fcccd634f37074dde49b541c27994fc7596740ef44cfd51') + codeManager.resolveStep(0, tx) + codeManager.resolveStep(70, tx) + }) + + t.test('CodeManager.getInstructionIndex', function (st) { + st.plan(2) + try { + const result = codeManager.getInstructionIndex('0x0d3a18d64dfe4f927832ab58d6451cecc4e517c5', 16) + console.log(result) + st.ok(result === 25) + } catch (error) { + st.fail(error) + } + + try { + const result = codeManager.getInstructionIndex('(Contract Creation - Step 63)', 70) + console.log(result) + st.ok(result === 6) + } catch (error) { + st.fail(error) + } + }) } \ No newline at end of file diff --git a/libs/remix-debug/test/debugger.ts b/libs/remix-debug/test/debugger.ts index e573f3e137..bd6dec1e68 100644 --- a/libs/remix-debug/test/debugger.ts +++ b/libs/remix-debug/test/debugger.ts @@ -151,149 +151,149 @@ contract Ballot { `; (async () => { - const privateKey = Buffer.from('503f38a9c967ed597e47fe25643985f032b072db8075426a92110f82df48dfcb', 'hex') - let output = compiler.compile(compilerInput(ballot)) - output = JSON.parse(output) - const param = '0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000148656c6c6f20576f726c64210000000000000000000000000000000000000000' - const web3 = await vmCall.getWeb3() - vmCall.sendTx(web3, {nonce: 0, privateKey: privateKey}, null, 0, output.contracts['test.sol']['Ballot'].evm.bytecode.object + param, (error, hash) => { - console.log(error, hash) + const privateKey = Buffer.from('503f38a9c967ed597e47fe25643985f032b072db8075426a92110f82df48dfcb', 'hex') + let output = compiler.compile(compilerInput(ballot)) + output = JSON.parse(output) + const param = '0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000148656c6c6f20576f726c64210000000000000000000000000000000000000000' + const web3 = await vmCall.getWeb3() + vmCall.sendTx(web3, {nonce: 0, privateKey: privateKey}, null, 0, output.contracts['test.sol']['Ballot'].evm.bytecode.object + param, (error, hash) => { + console.log(error, hash) + if (error) { + throw error + } else { + web3.eth.getTransaction(hash, (error, tx) => { if (error) { - throw error + throw error } else { - web3.eth.getTransaction(hash, (error, tx) => { - if (error) { - throw error - } else { - const sources = { - target: 'test.sol', - sources: { 'test.sol': { content: ballot } } - } - const compilationResults = new CompilerAbstract('json', output, sources) - const debugManager = new Debugger({ - compilationResult: () => compilationResults, - web3: web3, - offsetToLineColumnConverter: { - offsetToLineColumn: async (rawLocation) => { - return sourceMappingDecoder.convertOffsetToLineColumn(rawLocation, sourceMappingDecoder.getLinebreakPositions(ballot)) - } - } - }) + const sources = { + target: 'test.sol', + sources: { 'test.sol': { content: ballot } } + } + const compilationResults = new CompilerAbstract('json', output, sources) + const debugManager = new Debugger({ + compilationResult: () => compilationResults, + web3: web3, + offsetToLineColumnConverter: { + offsetToLineColumn: async (rawLocation) => { + return sourceMappingDecoder.convertOffsetToLineColumn(rawLocation, sourceMappingDecoder.getLinebreakPositions(ballot)) + } + } + }) - debugManager.callTree.event.register('callTreeReady', () => { - testDebugging(debugManager) - }) - debugManager.callTree.event.register('callTreeNotReady', (error) => { - console.error(error) - throw error - }) - debugManager.callTree.event.register('callTreeBuildFailed', (error) => { - console.error(error) - throw error - }) + debugManager.callTree.event.register('callTreeReady', () => { + testDebugging(debugManager) + }) + debugManager.callTree.event.register('callTreeNotReady', (error) => { + console.error(error) + throw error + }) + debugManager.callTree.event.register('callTreeBuildFailed', (error) => { + console.error(error) + throw error + }) - debugManager.debug(tx) - } - }) + debugManager.debug(tx) } - }) + }) + } + }) })() function testDebugging (debugManager) { - // stack - tape('traceManager.getStackAt 4', (t) => { - t.plan(1) - try { - const callstack = debugManager.traceManager.getStackAt(4) - t.equal(JSON.stringify(callstack), JSON.stringify(['0x0000000000000000000000000000000000000000000000000000000000000000'])) - } catch (error) { - return t.end(error) - } - }) + // stack + tape('traceManager.getStackAt 4', (t) => { + t.plan(1) + try { + const callstack = debugManager.traceManager.getStackAt(4) + t.equal(JSON.stringify(callstack), JSON.stringify(['0x0000000000000000000000000000000000000000000000000000000000000000'])) + } catch (error) { + return t.end(error) + } + }) - tape('traceManager.getStackAt 41', (t) => { - t.plan(1) - try { - const callstack = debugManager.traceManager.getStackAt(41) - t.equal(JSON.stringify(callstack), JSON.stringify([ - '0x0000000000000000000000000000000000000000000000000000000000000080', - '0x0000000000000000000000000000000000000000000000000000000000000020', - '0x0000000000000000000000000000000000000000000000000000000000000080', - '0x00000000000000000000000000000000000000000000000000000000000000e0', - '0x00000000000000000000000000000000000000000000000000000000000000e0'])) - } catch (error) { - return t.end(error) - } - }) + tape('traceManager.getStackAt 41', (t) => { + t.plan(1) + try { + const callstack = debugManager.traceManager.getStackAt(41) + t.equal(JSON.stringify(callstack), JSON.stringify([ + '0x0000000000000000000000000000000000000000000000000000000000000080', + '0x0000000000000000000000000000000000000000000000000000000000000020', + '0x0000000000000000000000000000000000000000000000000000000000000080', + '0x00000000000000000000000000000000000000000000000000000000000000e0', + '0x00000000000000000000000000000000000000000000000000000000000000e0'])) + } catch (error) { + return t.end(error) + } + }) - // storage - tape('traceManager.getCurrentCalledAddressAt', (t) => { - t.plan(1) + // storage + tape('traceManager.getCurrentCalledAddressAt', (t) => { + t.plan(1) - try { - const address = debugManager.traceManager.getCurrentCalledAddressAt(38) - console.log(address) - const storageView = debugManager.storageViewAt(196, address) + try { + const address = debugManager.traceManager.getCurrentCalledAddressAt(38) + console.log(address) + const storageView = debugManager.storageViewAt(196, address) - storageView.storageRange().then((storage) => { - t.equal(JSON.stringify(storage), JSON.stringify({ '0x290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e563': { key: '0x0000000000000000000000000000000000000000000000000000000000000000', value: '0x0000000000000000000000005b38da6a701c568545dcfcb03fcb875f56beddc4' } })) - }).catch((error) => { - if (error) return t.end(error) - }) - } catch (error) { - return t.end(error) - } - }) + storageView.storageRange().then((storage) => { + t.equal(JSON.stringify(storage), JSON.stringify({ '0x290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e563': { key: '0x0000000000000000000000000000000000000000000000000000000000000000', value: '0x0000000000000000000000005b38da6a701c568545dcfcb03fcb875f56beddc4' } })) + }).catch((error) => { + if (error) return t.end(error) + }) + } catch (error) { + return t.end(error) + } + }) - tape('traceManager.decodeStateAt', async (t) => { - t.plan(7) - try { - const state = await debugManager.extractStateAt(312) - const decodedState = await debugManager.decodeStateAt(312, state) - console.log(decodedState) - t.equal(decodedState['chairperson'].value, '0x5B38DA6A701C568545DCFCB03FCB875F56BEDDC4') - t.equal(decodedState['chairperson'].type, 'address') - t.equal(decodedState['proposals'].value[0].value.voteCount.value, '0') - t.equal(decodedState['proposals'].value[0].value.voteCount.type, 'uint256') - t.equal(decodedState['proposals'].value[0].type, 'struct Ballot.Proposal') - t.equal(decodedState['proposals'].length, '0x1') - t.equal(decodedState['proposals'].type, 'struct Ballot.Proposal[]') - } catch (error) { - if (error) return t.end(error) - } - }) + tape('traceManager.decodeStateAt', async (t) => { + t.plan(7) + try { + const state = await debugManager.extractStateAt(312) + const decodedState = await debugManager.decodeStateAt(312, state) + console.log(decodedState) + t.equal(decodedState['chairperson'].value, '0x5B38DA6A701C568545DCFCB03FCB875F56BEDDC4') + t.equal(decodedState['chairperson'].type, 'address') + t.equal(decodedState['proposals'].value[0].value.voteCount.value, '0') + t.equal(decodedState['proposals'].value[0].value.voteCount.type, 'uint256') + t.equal(decodedState['proposals'].value[0].type, 'struct Ballot.Proposal') + t.equal(decodedState['proposals'].length, '0x1') + t.equal(decodedState['proposals'].type, 'struct Ballot.Proposal[]') + } catch (error) { + if (error) return t.end(error) + } + }) - tape('traceManager.decodeLocalsAt', async (t) => { - t.plan(1) - const tested = JSON.parse('{"proposalNames":{"value":[{"value":"0x48656C6C6F20576F726C64210000000000000000000000000000000000000000","type":"bytes32"}],"length":"0x1","type":"bytes32[]","cursor":1,"hasNext":false},"p":{"value":"45","type":"uint256"},"addressLocal":{"value":"0x5B38DA6A701C568545DCFCB03FCB875F56BEDDC4","type":"address"},"i":{"value":"2","type":"uint256"},"proposalsLocals":{"value":[{"value":{"name":{"value":"0x48656C6C6F20576F726C64210000000000000000000000000000000000000000","type":"bytes32"},"voteCount":{"value":"0","type":"uint256"}},"type":"struct Ballot.Proposal"}],"length":"0x1","type":"struct Ballot.Proposal[]"}}') - try { - const address = debugManager.traceManager.getCurrentCalledAddressAt(327) - const location = await debugManager.sourceLocationFromVMTraceIndex(address, 327) - debugManager.decodeLocalsAt(327, location, (error, decodedlocals) => { - if (error) return t.end(error) - const res = deepequal(decodedlocals, tested) - t.ok(res, `test if locals does match. expected: ${JSON.stringify(tested)} - current: ${JSON.stringify(decodedlocals)}`) - }) - } catch (error) { - return t.end(error) - } - }) + tape('traceManager.decodeLocalsAt', async (t) => { + t.plan(1) + const tested = JSON.parse('{"proposalNames":{"value":[{"value":"0x48656C6C6F20576F726C64210000000000000000000000000000000000000000","type":"bytes32"}],"length":"0x1","type":"bytes32[]","cursor":1,"hasNext":false},"p":{"value":"45","type":"uint256"},"addressLocal":{"value":"0x5B38DA6A701C568545DCFCB03FCB875F56BEDDC4","type":"address"},"i":{"value":"2","type":"uint256"},"proposalsLocals":{"value":[{"value":{"name":{"value":"0x48656C6C6F20576F726C64210000000000000000000000000000000000000000","type":"bytes32"},"voteCount":{"value":"0","type":"uint256"}},"type":"struct Ballot.Proposal"}],"length":"0x1","type":"struct Ballot.Proposal[]"}}') + try { + const address = debugManager.traceManager.getCurrentCalledAddressAt(327) + const location = await debugManager.sourceLocationFromVMTraceIndex(address, 327) + debugManager.decodeLocalsAt(327, location, (error, decodedlocals) => { + if (error) return t.end(error) + const res = deepequal(decodedlocals, tested) + t.ok(res, `test if locals does match. expected: ${JSON.stringify(tested)} - current: ${JSON.stringify(decodedlocals)}`) + }) + } catch (error) { + return t.end(error) + } + }) - tape('breakPointManager', (t) => { - t.plan(2) - const {traceManager, callTree, solidityProxy} = debugManager - const breakPointManager = new BreakpointManager({traceManager, callTree, solidityProxy}) + tape('breakPointManager', (t) => { + t.plan(2) + const {traceManager, callTree, solidityProxy} = debugManager + const breakPointManager = new BreakpointManager({traceManager, callTree, solidityProxy}) - breakPointManager.add({fileName: 'test.sol', row: 39}) + breakPointManager.add({fileName: 'test.sol', row: 39}) - breakPointManager.event.register('breakpointHit', function (sourceLocation, step) { - t.equal(JSON.stringify(sourceLocation), JSON.stringify({ start: 1146, length: 6, file: 0, jump: '-' })) - t.equal(step, 212) - }) + breakPointManager.event.register('breakpointHit', function (sourceLocation, step) { + t.equal(JSON.stringify(sourceLocation), JSON.stringify({ start: 1146, length: 6, file: 0, jump: '-' })) + t.equal(step, 212) + }) - breakPointManager.event.register('noBreakpointHit', function () { - t.end('noBreakpointHit') - }) - breakPointManager.jumpNextBreakpoint(0, true) + breakPointManager.event.register('noBreakpointHit', function () { + t.end('noBreakpointHit') }) + breakPointManager.jumpNextBreakpoint(0, true) + }) } diff --git a/libs/remix-debug/test/decoder/contracts/byteStorage.ts b/libs/remix-debug/test/decoder/contracts/byteStorage.ts index 89720f2a21..6797c156fe 100644 --- a/libs/remix-debug/test/decoder/contracts/byteStorage.ts +++ b/libs/remix-debug/test/decoder/contracts/byteStorage.ts @@ -1,7 +1,7 @@ 'use strict' module.exports = { - contract: ` + contract: ` contract byteStorage { enum enum1 { e0, e1, e2, e3, e4, e5, e6, e7, e8, e9, e10, e11, e12, e13, e14, e15, e16, e17, e18, e19, e20, e21, e22, e23, e24, e25, e26, e27, e28, e29, e30, e31, e32, e33, e34, e35, e36, e37, e38, e39, e40, e41, e42, e43, e44, e45, e46, e47, e48, e49, e50, e51, e52, e53, e54, e55, e56, e57, e58, e59, e60, e61, e62, e63, e64, e65, e66, e67, e68, e69, e70, e71, e72, e73, e74, e75, e76, e77, e78, e79, e80, e81, e82, e83, e84, e85, e86, e87, e88, e89, e90, e91, e92, e93, e94, e95, e96, e97, e98, e99, e100, e101, e102, e103, e104, e105, e106, e107, e108, e109, e110, e111, e112, e113, e114, e115, e116, e117, e118, e119, e120, e121, e122, e123, e124, e125, e126, e127, e128, e129, e130, e131, e132, e133, e134, e135, e136, e137, e138, e139, e140, e141, e142, e143, e144, e145, e146, e147, e148, e149, e150, e151, e152, e153, e154, e155, e156, e157, e158, e159, e160, e161, e162, e163, e164, e165, e166, e167, e168, e169, e170, e171, e172, e173, e174, e175, e176, e177, e178, e179, e180, e181, e182, e183, e184, e185, e186, e187, e188, e189, e190, e191, e192, e193, e194, e195, e196, e197, e198, e199, e200, e201, e202, e203, e204, e205, e206, e207, e208, e209, e210, e211, e212, e213, e214, e215, e216, e217, e218, e219, e220, e221, e222, e223, e224, e225, e226, e227, e228, e229, e230, e231, e232, e233, e234, e235, e236, e237, e238, e239, e240, e241, e242, e243, e244, e245, e246, e247, e248, e249, e250, e251, e252, e253, e254, e255, e256, e257, e258, e259, e260 } @@ -48,42 +48,42 @@ module.exports = { string str2 = 'long__long__long__long__long__long__long__long__long__long__long__long__long__long__long__long__long__long__long__long__long__long__long__long__long__long__long__long__long__long__long__long__long__long__long__long__long__long__long__long__long__long__long__long__long__long__long__long'; } `, - storage: { - '0x0000000000000000000000000000000000000000000000000000000000000000': '0x0000000000000000000001fe350f199f244ac9a79038d254400b632a63322500', - '0x0000000000000000000000000000000000000000000000000000000000000001': '0x64796e616d696362797465730000000000000000000000000000000000000018', - '0x0000000000000000000000000000000000000000000000000000000000000002': '0x0000000043240000000032443500000002357645002375000035935615791201', - '0x0000000000000000000000000000000000000000000000000000000000000003': '0x0000000000045435436546570000000343454300000000003245546457650000', - '0x0000000000000000000000000000000000000000000000000000000000000004': '0x0000000000000000000300000000000000000000005435465400000000000000', - '0x0000000000000000000000000000000000000000000000000000000000000005': '0x0000000000324543543543530000000000000003243242345435000000000000', - '0x0000000000000000000000000000000000000000000000000000000000000006': '0x0032454354440000000000000000000000032454434435000000000000000000', - '0x0000000000000000000000000000000000000000000000000000000000000007': '0x0000000000000000000000000000000324543432432432450000000000000000', - '0x0000000000000000000000000000000000000000000000000000000000000008': '0x0000000000000000000000000000032453432543543500000000000000000000', - '0x0000000000000000000000000000000000000000000000000000000000000009': '0x0000000000000000000000000003245434354354350000000000000000000000', - '0x000000000000000000000000000000000000000000000000000000000000000a': '0x000000000000000000000000032454543543ab35000000000000000000000000', - '0x000000000000000000000000000000000000000000000000000000000000000b': '0x0000000000000000000000324544324234350000000000000000000000000000', - '0x000000000000000000000000000000000000000000000000000000000000000c': '0x00000000000000000000324543aef50000000000000000000000000000000000', - '0x000000000000000000000000000000000000000000000000000000000000000d': '0x0000000000000000003245435fff000000000000000000000000000000000000', - '0x000000000000000000000000000000000000000000000000000000000000000e': '0x00000000000000003245435f0000000000000000000000000000000000000000', - '0x000000000000000000000000000000000000000000000000000000000000000f': '0x000000000000003245435f000000000000000000000000000000000000000000', - '0x0000000000000000000000000000000000000000000000000000000000000010': '0x0000000000003245435f00000000000000000000000000000000000000000000', - '0x0000000000000000000000000000000000000000000000000000000000000011': '0x000000000003245fffffff000000000000000000000000000000000000000000', - '0x0000000000000000000000000000000000000000000000000000000000000012': '0x0000000003241235000000000000000000000000000000000000000000000000', - '0x0000000000000000000000000000000000000000000000000000000000000013': '0x0000000325213213000000000000000000000000000000000000000000000000', - '0x0000000000000000000000000000000000000000000000000000000000000014': '0x0000032454352324230000000000000000000000000000000000000000000000', - '0x0000000000000000000000000000000000000000000000000000000000000015': '0x0032454351230000000000000000000000000000000000000000000000000000', - '0x0000000000000000000000000000000000000000000000000000000000000016': '0x324324423432543543ab00000000000000000000000000000000000000000000', - '0x0000000000000000000000000000000000000000000000000000000000000017': '0x00000000000000000000000000000000000000000000000000000000000000f0', - '0x0000000000000000000000000000000000000000000000000000000000000018': '0x73686f727400000000000000000000000000000000000000000000000000000a', - '0x0000000000000000000000000000000000000000000000000000000000000019': '0xd188d0b5d0bbd0bbd18b00000000000000000000000000000000000000000014', - '0x000000000000000000000000000000000000000000000000000000000000001a': '0x000000000000000000000000000000000000000000000000000000000000023d', - '0x057c384a7d1c54f3a1b2e5e67b2617b8224fdfd1ea7234eea573a6ff665ff63e': '0x6c6f6e675f5f6c6f6e675f5f6c6f6e675f5f6c6f6e675f5f6c6f6e675f5f6c6f', - '0x057c384a7d1c54f3a1b2e5e67b2617b8224fdfd1ea7234eea573a6ff665ff63f': '0x6e675f5f6c6f6e675f5f6c6f6e675f5f6c6f6e675f5f6c6f6e675f5f6c6f6e67', - '0x057c384a7d1c54f3a1b2e5e67b2617b8224fdfd1ea7234eea573a6ff665ff640': '0x5f5f6c6f6e675f5f6c6f6e675f5f6c6f6e675f5f6c6f6e675f5f6c6f6e675f5f', - '0x057c384a7d1c54f3a1b2e5e67b2617b8224fdfd1ea7234eea573a6ff665ff641': '0x6c6f6e675f5f6c6f6e675f5f6c6f6e675f5f6c6f6e675f5f6c6f6e675f5f6c6f', - '0x057c384a7d1c54f3a1b2e5e67b2617b8224fdfd1ea7234eea573a6ff665ff642': '0x6e675f5f6c6f6e675f5f6c6f6e675f5f6c6f6e675f5f6c6f6e675f5f6c6f6e67', - '0x057c384a7d1c54f3a1b2e5e67b2617b8224fdfd1ea7234eea573a6ff665ff643': '0x5f5f6c6f6e675f5f6c6f6e675f5f6c6f6e675f5f6c6f6e675f5f6c6f6e675f5f', - '0x057c384a7d1c54f3a1b2e5e67b2617b8224fdfd1ea7234eea573a6ff665ff644': '0x6c6f6e675f5f6c6f6e675f5f6c6f6e675f5f6c6f6e675f5f6c6f6e675f5f6c6f', - '0x057c384a7d1c54f3a1b2e5e67b2617b8224fdfd1ea7234eea573a6ff665ff645': '0x6e675f5f6c6f6e675f5f6c6f6e675f5f6c6f6e675f5f6c6f6e675f5f6c6f6e67', - '0x057c384a7d1c54f3a1b2e5e67b2617b8224fdfd1ea7234eea573a6ff665ff646': '0x5f5f6c6f6e675f5f6c6f6e675f5f6c6f6e675f5f6c6f6e675f5f6c6f6e670000' - } + storage: { + '0x0000000000000000000000000000000000000000000000000000000000000000': '0x0000000000000000000001fe350f199f244ac9a79038d254400b632a63322500', + '0x0000000000000000000000000000000000000000000000000000000000000001': '0x64796e616d696362797465730000000000000000000000000000000000000018', + '0x0000000000000000000000000000000000000000000000000000000000000002': '0x0000000043240000000032443500000002357645002375000035935615791201', + '0x0000000000000000000000000000000000000000000000000000000000000003': '0x0000000000045435436546570000000343454300000000003245546457650000', + '0x0000000000000000000000000000000000000000000000000000000000000004': '0x0000000000000000000300000000000000000000005435465400000000000000', + '0x0000000000000000000000000000000000000000000000000000000000000005': '0x0000000000324543543543530000000000000003243242345435000000000000', + '0x0000000000000000000000000000000000000000000000000000000000000006': '0x0032454354440000000000000000000000032454434435000000000000000000', + '0x0000000000000000000000000000000000000000000000000000000000000007': '0x0000000000000000000000000000000324543432432432450000000000000000', + '0x0000000000000000000000000000000000000000000000000000000000000008': '0x0000000000000000000000000000032453432543543500000000000000000000', + '0x0000000000000000000000000000000000000000000000000000000000000009': '0x0000000000000000000000000003245434354354350000000000000000000000', + '0x000000000000000000000000000000000000000000000000000000000000000a': '0x000000000000000000000000032454543543ab35000000000000000000000000', + '0x000000000000000000000000000000000000000000000000000000000000000b': '0x0000000000000000000000324544324234350000000000000000000000000000', + '0x000000000000000000000000000000000000000000000000000000000000000c': '0x00000000000000000000324543aef50000000000000000000000000000000000', + '0x000000000000000000000000000000000000000000000000000000000000000d': '0x0000000000000000003245435fff000000000000000000000000000000000000', + '0x000000000000000000000000000000000000000000000000000000000000000e': '0x00000000000000003245435f0000000000000000000000000000000000000000', + '0x000000000000000000000000000000000000000000000000000000000000000f': '0x000000000000003245435f000000000000000000000000000000000000000000', + '0x0000000000000000000000000000000000000000000000000000000000000010': '0x0000000000003245435f00000000000000000000000000000000000000000000', + '0x0000000000000000000000000000000000000000000000000000000000000011': '0x000000000003245fffffff000000000000000000000000000000000000000000', + '0x0000000000000000000000000000000000000000000000000000000000000012': '0x0000000003241235000000000000000000000000000000000000000000000000', + '0x0000000000000000000000000000000000000000000000000000000000000013': '0x0000000325213213000000000000000000000000000000000000000000000000', + '0x0000000000000000000000000000000000000000000000000000000000000014': '0x0000032454352324230000000000000000000000000000000000000000000000', + '0x0000000000000000000000000000000000000000000000000000000000000015': '0x0032454351230000000000000000000000000000000000000000000000000000', + '0x0000000000000000000000000000000000000000000000000000000000000016': '0x324324423432543543ab00000000000000000000000000000000000000000000', + '0x0000000000000000000000000000000000000000000000000000000000000017': '0x00000000000000000000000000000000000000000000000000000000000000f0', + '0x0000000000000000000000000000000000000000000000000000000000000018': '0x73686f727400000000000000000000000000000000000000000000000000000a', + '0x0000000000000000000000000000000000000000000000000000000000000019': '0xd188d0b5d0bbd0bbd18b00000000000000000000000000000000000000000014', + '0x000000000000000000000000000000000000000000000000000000000000001a': '0x000000000000000000000000000000000000000000000000000000000000023d', + '0x057c384a7d1c54f3a1b2e5e67b2617b8224fdfd1ea7234eea573a6ff665ff63e': '0x6c6f6e675f5f6c6f6e675f5f6c6f6e675f5f6c6f6e675f5f6c6f6e675f5f6c6f', + '0x057c384a7d1c54f3a1b2e5e67b2617b8224fdfd1ea7234eea573a6ff665ff63f': '0x6e675f5f6c6f6e675f5f6c6f6e675f5f6c6f6e675f5f6c6f6e675f5f6c6f6e67', + '0x057c384a7d1c54f3a1b2e5e67b2617b8224fdfd1ea7234eea573a6ff665ff640': '0x5f5f6c6f6e675f5f6c6f6e675f5f6c6f6e675f5f6c6f6e675f5f6c6f6e675f5f', + '0x057c384a7d1c54f3a1b2e5e67b2617b8224fdfd1ea7234eea573a6ff665ff641': '0x6c6f6e675f5f6c6f6e675f5f6c6f6e675f5f6c6f6e675f5f6c6f6e675f5f6c6f', + '0x057c384a7d1c54f3a1b2e5e67b2617b8224fdfd1ea7234eea573a6ff665ff642': '0x6e675f5f6c6f6e675f5f6c6f6e675f5f6c6f6e675f5f6c6f6e675f5f6c6f6e67', + '0x057c384a7d1c54f3a1b2e5e67b2617b8224fdfd1ea7234eea573a6ff665ff643': '0x5f5f6c6f6e675f5f6c6f6e675f5f6c6f6e675f5f6c6f6e675f5f6c6f6e675f5f', + '0x057c384a7d1c54f3a1b2e5e67b2617b8224fdfd1ea7234eea573a6ff665ff644': '0x6c6f6e675f5f6c6f6e675f5f6c6f6e675f5f6c6f6e675f5f6c6f6e675f5f6c6f', + '0x057c384a7d1c54f3a1b2e5e67b2617b8224fdfd1ea7234eea573a6ff665ff645': '0x6e675f5f6c6f6e675f5f6c6f6e675f5f6c6f6e675f5f6c6f6e675f5f6c6f6e67', + '0x057c384a7d1c54f3a1b2e5e67b2617b8224fdfd1ea7234eea573a6ff665ff646': '0x5f5f6c6f6e675f5f6c6f6e675f5f6c6f6e675f5f6c6f6e675f5f6c6f6e670000' + } } diff --git a/libs/remix-debug/test/decoder/contracts/calldata.ts b/libs/remix-debug/test/decoder/contracts/calldata.ts index 62d3df3ea4..a3c61f3990 100644 --- a/libs/remix-debug/test/decoder/contracts/calldata.ts +++ b/libs/remix-debug/test/decoder/contracts/calldata.ts @@ -1,7 +1,7 @@ 'use strict' module.exports = { - contract: ` + contract: ` pragma experimental ABIEncoderV2; contract calldataLocal { constructor () public { diff --git a/libs/remix-debug/test/decoder/contracts/intLocal.ts b/libs/remix-debug/test/decoder/contracts/intLocal.ts index 0b4a112808..63897c99ce 100644 --- a/libs/remix-debug/test/decoder/contracts/intLocal.ts +++ b/libs/remix-debug/test/decoder/contracts/intLocal.ts @@ -1,7 +1,7 @@ 'use strict' module.exports = { - contract: ` + contract: ` contract proxy { struct testStruct { int one; diff --git a/libs/remix-debug/test/decoder/contracts/intStorage.ts b/libs/remix-debug/test/decoder/contracts/intStorage.ts index ec2ed7719e..76fd9bbbb5 100644 --- a/libs/remix-debug/test/decoder/contracts/intStorage.ts +++ b/libs/remix-debug/test/decoder/contracts/intStorage.ts @@ -1,7 +1,7 @@ 'use strict' module.exports = { - contract: ` + contract: ` contract intStorage { uint8 ui8 = 130; uint16 ui16 = 456; @@ -20,13 +20,13 @@ module.exports = { int32 ishrink = 2; } `, - fullStorage: { - '0x0000000000000000000000000000000000000000000000000000000000000000': '0x000000000000000000000000000003944700000000d3362ef70000110401c882', - '0x0000000000000000000000000000000000000000000000000000000000000001': '0xfffffffffffffffffffffffffffffffffffffffffffffffffffffff872e07e01', - '0x0000000000000000000000000000000000000000000000000000000000000002': '0x00000000000000000000000000000000000000000000000000000000075d2842', - '0x0000000000000000000000000000000000000000000000000000000000000003': '0x00fffffffffffffffffffffffffff937e4ffffffffffff751200000d7ffb2ed3', - '0x0000000000000000000000000000000000000000000000000000000000000004': '0x0000000000000000000000000000000000000000000000000000000000346767', - '0x0000000000000000000000000000000000000000000000000000000000000005': '0xfffffffffffffffffffffffffffffffffffffffffffffffffffffff872e07e01', - '0x0000000000000000000000000000000000000000000000000000000000000006': '0x0000000000000000000000000000000000000000000000000000000000000002' - } + fullStorage: { + '0x0000000000000000000000000000000000000000000000000000000000000000': '0x000000000000000000000000000003944700000000d3362ef70000110401c882', + '0x0000000000000000000000000000000000000000000000000000000000000001': '0xfffffffffffffffffffffffffffffffffffffffffffffffffffffff872e07e01', + '0x0000000000000000000000000000000000000000000000000000000000000002': '0x00000000000000000000000000000000000000000000000000000000075d2842', + '0x0000000000000000000000000000000000000000000000000000000000000003': '0x00fffffffffffffffffffffffffff937e4ffffffffffff751200000d7ffb2ed3', + '0x0000000000000000000000000000000000000000000000000000000000000004': '0x0000000000000000000000000000000000000000000000000000000000346767', + '0x0000000000000000000000000000000000000000000000000000000000000005': '0xfffffffffffffffffffffffffffffffffffffffffffffffffffffff872e07e01', + '0x0000000000000000000000000000000000000000000000000000000000000006': '0x0000000000000000000000000000000000000000000000000000000000000002' + } } diff --git a/libs/remix-debug/test/decoder/contracts/mappingStorage.ts b/libs/remix-debug/test/decoder/contracts/mappingStorage.ts index 57c5f58a93..bf01adba68 100644 --- a/libs/remix-debug/test/decoder/contracts/mappingStorage.ts +++ b/libs/remix-debug/test/decoder/contracts/mappingStorage.ts @@ -1,5 +1,5 @@ module.exports = { - contract: ` + contract: ` contract SimpleMappingState { uint _num; mapping(string => uint) _iBreakSolidityState; diff --git a/libs/remix-debug/test/decoder/contracts/miscLocal.ts b/libs/remix-debug/test/decoder/contracts/miscLocal.ts index 2447e2f959..0c008fb924 100644 --- a/libs/remix-debug/test/decoder/contracts/miscLocal.ts +++ b/libs/remix-debug/test/decoder/contracts/miscLocal.ts @@ -1,7 +1,7 @@ 'use strict' module.exports = { - contract: ` + contract: ` contract miscLocal { enum enumDef { one, diff --git a/libs/remix-debug/test/decoder/contracts/structArrayLocal.ts b/libs/remix-debug/test/decoder/contracts/structArrayLocal.ts index c8391005d8..f9a38abf0f 100644 --- a/libs/remix-debug/test/decoder/contracts/structArrayLocal.ts +++ b/libs/remix-debug/test/decoder/contracts/structArrayLocal.ts @@ -1,7 +1,7 @@ 'use strict' module.exports = { - contract: ` + contract: ` contract structArrayLocal { struct teststruct { string a; diff --git a/libs/remix-debug/test/decoder/contracts/structArrayStorage.ts b/libs/remix-debug/test/decoder/contracts/structArrayStorage.ts index 5c38132b4c..3947b7d1de 100644 --- a/libs/remix-debug/test/decoder/contracts/structArrayStorage.ts +++ b/libs/remix-debug/test/decoder/contracts/structArrayStorage.ts @@ -1,7 +1,7 @@ 'use strict' module.exports = { - contract: `contract structArrayStorage { + contract: `contract structArrayStorage { struct intStruct { int8 i8; int16 i16; @@ -152,64 +152,64 @@ module.exports = { } } `, - storage: { - '0x0000000000000000000000000000000000000000000000000000000000000000': '0x0000000000000000000000000000000000000000000000000000000080ffca20', - '0x0000000000000000000000000000000000000000000000000000000000000001': '0xfffffffffffffffffffffffffffffffffffffffffffffffffffffede75b8df64', - '0x0000000000000000000000000000000000000000000000000000000000000002': '0x0000000000000000000000000000000000000000000000000000eb68e76e86fc', - '0x0000000000000000000000000000000000000000000000000000000000000003': '0x0000000000001105fffffffffffff3520000000000000159fffffffffffff7aa', - '0x0000000000000000000000000000000000000000000000000000000000000004': '0x00000000000000000000000000000cb6fffffffffffff6d80000000000000144', - '0x0000000000000000000000000000000000000000000000000000000000000005': '0x0000000000000000000000000000000000000000000000000000000000000009', - '0x036b6384b5eca791c62761152d0c79bb0604c104a5fb6f4eb0703f3154bb3db0': '0x0000000000001105fffffffffffff3520000000000000159fffffffffffff7aa', - '0x036b6384b5eca791c62761152d0c79bb0604c104a5fb6f4eb0703f3154bb3db1': '0xffffffffffffff020000000000000cb6fffffffffffff6d80000000000000144', - '0x036b6384b5eca791c62761152d0c79bb0604c104a5fb6f4eb0703f3154bb3db2': '0x000000000000000000000000000000000000000000000000fffffffffffff6ce', - '0x0000000000000000000000000000000000000000000000000000000000000006': '0x0000000000000000000000000000000000000000000000000000000000000001', - '0x0000000000000000000000000000000000000000000000000000000000000007': '0x0000000000000000000000000000000000000000000000000000000000000003', - '0x0000000000000000000000000000000000000000000000000000000000000008': '0x000000000000000000000000000000000000000000000000000000000000000a', - '0x0000000000000000000000000000000000000000000000000000000000000009': '0x0000000000000000000000000000000000000000000000000000000000000002', - '0xf652222313e28459528d920b65115c16c04f3efc82aaedc97be59f3f377c0d3f': '0x0000000000000000000000000000000000000000000000000000000000000003', - '0xa66cc928b5edb82af9bd49922954155ab7b0942694bea4ce44661d9a8736c688': '0x0000000000000000000000000000000000000000fffffb2efffffff40000000c', - '0xf3f7a9fe364faab93b216da50a3214154f22a0a2b415b23a84c8169e8b636ee3': '0x0000000200084b37fe9a6755fffe1dc0fffffff4000004d30000000c00000001', - '0xf3f7a9fe364faab93b216da50a3214154f22a0a2b415b23a84c8169e8b636ee4': '0x00000000000000000000000000000000000000000000000000038bf0ffffffff', - '0x6e1540171b6c0c960b71a7020d9f60077f6af931a8bbf590da0223dacf75c7af': '0x00000000000000000000000000000000000000000000000000038bf000038bf0', - '0x000000000000000000000000000000000000000000000000000000000000000a': '0x0000000000000000000000000000000000000000000000000000000000000002', - '0xc65a7bb8d6351c1cf70c95a316cc6a92839c986682d98bc35f958f4883f9d2a8': '0x0000000000000000000000000000000000000000000000000000000000000003', - '0xc65a7bb8d6351c1cf70c95a316cc6a92839c986682d98bc35f958f4883f9d2a9': '0x0000000000000000000000000000000000000000000000000000000000000003', - '0xc65a7bb8d6351c1cf70c95a316cc6a92839c986682d98bc35f958f4883f9d2aa': '0x0000000000000000000000000000000000000000000000000000000000000003', - '0xc65a7bb8d6351c1cf70c95a316cc6a92839c986682d98bc35f958f4883f9d2ab': '0x0000000000000000000000000000000000000000000000000000000000000003', - '0xc65a7bb8d6351c1cf70c95a316cc6a92839c986682d98bc35f958f4883f9d2ac': '0x0000000000000000000000000000000000000000000000000000000000000003', - '0xc65a7bb8d6351c1cf70c95a316cc6a92839c986682d98bc35f958f4883f9d2ad': '0x0000000000000000000000000000000000000000000000000000000000000003', - '0xc65a7bb8d6351c1cf70c95a316cc6a92839c986682d98bc35f958f4883f9d2ae': '0x0000000000000000000000000000000000000000000000000000000000000003', - '0xc65a7bb8d6351c1cf70c95a316cc6a92839c986682d98bc35f958f4883f9d2af': '0x0000000000000000000000000000000000000000000000000000000000000003', - '0x410c2796757c1866e144712b649ab035b22d7295530f125d2b7bc17fa7b793b5': '0x0000000000000000000000000000000000000000ffffffe4ffffffe900000017', - '0x06b493c1ca289c5326ef56c162cd187bf96c737c2c9bbda318cc345be15042af': '0x0000000000000000000000000000000000000000ffffffe4ffffffe900000017', - '0x7a0b543a77c72a2154fae01417d93ab4a7f07c9a6bbce5febfeb9904a41b7914': '0x0000000000000000000000000000000000000000ffffffe4ffffffe900000017', - '0x5370eae143cc2f6260640bd734b0cdaf587bbcfc81362df39d56d5a29a7e663b': '0x0000000000000000000000000000000000000000ffffffe4ffffffe900000017', - '0xd5211e5652076f058928f5b24e1816690291c298b337ea927f8d0f3aabb8a05a': '0x0000000000000000000000000000000000000000ffffffe4ffffffe900000017', - '0xf232dee5d9edbb879fab95c81a3867fe42b8d79b05e9c99336c5297487f94e8d': '0x0000000000000000000000000000000000000000ffffffe4ffffffe900000017', - '0x8a82e6d20ae2c2a82dd8e575dac6354ce964fd35e3d1cdb79bb1757c6a7675b6': '0x0000000000000000000000000000000000000000ffffffe4ffffffe900000017', - '0xa9e6724ab7d0ccf2de69222bc5703c9df2049038736e6d57f437315272b76a3a': '0x0000000000000000000000000000000000000000ffffffe4ffffffe900000017', - '0x000000000000000000000000000000000000000000000000000000000000000b': '0x0000000000000000000000000000000000000000000000000000000000000002', - '0x000000000000000000000000000000000000000000000000000000000000000c': '0x0000000000000000000000000000000000000000000000000000000000000001', - '0x000000000000000000000000000000000000000000000000000000000000000d': '0x0000000000000000000000000000000000000000000000000000000000000003', - '0x0175b7a638427703f0dbe7bb9bbf987a2551717b34e79f33b5b1008d1fa01db9': '0x0000000000000000000000000000000000000000000000000000000000000022', - '0x0175b7a638427703f0dbe7bb9bbf987a2551717b34e79f33b5b1008d1fa01dba': '0x746573745f7374725f73686f727400000000000000000000000000000000001c', - '0x0175b7a638427703f0dbe7bb9bbf987a2551717b34e79f33b5b1008d1fa01dbb': '0x0000000000000000000000000000000000000000000000000000000000000085', - '0x0175b7a638427703f0dbe7bb9bbf987a2551717b34e79f33b5b1008d1fa01dbc': '0x00000000000000000000000000000000000000000000000000000000000000db', - '0x40abea1508c7557b93b3e219e777ce8530b60f9f8452ef1c627dbc62b53708fc': '0x746573745f7374725f6c6f6e6720746573745f7374725f6c6f206e6774657374', - '0x40abea1508c7557b93b3e219e777ce8530b60f9f8452ef1c627dbc62b53708fd': '0x5f7374725f6c6f6e67746573745f7374725f206c6f6e67746573745f7374725f', - '0x40abea1508c7557b93b3e219e777ce8530b60f9f8452ef1c627dbc62b53708fe': '0x6c6f6e67746573745f207374725f6c6f6e67746573745f7374725f6c206f6e67', - '0x40abea1508c7557b93b3e219e777ce8530b60f9f8452ef1c627dbc62b53708ff': '0x746573745f7374725f6c6f6e6700000000000000000000000000000000000000', - '0xdf6966c971051c3d54ec59162606531493a51404a002842f56009d7e5cf4a8c7': '0x0000000000000000000000000000000000000000000000000000000000000032', - '0xdf6966c971051c3d54ec59162606531493a51404a002842f56009d7e5cf4a8c8': '0x746573745f7374725f73686f727400000000000000000000000000000000001c', - '0xd7b6990105719101dabeb77144f2a3385c8033acd3af97e9423a695e81ad1eb5': '0x000000000000000000000000000000000000000000000000000000000000003c', - '0xd7b6990105719101dabeb77144f2a3385c8033acd3af97e9423a695e81ad1eb6': '0x746573745f7374725f73686f727400000000000000000000000000000000001c', - '0xd7b6990105719101dabeb77144f2a3385c8033acd3af97e9423a695e81ad1eb7': '0x0000000000000000000000000000000000000000000000000000000000000054', - '0xd7b6990105719101dabeb77144f2a3385c8033acd3af97e9423a695e81ad1eb8': '0x00000000000000000000000000000000000000000000000000000000000000db', - '0x87466a1ae97409dd9d9cd9368751b439509d8b3f8fc2bb47a4264e5d6fd4d324': '0x746573745f7374725f6c6f6e6720746573745f7374725f6c6f206e6774657374', - '0x87466a1ae97409dd9d9cd9368751b439509d8b3f8fc2bb47a4264e5d6fd4d325': '0x5f7374725f6c6f6e67746573745f7374725f206c6f6e67746573745f7374725f', - '0x87466a1ae97409dd9d9cd9368751b439509d8b3f8fc2bb47a4264e5d6fd4d326': '0x6c6f6e67746573745f207374725f6c6f6e67746573745f7374725f6c206f6e67', - '0x87466a1ae97409dd9d9cd9368751b439509d8b3f8fc2bb47a4264e5d6fd4d327': '0x746573745f7374725f6c6f6e6700000000000000000000000000000000000000', - '0xd7b6990105719101dabeb77144f2a3385c8033acd3af97e9423a695e81ad1eb9': '0x00000000000000000000000000000000000000000000000000000000000000de', - '0xd7b6990105719101dabeb77144f2a3385c8033acd3af97e9423a695e81ad1eba': '0x746573745f7374725f73686f727400000000000000000000000000000000001c' - } + storage: { + '0x0000000000000000000000000000000000000000000000000000000000000000': '0x0000000000000000000000000000000000000000000000000000000080ffca20', + '0x0000000000000000000000000000000000000000000000000000000000000001': '0xfffffffffffffffffffffffffffffffffffffffffffffffffffffede75b8df64', + '0x0000000000000000000000000000000000000000000000000000000000000002': '0x0000000000000000000000000000000000000000000000000000eb68e76e86fc', + '0x0000000000000000000000000000000000000000000000000000000000000003': '0x0000000000001105fffffffffffff3520000000000000159fffffffffffff7aa', + '0x0000000000000000000000000000000000000000000000000000000000000004': '0x00000000000000000000000000000cb6fffffffffffff6d80000000000000144', + '0x0000000000000000000000000000000000000000000000000000000000000005': '0x0000000000000000000000000000000000000000000000000000000000000009', + '0x036b6384b5eca791c62761152d0c79bb0604c104a5fb6f4eb0703f3154bb3db0': '0x0000000000001105fffffffffffff3520000000000000159fffffffffffff7aa', + '0x036b6384b5eca791c62761152d0c79bb0604c104a5fb6f4eb0703f3154bb3db1': '0xffffffffffffff020000000000000cb6fffffffffffff6d80000000000000144', + '0x036b6384b5eca791c62761152d0c79bb0604c104a5fb6f4eb0703f3154bb3db2': '0x000000000000000000000000000000000000000000000000fffffffffffff6ce', + '0x0000000000000000000000000000000000000000000000000000000000000006': '0x0000000000000000000000000000000000000000000000000000000000000001', + '0x0000000000000000000000000000000000000000000000000000000000000007': '0x0000000000000000000000000000000000000000000000000000000000000003', + '0x0000000000000000000000000000000000000000000000000000000000000008': '0x000000000000000000000000000000000000000000000000000000000000000a', + '0x0000000000000000000000000000000000000000000000000000000000000009': '0x0000000000000000000000000000000000000000000000000000000000000002', + '0xf652222313e28459528d920b65115c16c04f3efc82aaedc97be59f3f377c0d3f': '0x0000000000000000000000000000000000000000000000000000000000000003', + '0xa66cc928b5edb82af9bd49922954155ab7b0942694bea4ce44661d9a8736c688': '0x0000000000000000000000000000000000000000fffffb2efffffff40000000c', + '0xf3f7a9fe364faab93b216da50a3214154f22a0a2b415b23a84c8169e8b636ee3': '0x0000000200084b37fe9a6755fffe1dc0fffffff4000004d30000000c00000001', + '0xf3f7a9fe364faab93b216da50a3214154f22a0a2b415b23a84c8169e8b636ee4': '0x00000000000000000000000000000000000000000000000000038bf0ffffffff', + '0x6e1540171b6c0c960b71a7020d9f60077f6af931a8bbf590da0223dacf75c7af': '0x00000000000000000000000000000000000000000000000000038bf000038bf0', + '0x000000000000000000000000000000000000000000000000000000000000000a': '0x0000000000000000000000000000000000000000000000000000000000000002', + '0xc65a7bb8d6351c1cf70c95a316cc6a92839c986682d98bc35f958f4883f9d2a8': '0x0000000000000000000000000000000000000000000000000000000000000003', + '0xc65a7bb8d6351c1cf70c95a316cc6a92839c986682d98bc35f958f4883f9d2a9': '0x0000000000000000000000000000000000000000000000000000000000000003', + '0xc65a7bb8d6351c1cf70c95a316cc6a92839c986682d98bc35f958f4883f9d2aa': '0x0000000000000000000000000000000000000000000000000000000000000003', + '0xc65a7bb8d6351c1cf70c95a316cc6a92839c986682d98bc35f958f4883f9d2ab': '0x0000000000000000000000000000000000000000000000000000000000000003', + '0xc65a7bb8d6351c1cf70c95a316cc6a92839c986682d98bc35f958f4883f9d2ac': '0x0000000000000000000000000000000000000000000000000000000000000003', + '0xc65a7bb8d6351c1cf70c95a316cc6a92839c986682d98bc35f958f4883f9d2ad': '0x0000000000000000000000000000000000000000000000000000000000000003', + '0xc65a7bb8d6351c1cf70c95a316cc6a92839c986682d98bc35f958f4883f9d2ae': '0x0000000000000000000000000000000000000000000000000000000000000003', + '0xc65a7bb8d6351c1cf70c95a316cc6a92839c986682d98bc35f958f4883f9d2af': '0x0000000000000000000000000000000000000000000000000000000000000003', + '0x410c2796757c1866e144712b649ab035b22d7295530f125d2b7bc17fa7b793b5': '0x0000000000000000000000000000000000000000ffffffe4ffffffe900000017', + '0x06b493c1ca289c5326ef56c162cd187bf96c737c2c9bbda318cc345be15042af': '0x0000000000000000000000000000000000000000ffffffe4ffffffe900000017', + '0x7a0b543a77c72a2154fae01417d93ab4a7f07c9a6bbce5febfeb9904a41b7914': '0x0000000000000000000000000000000000000000ffffffe4ffffffe900000017', + '0x5370eae143cc2f6260640bd734b0cdaf587bbcfc81362df39d56d5a29a7e663b': '0x0000000000000000000000000000000000000000ffffffe4ffffffe900000017', + '0xd5211e5652076f058928f5b24e1816690291c298b337ea927f8d0f3aabb8a05a': '0x0000000000000000000000000000000000000000ffffffe4ffffffe900000017', + '0xf232dee5d9edbb879fab95c81a3867fe42b8d79b05e9c99336c5297487f94e8d': '0x0000000000000000000000000000000000000000ffffffe4ffffffe900000017', + '0x8a82e6d20ae2c2a82dd8e575dac6354ce964fd35e3d1cdb79bb1757c6a7675b6': '0x0000000000000000000000000000000000000000ffffffe4ffffffe900000017', + '0xa9e6724ab7d0ccf2de69222bc5703c9df2049038736e6d57f437315272b76a3a': '0x0000000000000000000000000000000000000000ffffffe4ffffffe900000017', + '0x000000000000000000000000000000000000000000000000000000000000000b': '0x0000000000000000000000000000000000000000000000000000000000000002', + '0x000000000000000000000000000000000000000000000000000000000000000c': '0x0000000000000000000000000000000000000000000000000000000000000001', + '0x000000000000000000000000000000000000000000000000000000000000000d': '0x0000000000000000000000000000000000000000000000000000000000000003', + '0x0175b7a638427703f0dbe7bb9bbf987a2551717b34e79f33b5b1008d1fa01db9': '0x0000000000000000000000000000000000000000000000000000000000000022', + '0x0175b7a638427703f0dbe7bb9bbf987a2551717b34e79f33b5b1008d1fa01dba': '0x746573745f7374725f73686f727400000000000000000000000000000000001c', + '0x0175b7a638427703f0dbe7bb9bbf987a2551717b34e79f33b5b1008d1fa01dbb': '0x0000000000000000000000000000000000000000000000000000000000000085', + '0x0175b7a638427703f0dbe7bb9bbf987a2551717b34e79f33b5b1008d1fa01dbc': '0x00000000000000000000000000000000000000000000000000000000000000db', + '0x40abea1508c7557b93b3e219e777ce8530b60f9f8452ef1c627dbc62b53708fc': '0x746573745f7374725f6c6f6e6720746573745f7374725f6c6f206e6774657374', + '0x40abea1508c7557b93b3e219e777ce8530b60f9f8452ef1c627dbc62b53708fd': '0x5f7374725f6c6f6e67746573745f7374725f206c6f6e67746573745f7374725f', + '0x40abea1508c7557b93b3e219e777ce8530b60f9f8452ef1c627dbc62b53708fe': '0x6c6f6e67746573745f207374725f6c6f6e67746573745f7374725f6c206f6e67', + '0x40abea1508c7557b93b3e219e777ce8530b60f9f8452ef1c627dbc62b53708ff': '0x746573745f7374725f6c6f6e6700000000000000000000000000000000000000', + '0xdf6966c971051c3d54ec59162606531493a51404a002842f56009d7e5cf4a8c7': '0x0000000000000000000000000000000000000000000000000000000000000032', + '0xdf6966c971051c3d54ec59162606531493a51404a002842f56009d7e5cf4a8c8': '0x746573745f7374725f73686f727400000000000000000000000000000000001c', + '0xd7b6990105719101dabeb77144f2a3385c8033acd3af97e9423a695e81ad1eb5': '0x000000000000000000000000000000000000000000000000000000000000003c', + '0xd7b6990105719101dabeb77144f2a3385c8033acd3af97e9423a695e81ad1eb6': '0x746573745f7374725f73686f727400000000000000000000000000000000001c', + '0xd7b6990105719101dabeb77144f2a3385c8033acd3af97e9423a695e81ad1eb7': '0x0000000000000000000000000000000000000000000000000000000000000054', + '0xd7b6990105719101dabeb77144f2a3385c8033acd3af97e9423a695e81ad1eb8': '0x00000000000000000000000000000000000000000000000000000000000000db', + '0x87466a1ae97409dd9d9cd9368751b439509d8b3f8fc2bb47a4264e5d6fd4d324': '0x746573745f7374725f6c6f6e6720746573745f7374725f6c6f206e6774657374', + '0x87466a1ae97409dd9d9cd9368751b439509d8b3f8fc2bb47a4264e5d6fd4d325': '0x5f7374725f6c6f6e67746573745f7374725f206c6f6e67746573745f7374725f', + '0x87466a1ae97409dd9d9cd9368751b439509d8b3f8fc2bb47a4264e5d6fd4d326': '0x6c6f6e67746573745f207374725f6c6f6e67746573745f7374725f6c206f6e67', + '0x87466a1ae97409dd9d9cd9368751b439509d8b3f8fc2bb47a4264e5d6fd4d327': '0x746573745f7374725f6c6f6e6700000000000000000000000000000000000000', + '0xd7b6990105719101dabeb77144f2a3385c8033acd3af97e9423a695e81ad1eb9': '0x00000000000000000000000000000000000000000000000000000000000000de', + '0xd7b6990105719101dabeb77144f2a3385c8033acd3af97e9423a695e81ad1eba': '0x746573745f7374725f73686f727400000000000000000000000000000000001c' + } } diff --git a/libs/remix-debug/test/decoder/decodeInfo.ts b/libs/remix-debug/test/decoder/decodeInfo.ts index cd2b8cf94d..f3ac398905 100644 --- a/libs/remix-debug/test/decoder/decodeInfo.ts +++ b/libs/remix-debug/test/decoder/decodeInfo.ts @@ -10,86 +10,86 @@ import { compilerInput } from '../helpers/compilerHelper' import * as util from '../../src/solidity-decoder/types/util' tape('solidity', function (t) { - t.test('astHelper, decodeInfo', function (st) { - let output = compile(compilerInput(contracts)) - output = JSON.parse(output) + t.test('astHelper, decodeInfo', function (st) { + let output = compile(compilerInput(contracts)) + output = JSON.parse(output) - let state: any = astHelper.extractStateDefinitions('test.sol:contractUint', output.sources, null) - let states = astHelper.extractStatesDefinitions(output.sources, null) - let stateDef = state.stateDefinitions - let parsedType = decodeInfo.parseType(stateDef[0].typeDescriptions.typeString, states, 'contractUint', util.extractLocationFromAstVariable(stateDef[0])) - checkDecodeInfo(st, parsedType, 1, 1, 'uint8') - parsedType = decodeInfo.parseType(stateDef[1].typeDescriptions.typeString, states, 'contractUint', util.extractLocationFromAstVariable(stateDef[1])) - checkDecodeInfo(st, parsedType, 1, 32, 'uint256') - parsedType = decodeInfo.parseType(stateDef[2].typeDescriptions.typeString, states, 'contractUint', util.extractLocationFromAstVariable(stateDef[2])) - checkDecodeInfo(st, parsedType, 1, 32, 'uint256') - parsedType = decodeInfo.parseType(stateDef[3].typeDescriptions.typeString, states, 'contractUint', util.extractLocationFromAstVariable(stateDef[3])) - checkDecodeInfo(st, parsedType, 1, 16, 'bytes16') + let state: any = astHelper.extractStateDefinitions('test.sol:contractUint', output.sources, null) + let states = astHelper.extractStatesDefinitions(output.sources, null) + let stateDef = state.stateDefinitions + let parsedType = decodeInfo.parseType(stateDef[0].typeDescriptions.typeString, states, 'contractUint', util.extractLocationFromAstVariable(stateDef[0])) + checkDecodeInfo(st, parsedType, 1, 1, 'uint8') + parsedType = decodeInfo.parseType(stateDef[1].typeDescriptions.typeString, states, 'contractUint', util.extractLocationFromAstVariable(stateDef[1])) + checkDecodeInfo(st, parsedType, 1, 32, 'uint256') + parsedType = decodeInfo.parseType(stateDef[2].typeDescriptions.typeString, states, 'contractUint', util.extractLocationFromAstVariable(stateDef[2])) + checkDecodeInfo(st, parsedType, 1, 32, 'uint256') + parsedType = decodeInfo.parseType(stateDef[3].typeDescriptions.typeString, states, 'contractUint', util.extractLocationFromAstVariable(stateDef[3])) + checkDecodeInfo(st, parsedType, 1, 16, 'bytes16') - state = astHelper.extractStateDefinitions('test.sol:contractStructAndArray', output.sources, null) - stateDef = state.stateDefinitions - parsedType = decodeInfo.parseType(stateDef[1].typeDescriptions.typeString, states, 'contractStructAndArray', util.extractLocationFromAstVariable(stateDef[1])) - checkDecodeInfo(st, parsedType, 2, 32, 'struct contractStructAndArray.structDef') - parsedType = decodeInfo.parseType(stateDef[2].typeDescriptions.typeString, states, 'contractStructAndArray', util.extractLocationFromAstVariable(stateDef[2])) - checkDecodeInfo(st, parsedType, 6, 32, 'struct contractStructAndArray.structDef[3]') - parsedType = decodeInfo.parseType(stateDef[3].typeDescriptions.typeString, states, 'contractStructAndArray', util.extractLocationFromAstVariable(stateDef[3])) - checkDecodeInfo(st, parsedType, 2, 32, 'bytes12[4]') + state = astHelper.extractStateDefinitions('test.sol:contractStructAndArray', output.sources, null) + stateDef = state.stateDefinitions + parsedType = decodeInfo.parseType(stateDef[1].typeDescriptions.typeString, states, 'contractStructAndArray', util.extractLocationFromAstVariable(stateDef[1])) + checkDecodeInfo(st, parsedType, 2, 32, 'struct contractStructAndArray.structDef') + parsedType = decodeInfo.parseType(stateDef[2].typeDescriptions.typeString, states, 'contractStructAndArray', util.extractLocationFromAstVariable(stateDef[2])) + checkDecodeInfo(st, parsedType, 6, 32, 'struct contractStructAndArray.structDef[3]') + parsedType = decodeInfo.parseType(stateDef[3].typeDescriptions.typeString, states, 'contractStructAndArray', util.extractLocationFromAstVariable(stateDef[3])) + checkDecodeInfo(st, parsedType, 2, 32, 'bytes12[4]') - state = astHelper.extractStateDefinitions('test.sol:contractArray', output.sources, null) - stateDef = state.stateDefinitions - parsedType = decodeInfo.parseType(stateDef[0].typeDescriptions.typeString, states, 'contractArray', util.extractLocationFromAstVariable(stateDef[0])) - checkDecodeInfo(st, parsedType, 1, 32, 'uint32[5]') - parsedType = decodeInfo.parseType(stateDef[1].typeDescriptions.typeString, states, 'contractArray', util.extractLocationFromAstVariable(stateDef[1])) - checkDecodeInfo(st, parsedType, 1, 32, 'int8[]') - parsedType = decodeInfo.parseType(stateDef[2].typeDescriptions.typeString, states, 'contractArray', util.extractLocationFromAstVariable(stateDef[2])) - checkDecodeInfo(st, parsedType, 4, 32, 'int16[][3][][4]') + state = astHelper.extractStateDefinitions('test.sol:contractArray', output.sources, null) + stateDef = state.stateDefinitions + parsedType = decodeInfo.parseType(stateDef[0].typeDescriptions.typeString, states, 'contractArray', util.extractLocationFromAstVariable(stateDef[0])) + checkDecodeInfo(st, parsedType, 1, 32, 'uint32[5]') + parsedType = decodeInfo.parseType(stateDef[1].typeDescriptions.typeString, states, 'contractArray', util.extractLocationFromAstVariable(stateDef[1])) + checkDecodeInfo(st, parsedType, 1, 32, 'int8[]') + parsedType = decodeInfo.parseType(stateDef[2].typeDescriptions.typeString, states, 'contractArray', util.extractLocationFromAstVariable(stateDef[2])) + checkDecodeInfo(st, parsedType, 4, 32, 'int16[][3][][4]') - state = astHelper.extractStateDefinitions('test.sol:contractEnum', output.sources, null) - stateDef = state.stateDefinitions - parsedType = decodeInfo.parseType(stateDef[1].typeDescriptions.typeString, states, 'contractEnum', null) - checkDecodeInfo(st, parsedType, 1, 2, 'enum') + state = astHelper.extractStateDefinitions('test.sol:contractEnum', output.sources, null) + stateDef = state.stateDefinitions + parsedType = decodeInfo.parseType(stateDef[1].typeDescriptions.typeString, states, 'contractEnum', null) + checkDecodeInfo(st, parsedType, 1, 2, 'enum') - state = astHelper.extractStateDefinitions('test.sol:contractSmallVariable', output.sources, null) - stateDef = state.stateDefinitions - parsedType = decodeInfo.parseType(stateDef[0].typeDescriptions.typeString, states, 'contractSmallVariable', util.extractLocationFromAstVariable(stateDef[0])) - checkDecodeInfo(st, parsedType, 1, 1, 'int8') - parsedType = decodeInfo.parseType(stateDef[1].typeDescriptions.typeString, states, 'contractSmallVariable', util.extractLocationFromAstVariable(stateDef[1])) - checkDecodeInfo(st, parsedType, 1, 1, 'uint8') - parsedType = decodeInfo.parseType(stateDef[2].typeDescriptions.typeString, states, 'contractSmallVariable', util.extractLocationFromAstVariable(stateDef[2])) - checkDecodeInfo(st, parsedType, 1, 2, 'uint16') - parsedType = decodeInfo.parseType(stateDef[3].typeDescriptions.typeString, states, 'contractSmallVariable', util.extractLocationFromAstVariable(stateDef[3])) - checkDecodeInfo(st, parsedType, 1, 4, 'int32') - parsedType = decodeInfo.parseType(stateDef[4].typeDescriptions.typeString, states, 'contractSmallVariable', util.extractLocationFromAstVariable(stateDef[4])) - checkDecodeInfo(st, parsedType, 1, 32, 'uint256') - parsedType = decodeInfo.parseType(stateDef[5].typeDescriptions.typeString, states, 'contractSmallVariable', util.extractLocationFromAstVariable(stateDef[5])) - checkDecodeInfo(st, parsedType, 1, 2, 'int16') + state = astHelper.extractStateDefinitions('test.sol:contractSmallVariable', output.sources, null) + stateDef = state.stateDefinitions + parsedType = decodeInfo.parseType(stateDef[0].typeDescriptions.typeString, states, 'contractSmallVariable', util.extractLocationFromAstVariable(stateDef[0])) + checkDecodeInfo(st, parsedType, 1, 1, 'int8') + parsedType = decodeInfo.parseType(stateDef[1].typeDescriptions.typeString, states, 'contractSmallVariable', util.extractLocationFromAstVariable(stateDef[1])) + checkDecodeInfo(st, parsedType, 1, 1, 'uint8') + parsedType = decodeInfo.parseType(stateDef[2].typeDescriptions.typeString, states, 'contractSmallVariable', util.extractLocationFromAstVariable(stateDef[2])) + checkDecodeInfo(st, parsedType, 1, 2, 'uint16') + parsedType = decodeInfo.parseType(stateDef[3].typeDescriptions.typeString, states, 'contractSmallVariable', util.extractLocationFromAstVariable(stateDef[3])) + checkDecodeInfo(st, parsedType, 1, 4, 'int32') + parsedType = decodeInfo.parseType(stateDef[4].typeDescriptions.typeString, states, 'contractSmallVariable', util.extractLocationFromAstVariable(stateDef[4])) + checkDecodeInfo(st, parsedType, 1, 32, 'uint256') + parsedType = decodeInfo.parseType(stateDef[5].typeDescriptions.typeString, states, 'contractSmallVariable', util.extractLocationFromAstVariable(stateDef[5])) + checkDecodeInfo(st, parsedType, 1, 2, 'int16') - output = compile(compilerInput(simplecontracts)) - output = JSON.parse(output) - state = astHelper.extractStateDefinitions('test.sol:simpleContract', output.sources, null) - states = astHelper.extractStatesDefinitions(output.sources, null) - stateDef = state.stateDefinitions - parsedType = decodeInfo.parseType(stateDef[2].typeDescriptions.typeString, states, 'simpleContract', util.extractLocationFromAstVariable(stateDef[2])) - checkDecodeInfo(st, parsedType, 2, 32, 'struct simpleContract.structDef') - parsedType = decodeInfo.parseType(stateDef[3].typeDescriptions.typeString, states, 'simpleContract', util.extractLocationFromAstVariable(stateDef[3])) - checkDecodeInfo(st, parsedType, 6, 32, 'struct simpleContract.structDef[3]') - parsedType = decodeInfo.parseType(stateDef[4].typeDescriptions.typeString, states, 'simpleContract', util.extractLocationFromAstVariable(stateDef[4])) - checkDecodeInfo(st, parsedType, 1, 1, 'enum') + output = compile(compilerInput(simplecontracts)) + output = JSON.parse(output) + state = astHelper.extractStateDefinitions('test.sol:simpleContract', output.sources, null) + states = astHelper.extractStatesDefinitions(output.sources, null) + stateDef = state.stateDefinitions + parsedType = decodeInfo.parseType(stateDef[2].typeDescriptions.typeString, states, 'simpleContract', util.extractLocationFromAstVariable(stateDef[2])) + checkDecodeInfo(st, parsedType, 2, 32, 'struct simpleContract.structDef') + parsedType = decodeInfo.parseType(stateDef[3].typeDescriptions.typeString, states, 'simpleContract', util.extractLocationFromAstVariable(stateDef[3])) + checkDecodeInfo(st, parsedType, 6, 32, 'struct simpleContract.structDef[3]') + parsedType = decodeInfo.parseType(stateDef[4].typeDescriptions.typeString, states, 'simpleContract', util.extractLocationFromAstVariable(stateDef[4])) + checkDecodeInfo(st, parsedType, 1, 1, 'enum') - state = astHelper.extractStateDefinitions('test.sol:test2', output.sources, null) - stateDef = state.stateDefinitions - parsedType = decodeInfo.parseType(stateDef[0].typeDescriptions.typeString, states, 'test1', util.extractLocationFromAstVariable(stateDef[0])) - checkDecodeInfo(st, parsedType, 1, 32, 'struct test1.str') + state = astHelper.extractStateDefinitions('test.sol:test2', output.sources, null) + stateDef = state.stateDefinitions + parsedType = decodeInfo.parseType(stateDef[0].typeDescriptions.typeString, states, 'test1', util.extractLocationFromAstVariable(stateDef[0])) + checkDecodeInfo(st, parsedType, 1, 32, 'struct test1.str') - state = stateDecoder.extractStateVariables('test.sol:test2', output.sources) - checkDecodeInfo(st, parsedType, 1, 32, 'struct test1.str') + state = stateDecoder.extractStateVariables('test.sol:test2', output.sources) + checkDecodeInfo(st, parsedType, 1, 32, 'struct test1.str') - st.end() - }) + st.end() + }) }) function checkDecodeInfo (st, decodeInfo, storageSlots, storageBytes, typeName) { - st.equal(decodeInfo.storageSlots, storageSlots) - st.equal(decodeInfo.storageBytes, storageBytes) - st.equal(decodeInfo.typeName, typeName) + st.equal(decodeInfo.storageSlots, storageSlots) + st.equal(decodeInfo.storageBytes, storageBytes) + st.equal(decodeInfo.typeName, typeName) } diff --git a/libs/remix-debug/test/decoder/localDecoder.ts b/libs/remix-debug/test/decoder/localDecoder.ts index e8e92d25f2..18b7889a1c 100644 --- a/libs/remix-debug/test/decoder/localDecoder.ts +++ b/libs/remix-debug/test/decoder/localDecoder.ts @@ -15,62 +15,62 @@ const calldataLocalTest = require('./localsTests/calldata') const compilerInput = require('../helpers/compilerHelper').compilerInput tape('solidity', function (t) { - t.test('local decoder', async function (st) { - const privateKey = Buffer.from('503f38a9c967ed597e47fe25643985f032b072db8075426a92110f82df48dfcb', 'hex') - await test(st, privateKey) - }) + t.test('local decoder', async function (st) { + const privateKey = Buffer.from('503f38a9c967ed597e47fe25643985f032b072db8075426a92110f82df48dfcb', 'hex') + await test(st, privateKey) + }) }) async function test (st, privateKey) { - let output = compiler.compile(compilerInput(intLocal.contract)) - output = JSON.parse(output) - let sources = { - target: 'test.sol', - sources: { 'test.sol': { content: intLocal.contract } } - } - let compilationResults = new CompilerAbstract('json', output, sources) - console.log('intLocalTest') - await intLocalTest(st, privateKey, output.contracts['test.sol']['intLocal'].evm.bytecode.object, compilationResults, intLocal.contract) + let output = compiler.compile(compilerInput(intLocal.contract)) + output = JSON.parse(output) + let sources = { + target: 'test.sol', + sources: { 'test.sol': { content: intLocal.contract } } + } + let compilationResults = new CompilerAbstract('json', output, sources) + console.log('intLocalTest') + await intLocalTest(st, privateKey, output.contracts['test.sol']['intLocal'].evm.bytecode.object, compilationResults, intLocal.contract) - output = compiler.compile(compilerInput(miscLocal.contract)) - output = JSON.parse(output) - sources = { - target: 'test.sol', - sources: { 'test.sol': { content: miscLocal.contract } } - } - compilationResults = new CompilerAbstract('json', output, sources) - console.log('miscLocalTest') - await miscLocalTest(st, privateKey, output.contracts['test.sol']['miscLocal'].evm.bytecode.object, compilationResults, miscLocal.contract) + output = compiler.compile(compilerInput(miscLocal.contract)) + output = JSON.parse(output) + sources = { + target: 'test.sol', + sources: { 'test.sol': { content: miscLocal.contract } } + } + compilationResults = new CompilerAbstract('json', output, sources) + console.log('miscLocalTest') + await miscLocalTest(st, privateKey, output.contracts['test.sol']['miscLocal'].evm.bytecode.object, compilationResults, miscLocal.contract) - output = compiler.compile(compilerInput(miscLocal.contract)) - output = JSON.parse(output) - sources = { - target: 'test.sol', - sources: { 'test.sol': { content: miscLocal.contract } } - } - compilationResults = new CompilerAbstract('json', output, sources) - console.log('misc2LocalTest') - await misc2LocalTest(st, privateKey, output.contracts['test.sol']['miscLocal2'].evm.bytecode.object, compilationResults, miscLocal.contract) + output = compiler.compile(compilerInput(miscLocal.contract)) + output = JSON.parse(output) + sources = { + target: 'test.sol', + sources: { 'test.sol': { content: miscLocal.contract } } + } + compilationResults = new CompilerAbstract('json', output, sources) + console.log('misc2LocalTest') + await misc2LocalTest(st, privateKey, output.contracts['test.sol']['miscLocal2'].evm.bytecode.object, compilationResults, miscLocal.contract) - output = compiler.compile(compilerInput(structArrayLocal.contract)) - output = JSON.parse(output) - sources = { - target: 'test.sol', - sources: { 'test.sol': { content: structArrayLocal.contract } } - } - compilationResults = new CompilerAbstract('json', output, sources) - console.log('structArrayLocalTest') - await structArrayLocalTest(st, privateKey, output.contracts['test.sol']['structArrayLocal'].evm.bytecode.object, compilationResults, structArrayLocal.contract) + output = compiler.compile(compilerInput(structArrayLocal.contract)) + output = JSON.parse(output) + sources = { + target: 'test.sol', + sources: { 'test.sol': { content: structArrayLocal.contract } } + } + compilationResults = new CompilerAbstract('json', output, sources) + console.log('structArrayLocalTest') + await structArrayLocalTest(st, privateKey, output.contracts['test.sol']['structArrayLocal'].evm.bytecode.object, compilationResults, structArrayLocal.contract) - output = compiler.compile(compilerInput(calldataLocal.contract)) - output = JSON.parse(output) - sources = { - target: 'test.sol', - sources: { 'test.sol': { content: calldataLocal.contract } } - } - compilationResults = new CompilerAbstract('json', output, sources) - console.log('calldataLocalTest') - await calldataLocalTest(st, privateKey, output.contracts['test.sol']['calldataLocal'].evm.bytecode.object, compilationResults, calldataLocal.contract) + output = compiler.compile(compilerInput(calldataLocal.contract)) + output = JSON.parse(output) + sources = { + target: 'test.sol', + sources: { 'test.sol': { content: calldataLocal.contract } } + } + compilationResults = new CompilerAbstract('json', output, sources) + console.log('calldataLocalTest') + await calldataLocalTest(st, privateKey, output.contracts['test.sol']['calldataLocal'].evm.bytecode.object, compilationResults, calldataLocal.contract) - st.end() + st.end() } diff --git a/libs/remix-debug/test/decoder/localsTests/calldata.ts b/libs/remix-debug/test/decoder/localsTests/calldata.ts index c198840b7a..8f2ab7a43f 100644 --- a/libs/remix-debug/test/decoder/localsTests/calldata.ts +++ b/libs/remix-debug/test/decoder/localsTests/calldata.ts @@ -11,66 +11,66 @@ import { EventManager } from '../../../src/eventManager' import * as helper from './helper' module.exports = async function (st, privateKey, contractBytecode, compilationResult, contractCode) { - let txHash - let web3 - try { - web3 = await (vmCall as any).getWeb3() - const hash = await (vmCall as any).sendTx(web3, { nonce: 0, privateKey: privateKey }, null, 0, contractBytecode) - const receipt = await web3.eth.getTransactionReceipt(hash) - const to = receipt.contractAddress - console.log('to', to) - // call to level11 - txHash = await (vmCall as any).sendTx(web3, { nonce: 1, privateKey: privateKey }, to, 0, 'a372a595000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000001520000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000015400000000000000000000000000000000000000000000000000000000000000') - } catch (e) { - return st.fail(e) - } - return new Promise((resolve) => { - web3.eth.getTransaction(txHash, function (error, tx) { - if (error) { - return st.fail(error) - } - const traceManager = new TraceManager({ web3 }) - const codeManager = new CodeManager(traceManager) - codeManager.clear() - const solidityProxy = new SolidityProxy({ - getCurrentCalledAddressAt: traceManager.getCurrentCalledAddressAt.bind(traceManager), - getCode: codeManager.getCode.bind(codeManager), - compilationResult: () => compilationResult - }) - const debuggerEvent = new EventManager() - const offsetToLineColumnConverter = { - offsetToLineColumn: (rawLocation) => { - return new Promise((resolve) => { - const lineBreaks = sourceMappingDecoder.getLinebreakPositions(contractCode) - resolve(sourceMappingDecoder.convertOffsetToLineColumn(rawLocation, lineBreaks)) - }) - } - } - const callTree = new InternalCallTree(debuggerEvent, traceManager, solidityProxy, codeManager, { includeLocalVariables: true }, offsetToLineColumnConverter) - callTree.event.register('callTreeBuildFailed', (error) => { - st.fail(error) - }) - callTree.event.register('callTreeNotReady', (reason) => { - st.fail(reason) - }) - callTree.event.register('callTreeReady', (scopes, scopeStarts) => { - helper.decodeLocals(st, 140, traceManager, callTree, function (locals) { - try { - const expected = {"p":{"value":"45","type":"uint256"},"foo":{"length":"1","value":[{"value":"3","type":"uint8"}],"type":"uint8[1]"},"boo":{"length":"1","value":[{"length":"2","value":[{"value":"R","type":"string"},{"value":"T","type":"string"}],"type":"string[2]"}],"type":"string[2][1]"}} - st.deepEqual(locals, expected) - } catch (e) { - st.fail(e.message) - } - resolve({}) - }) - }) + let txHash + let web3 + try { + web3 = await (vmCall as any).getWeb3() + const hash = await (vmCall as any).sendTx(web3, { nonce: 0, privateKey: privateKey }, null, 0, contractBytecode) + const receipt = await web3.eth.getTransactionReceipt(hash) + const to = receipt.contractAddress + console.log('to', to) + // call to level11 + txHash = await (vmCall as any).sendTx(web3, { nonce: 1, privateKey: privateKey }, to, 0, 'a372a595000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000001520000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000015400000000000000000000000000000000000000000000000000000000000000') + } catch (e) { + return st.fail(e) + } + return new Promise((resolve) => { + web3.eth.getTransaction(txHash, function (error, tx) { + if (error) { + return st.fail(error) + } + const traceManager = new TraceManager({ web3 }) + const codeManager = new CodeManager(traceManager) + codeManager.clear() + const solidityProxy = new SolidityProxy({ + getCurrentCalledAddressAt: traceManager.getCurrentCalledAddressAt.bind(traceManager), + getCode: codeManager.getCode.bind(codeManager), + compilationResult: () => compilationResult + }) + const debuggerEvent = new EventManager() + const offsetToLineColumnConverter = { + offsetToLineColumn: (rawLocation) => { + return new Promise((resolve) => { + const lineBreaks = sourceMappingDecoder.getLinebreakPositions(contractCode) + resolve(sourceMappingDecoder.convertOffsetToLineColumn(rawLocation, lineBreaks)) + }) + } + } + const callTree = new InternalCallTree(debuggerEvent, traceManager, solidityProxy, codeManager, { includeLocalVariables: true }, offsetToLineColumnConverter) + callTree.event.register('callTreeBuildFailed', (error) => { + st.fail(error) + }) + callTree.event.register('callTreeNotReady', (reason) => { + st.fail(reason) + }) + callTree.event.register('callTreeReady', (scopes, scopeStarts) => { + helper.decodeLocals(st, 140, traceManager, callTree, function (locals) { + try { + const expected = {"p":{"value":"45","type":"uint256"},"foo":{"length":"1","value":[{"value":"3","type":"uint8"}],"type":"uint8[1]"},"boo":{"length":"1","value":[{"length":"2","value":[{"value":"R","type":"string"},{"value":"T","type":"string"}],"type":"string[2]"}],"type":"string[2][1]"}} + st.deepEqual(locals, expected) + } catch (e) { + st.fail(e.message) + } + resolve({}) + }) + }) - traceManager.resolveTrace(tx).then(() => { - debuggerEvent.trigger('newTraceLoaded', [traceManager.trace]) - }).catch((error) => { - st.fail(error) - }) - }) + traceManager.resolveTrace(tx).then(() => { + debuggerEvent.trigger('newTraceLoaded', [traceManager.trace]) + }).catch((error) => { + st.fail(error) + }) }) + }) } \ No newline at end of file diff --git a/libs/remix-debug/test/decoder/localsTests/helper.ts b/libs/remix-debug/test/decoder/localsTests/helper.ts index d966c16f5a..9e2b7e7d21 100644 --- a/libs/remix-debug/test/decoder/localsTests/helper.ts +++ b/libs/remix-debug/test/decoder/localsTests/helper.ts @@ -5,42 +5,42 @@ import { solidityLocals } from '../../../src/solidity-decoder/localDecoder' Decode local variable */ export function decodeLocals (st, index, traceManager, callTree, verifier) { - try { - traceManager.waterfall([ - function getStackAt (stepIndex, callback) { - try { - const result = traceManager.getStackAt(stepIndex) - callback(null, result) - } catch (error) { - callback(error) - } - }, - function getMemoryAt (stepIndex, callback) { - try { - const result = traceManager.getMemoryAt(stepIndex) - callback(null, result) - } catch (error) { - callback(error) - } - }, - function getCallDataAt (stepIndex, callback) { - try { - const result = traceManager.getCallDataAt(stepIndex) - callback(null, result) - } catch (error) { - callback(error) - } - }], - index, - function (error, result) { - if (error) { - return st.fail(error) - } - solidityLocals(index, callTree, result[0].value, result[1].value, {}, result[2].value, { start: 5000 }, null).then((locals) => { - verifier(locals) - }) - }) - } catch (e) { - st.fail(e.message) - } + try { + traceManager.waterfall([ + function getStackAt (stepIndex, callback) { + try { + const result = traceManager.getStackAt(stepIndex) + callback(null, result) + } catch (error) { + callback(error) + } + }, + function getMemoryAt (stepIndex, callback) { + try { + const result = traceManager.getMemoryAt(stepIndex) + callback(null, result) + } catch (error) { + callback(error) + } + }, + function getCallDataAt (stepIndex, callback) { + try { + const result = traceManager.getCallDataAt(stepIndex) + callback(null, result) + } catch (error) { + callback(error) + } + }], + index, + function (error, result) { + if (error) { + return st.fail(error) + } + solidityLocals(index, callTree, result[0].value, result[1].value, {}, result[2].value, { start: 5000 }, null).then((locals) => { + verifier(locals) + }) + }) + } catch (e) { + st.fail(e.message) + } } diff --git a/libs/remix-debug/test/decoder/localsTests/int.ts b/libs/remix-debug/test/decoder/localsTests/int.ts index 72d02c145b..50e0e714ac 100644 --- a/libs/remix-debug/test/decoder/localsTests/int.ts +++ b/libs/remix-debug/test/decoder/localsTests/int.ts @@ -11,140 +11,140 @@ import * as sourceMappingDecoder from '../../../src/source/sourceMappingDecoder' import * as helper from './helper' module.exports = function (st, privateKey, contractBytecode, compilationResult, contractCode) { - // eslint-disable-next-line no-async-promise-executor - return new Promise(async (resolve) => { - const web3 = await (vmCall as any).getWeb3(); - (vmCall as any).sendTx(web3, { nonce: 0, privateKey: privateKey }, null, 0, contractBytecode, function (error, hash) { - if (error) { - return st.fail(error) - } - web3.eth.getTransaction(hash, function (error, tx) { - if (error) { - return st.fail(error) - } - tx.to = contractCreationToken('0') - const traceManager = new TraceManager({ web3 }) - const codeManager = new CodeManager(traceManager) - codeManager.clear() - const solidityProxy = new SolidityProxy({ - getCurrentCalledAddressAt: traceManager.getCurrentCalledAddressAt.bind(traceManager), - getCode: codeManager.getCode.bind(codeManager), - compilationResult: () => compilationResult - }) - const debuggerEvent = new EventManager() - const offsetToLineColumnConverter = { - offsetToLineColumn: (rawLocation) => { - return new Promise((resolve) => { - const lineBreaks = sourceMappingDecoder.getLinebreakPositions(contractCode) - resolve(sourceMappingDecoder.convertOffsetToLineColumn(rawLocation, lineBreaks)) - }) - } - } - const callTree = new InternalCallTree(debuggerEvent, traceManager, solidityProxy, codeManager, { includeLocalVariables: true }, offsetToLineColumnConverter) - callTree.event.register('callTreeBuildFailed', (error) => { - st.fail(error) - }) - callTree.event.register('callTreeNotReady', (reason) => { - st.fail(reason) - }) - callTree.event.register('callTreeReady', async (scopes, scopeStarts) => { - try { + // eslint-disable-next-line no-async-promise-executor + return new Promise(async (resolve) => { + const web3 = await (vmCall as any).getWeb3(); + (vmCall as any).sendTx(web3, { nonce: 0, privateKey: privateKey }, null, 0, contractBytecode, function (error, hash) { + if (error) { + return st.fail(error) + } + web3.eth.getTransaction(hash, function (error, tx) { + if (error) { + return st.fail(error) + } + tx.to = contractCreationToken('0') + const traceManager = new TraceManager({ web3 }) + const codeManager = new CodeManager(traceManager) + codeManager.clear() + const solidityProxy = new SolidityProxy({ + getCurrentCalledAddressAt: traceManager.getCurrentCalledAddressAt.bind(traceManager), + getCode: codeManager.getCode.bind(codeManager), + compilationResult: () => compilationResult + }) + const debuggerEvent = new EventManager() + const offsetToLineColumnConverter = { + offsetToLineColumn: (rawLocation) => { + return new Promise((resolve) => { + const lineBreaks = sourceMappingDecoder.getLinebreakPositions(contractCode) + resolve(sourceMappingDecoder.convertOffsetToLineColumn(rawLocation, lineBreaks)) + }) + } + } + const callTree = new InternalCallTree(debuggerEvent, traceManager, solidityProxy, codeManager, { includeLocalVariables: true }, offsetToLineColumnConverter) + callTree.event.register('callTreeBuildFailed', (error) => { + st.fail(error) + }) + callTree.event.register('callTreeNotReady', (reason) => { + st.fail(reason) + }) + callTree.event.register('callTreeReady', async (scopes, scopeStarts) => { + try { - // test gas cost per line - st.equals((await callTree.getGasCostPerLine(0, 16)).gasCost, 11) - st.equals((await callTree.getGasCostPerLine(0, 32)).gasCost, 84) + // test gas cost per line + st.equals((await callTree.getGasCostPerLine(0, 16)).gasCost, 11) + st.equals((await callTree.getGasCostPerLine(0, 32)).gasCost, 84) - const functions1 = callTree.retrieveFunctionsStack(103) - const functions2 = callTree.retrieveFunctionsStack(116) - const functions3 = callTree.retrieveFunctionsStack(13) + const functions1 = callTree.retrieveFunctionsStack(103) + const functions2 = callTree.retrieveFunctionsStack(116) + const functions3 = callTree.retrieveFunctionsStack(13) - st.equals(functions1.length, 2) - st.equals(functions2.length, 3) - st.equals(functions3.length, 1) + st.equals(functions1.length, 2) + st.equals(functions2.length, 3) + st.equals(functions3.length, 1) - st.equal(functions1[0].gasCost, 54) + st.equal(functions1[0].gasCost, 54) - st.equals(Object.keys(functions1[0])[0], 'functionDefinition') - st.equals(Object.keys(functions1[0])[1], 'inputs') - st.equals(functions1[0].inputs[0], 'foo') - st.equals(Object.keys(functions2[0])[0], 'functionDefinition') - st.equals(Object.keys(functions2[0])[1], 'inputs') - st.equals(Object.keys(functions2[1])[0], 'functionDefinition') - st.equals(Object.keys(functions2[1])[1], 'inputs') - st.equals(functions2[0].inputs[0], 'asd') - st.equals(functions2[1].inputs[0], 'foo') + st.equals(Object.keys(functions1[0])[0], 'functionDefinition') + st.equals(Object.keys(functions1[0])[1], 'inputs') + st.equals(functions1[0].inputs[0], 'foo') + st.equals(Object.keys(functions2[0])[0], 'functionDefinition') + st.equals(Object.keys(functions2[0])[1], 'inputs') + st.equals(Object.keys(functions2[1])[0], 'functionDefinition') + st.equals(Object.keys(functions2[1])[1], 'inputs') + st.equals(functions2[0].inputs[0], 'asd') + st.equals(functions2[1].inputs[0], 'foo') - st.equals(functions1[0].functionDefinition.name, 'level11') - st.equals(functions2[0].functionDefinition.name, 'level12') - st.equals(functions2[1].functionDefinition.name, 'level11') + st.equals(functions1[0].functionDefinition.name, 'level11') + st.equals(functions2[0].functionDefinition.name, 'level12') + st.equals(functions2[1].functionDefinition.name, 'level11') - st.equals(scopeStarts[0], '1') - st.equals(scopeStarts[10], '1.1') - st.equals(scopeStarts[102], '1.1.1') - st.equals(scopeStarts[115], '1.1.1.1') - st.equals(scopeStarts[136], '1.1.2') - st.equals(scopeStarts[153], '1.1.3') - st.equals(scopeStarts[166], '1.1.3.1') - st.equals(scopes['1.1'].locals['ui8'].type.typeName, 'uint8') - st.equals(scopes['1.1'].locals['ui16'].type.typeName, 'uint16') - st.equals(scopes['1.1'].locals['ui32'].type.typeName, 'uint32') - st.equals(scopes['1.1'].locals['ui64'].type.typeName, 'uint64') - st.equals(scopes['1.1'].locals['ui128'].type.typeName, 'uint128') - st.equals(scopes['1.1'].locals['ui256'].type.typeName, 'uint256') - st.equals(scopes['1.1'].locals['ui'].type.typeName, 'uint256') - st.equals(scopes['1.1'].locals['i8'].type.typeName, 'int8') - st.equals(scopes['1.1'].locals['i16'].type.typeName, 'int16') - st.equals(scopes['1.1'].locals['i32'].type.typeName, 'int32') - st.equals(scopes['1.1'].locals['i64'].type.typeName, 'int64') - st.equals(scopes['1.1'].locals['i128'].type.typeName, 'int128') - st.equals(scopes['1.1'].locals['i256'].type.typeName, 'int256') - st.equals(scopes['1.1'].locals['i'].type.typeName, 'int256') - st.equals(scopes['1.1'].locals['ishrink'].type.typeName, 'int32') - st.equals(scopes['1.1.1'].locals['ui8'].type.typeName, 'uint8') - st.equals(scopes['1.1.1.1'].locals['ui81'].type.typeName, 'uint8') - st.equals(scopes['1.1.2'].locals['ui81'].type.typeName, 'uint8') - st.equals(scopes['1.1.3'].locals['ui8'].type.typeName, 'uint8') - st.equals(scopes['1.1.3.1'].locals['ui81'].type.typeName, 'uint8') - } catch (e) { - st.fail(e.message) - } + st.equals(scopeStarts[0], '1') + st.equals(scopeStarts[10], '1.1') + st.equals(scopeStarts[102], '1.1.1') + st.equals(scopeStarts[115], '1.1.1.1') + st.equals(scopeStarts[136], '1.1.2') + st.equals(scopeStarts[153], '1.1.3') + st.equals(scopeStarts[166], '1.1.3.1') + st.equals(scopes['1.1'].locals['ui8'].type.typeName, 'uint8') + st.equals(scopes['1.1'].locals['ui16'].type.typeName, 'uint16') + st.equals(scopes['1.1'].locals['ui32'].type.typeName, 'uint32') + st.equals(scopes['1.1'].locals['ui64'].type.typeName, 'uint64') + st.equals(scopes['1.1'].locals['ui128'].type.typeName, 'uint128') + st.equals(scopes['1.1'].locals['ui256'].type.typeName, 'uint256') + st.equals(scopes['1.1'].locals['ui'].type.typeName, 'uint256') + st.equals(scopes['1.1'].locals['i8'].type.typeName, 'int8') + st.equals(scopes['1.1'].locals['i16'].type.typeName, 'int16') + st.equals(scopes['1.1'].locals['i32'].type.typeName, 'int32') + st.equals(scopes['1.1'].locals['i64'].type.typeName, 'int64') + st.equals(scopes['1.1'].locals['i128'].type.typeName, 'int128') + st.equals(scopes['1.1'].locals['i256'].type.typeName, 'int256') + st.equals(scopes['1.1'].locals['i'].type.typeName, 'int256') + st.equals(scopes['1.1'].locals['ishrink'].type.typeName, 'int32') + st.equals(scopes['1.1.1'].locals['ui8'].type.typeName, 'uint8') + st.equals(scopes['1.1.1.1'].locals['ui81'].type.typeName, 'uint8') + st.equals(scopes['1.1.2'].locals['ui81'].type.typeName, 'uint8') + st.equals(scopes['1.1.3'].locals['ui8'].type.typeName, 'uint8') + st.equals(scopes['1.1.3.1'].locals['ui81'].type.typeName, 'uint8') + } catch (e) { + st.fail(e.message) + } - helper.decodeLocals(st, 95, traceManager, callTree, function (locals) { - st.equals(Object.keys(locals).length, 16) - st.equals(locals['ui8'].value, '130') - st.equals(locals['ui16'].value, '456') - st.equals(locals['ui32'].value, '4356') - st.equals(locals['ui64'].value, '3543543543') - st.equals(locals['ui128'].value, '234567') - st.equals(locals['ui256'].value, '115792089237316195423570985008687907853269984665640564039457584007880697216513') - st.equals(locals['ui'].value, '123545666') - st.equals(locals['i8'].value, '-45') - st.equals(locals['i16'].value, '-1234') - st.equals(locals['i32'].value, '3455') - st.equals(locals['i64'].value, '-35566') - st.equals(locals['i128'].value, '-444444') - st.equals(locals['i256'].value, '3434343') - st.equals(locals['i'].value, '-32432423423') - st.equals(locals['ishrink'].value, '2') - }) + helper.decodeLocals(st, 95, traceManager, callTree, function (locals) { + st.equals(Object.keys(locals).length, 16) + st.equals(locals['ui8'].value, '130') + st.equals(locals['ui16'].value, '456') + st.equals(locals['ui32'].value, '4356') + st.equals(locals['ui64'].value, '3543543543') + st.equals(locals['ui128'].value, '234567') + st.equals(locals['ui256'].value, '115792089237316195423570985008687907853269984665640564039457584007880697216513') + st.equals(locals['ui'].value, '123545666') + st.equals(locals['i8'].value, '-45') + st.equals(locals['i16'].value, '-1234') + st.equals(locals['i32'].value, '3455') + st.equals(locals['i64'].value, '-35566') + st.equals(locals['i128'].value, '-444444') + st.equals(locals['i256'].value, '3434343') + st.equals(locals['i'].value, '-32432423423') + st.equals(locals['ishrink'].value, '2') + }) - helper.decodeLocals(st, 106, traceManager, callTree, function (locals) { - try { - st.equals(locals['ui8'].value, '123') - st.equals(Object.keys(locals).length, 2) - } catch (e) { - st.fail(e.message) - } - resolve({}) - }) - }) - traceManager.resolveTrace(tx).then(() => { - debuggerEvent.trigger('newTraceLoaded', [traceManager.trace]) - }).catch((error) => { - st.fail(error) - }) - }) + helper.decodeLocals(st, 106, traceManager, callTree, function (locals) { + try { + st.equals(locals['ui8'].value, '123') + st.equals(Object.keys(locals).length, 2) + } catch (e) { + st.fail(e.message) + } + resolve({}) + }) + }) + traceManager.resolveTrace(tx).then(() => { + debuggerEvent.trigger('newTraceLoaded', [traceManager.trace]) + }).catch((error) => { + st.fail(error) }) + }) }) + }) } diff --git a/libs/remix-debug/test/decoder/localsTests/misc.ts b/libs/remix-debug/test/decoder/localsTests/misc.ts index 6ea357fc42..8e7993a0fc 100644 --- a/libs/remix-debug/test/decoder/localsTests/misc.ts +++ b/libs/remix-debug/test/decoder/localsTests/misc.ts @@ -10,80 +10,80 @@ import { CodeManager } from '../../../src/code/codeManager' import * as sourceMappingDecoder from '../../../src/source/sourceMappingDecoder' module.exports = function (st, privateKey, contractBytecode, compilationResult, contractCode) { - // eslint-disable-next-line no-async-promise-executor - return new Promise(async (resolve) => { - const web3 = await (vmCall as any).getWeb3(); - (vmCall as any).sendTx(web3, { nonce: 0, privateKey: privateKey }, null, 0, contractBytecode, function (error, hash) { - if (error) { - return st.fail(error) + // eslint-disable-next-line no-async-promise-executor + return new Promise(async (resolve) => { + const web3 = await (vmCall as any).getWeb3(); + (vmCall as any).sendTx(web3, { nonce: 0, privateKey: privateKey }, null, 0, contractBytecode, function (error, hash) { + if (error) { + return st.fail(error) + } + web3.eth.getTransaction(hash, function (error, tx) { + if (error) { + return st.fail(error) + } + tx.to = contractCreationToken('0') + const traceManager = new TraceManager({ web3 }) + const codeManager = new CodeManager(traceManager) + codeManager.clear() + const solidityProxy = new SolidityProxy({ + getCurrentCalledAddressAt: traceManager.getCurrentCalledAddressAt.bind(traceManager), + getCode: codeManager.getCode.bind(codeManager), + compilationResult: () => compilationResult + }) + const debuggerEvent = new EventManager() + const offsetToLineColumnConverter = { + offsetToLineColumn: (rawLocation) => { + return new Promise((resolve) => { + const lineBreaks = sourceMappingDecoder.getLinebreakPositions(contractCode) + resolve(sourceMappingDecoder.convertOffsetToLineColumn(rawLocation, lineBreaks)) + }) + } + } + const callTree = new InternalCallTree(debuggerEvent, traceManager, solidityProxy, codeManager, { includeLocalVariables: true }, offsetToLineColumnConverter) + callTree.event.register('callTreeBuildFailed', (error) => { + st.fail(error) + }) + callTree.event.register('callTreeReady', (scopes, scopeStarts) => { + helper.decodeLocals(st, 70, traceManager, callTree, function (locals) { + try { + st.equals(locals['boolFalse'].value, false) + st.equals(locals['boolTrue'].value, true) + st.equals(locals['testEnum'].value, 'three') + st.equals(locals['sender'].value, '0x5B38DA6A701C568545DCFCB03FCB875F56BEDDC4') + st.equals(locals['_bytes1'].value, '0x99') + st.equals(locals['__bytes1'].value, '0x99') + st.equals(locals['__bytes2'].value, '0x99AB') + st.equals(locals['__bytes4'].value, '0x99FA0000') + st.equals(locals['__bytes6'].value, '0x990000000000') + st.equals(locals['__bytes7'].value, '0x99356700000000') + st.equals(locals['__bytes8'].value, '0x99ABD41700000000') + st.equals(locals['__bytes9'].value, '0x99156744AF00000000') + st.equals(locals['__bytes13'].value, '0x99123423425300000000000000') + st.equals(locals['__bytes16'].value, '0x99AFAD23432400000000000000000000') + st.equals(locals['__bytes24'].value, '0x99AFAD234324000000000000000000000000000000000000') + st.equals(locals['__bytes32'].value, '0x9999ABD41799ABD4170000000000000000000000000000000000000000000000') + st.equals(Object.keys(locals).length, 16) + } catch (e) { + st.fail(e.message) } - web3.eth.getTransaction(hash, function (error, tx) { - if (error) { - return st.fail(error) - } - tx.to = contractCreationToken('0') - const traceManager = new TraceManager({ web3 }) - const codeManager = new CodeManager(traceManager) - codeManager.clear() - const solidityProxy = new SolidityProxy({ - getCurrentCalledAddressAt: traceManager.getCurrentCalledAddressAt.bind(traceManager), - getCode: codeManager.getCode.bind(codeManager), - compilationResult: () => compilationResult - }) - const debuggerEvent = new EventManager() - const offsetToLineColumnConverter = { - offsetToLineColumn: (rawLocation) => { - return new Promise((resolve) => { - const lineBreaks = sourceMappingDecoder.getLinebreakPositions(contractCode) - resolve(sourceMappingDecoder.convertOffsetToLineColumn(rawLocation, lineBreaks)) - }) - } - } - const callTree = new InternalCallTree(debuggerEvent, traceManager, solidityProxy, codeManager, { includeLocalVariables: true }, offsetToLineColumnConverter) - callTree.event.register('callTreeBuildFailed', (error) => { - st.fail(error) - }) - callTree.event.register('callTreeReady', (scopes, scopeStarts) => { - helper.decodeLocals(st, 70, traceManager, callTree, function (locals) { - try { - st.equals(locals['boolFalse'].value, false) - st.equals(locals['boolTrue'].value, true) - st.equals(locals['testEnum'].value, 'three') - st.equals(locals['sender'].value, '0x5B38DA6A701C568545DCFCB03FCB875F56BEDDC4') - st.equals(locals['_bytes1'].value, '0x99') - st.equals(locals['__bytes1'].value, '0x99') - st.equals(locals['__bytes2'].value, '0x99AB') - st.equals(locals['__bytes4'].value, '0x99FA0000') - st.equals(locals['__bytes6'].value, '0x990000000000') - st.equals(locals['__bytes7'].value, '0x99356700000000') - st.equals(locals['__bytes8'].value, '0x99ABD41700000000') - st.equals(locals['__bytes9'].value, '0x99156744AF00000000') - st.equals(locals['__bytes13'].value, '0x99123423425300000000000000') - st.equals(locals['__bytes16'].value, '0x99AFAD23432400000000000000000000') - st.equals(locals['__bytes24'].value, '0x99AFAD234324000000000000000000000000000000000000') - st.equals(locals['__bytes32'].value, '0x9999ABD41799ABD4170000000000000000000000000000000000000000000000') - st.equals(Object.keys(locals).length, 16) - } catch (e) { - st.fail(e.message) - } - }) + }) - helper.decodeLocals(st, 7, traceManager, callTree, function (locals) { - try { - // st.equals(Object.keys(locals).length, 0) - st.equals(0, 0) - } catch (e) { - st.fail(e.message) - } - resolve({}) - }) - }) - traceManager.resolveTrace(tx).then(() => { - debuggerEvent.trigger('newTraceLoaded', [traceManager.trace]) - }).catch((error) => { - st.fail(error) - }) - }) + helper.decodeLocals(st, 7, traceManager, callTree, function (locals) { + try { + // st.equals(Object.keys(locals).length, 0) + st.equals(0, 0) + } catch (e) { + st.fail(e.message) + } + resolve({}) + }) + }) + traceManager.resolveTrace(tx).then(() => { + debuggerEvent.trigger('newTraceLoaded', [traceManager.trace]) + }).catch((error) => { + st.fail(error) }) - }) + }) + }) + }) } diff --git a/libs/remix-debug/test/decoder/localsTests/misc2.ts b/libs/remix-debug/test/decoder/localsTests/misc2.ts index 4f4342c8b5..f02755ccbd 100644 --- a/libs/remix-debug/test/decoder/localsTests/misc2.ts +++ b/libs/remix-debug/test/decoder/localsTests/misc2.ts @@ -10,66 +10,66 @@ import { CodeManager } from '../../../src/code/codeManager' import * as sourceMappingDecoder from '../../../src/source/sourceMappingDecoder' module.exports = function (st, privateKey, contractBytecode, compilationResult, contractCode) { - // eslint-disable-next-line no-async-promise-executor - return new Promise(async (resolve) => { - const web3 = await (vmCall as any).getWeb3(); - (vmCall as any).sendTx(web3, { nonce: 0, privateKey: privateKey }, null, 0, contractBytecode, function (error, hash) { - if (error) { - return st.fail(error) + // eslint-disable-next-line no-async-promise-executor + return new Promise(async (resolve) => { + const web3 = await (vmCall as any).getWeb3(); + (vmCall as any).sendTx(web3, { nonce: 0, privateKey: privateKey }, null, 0, contractBytecode, function (error, hash) { + if (error) { + return st.fail(error) + } + web3.eth.getTransaction(hash, function (error, tx) { + if (error) { + return st.fail(error) + } + tx.to = contractCreationToken('0') + const traceManager = new TraceManager({ web3 }) + const codeManager = new CodeManager(traceManager) + codeManager.clear() + const solidityProxy = new SolidityProxy({ + getCurrentCalledAddressAt: traceManager.getCurrentCalledAddressAt.bind(traceManager), + getCode: codeManager.getCode.bind(codeManager), + compilationResult: () => compilationResult + }) + const debuggerEvent = new EventManager() + const offsetToLineColumnConverter = { + offsetToLineColumn: (rawLocation) => { + return new Promise((resolve) => { + const lineBreaks = sourceMappingDecoder.getLinebreakPositions(contractCode) + resolve(sourceMappingDecoder.convertOffsetToLineColumn(rawLocation, lineBreaks)) + }) + } + } + const callTree = new InternalCallTree(debuggerEvent, traceManager, solidityProxy, codeManager, { includeLocalVariables: true }, offsetToLineColumnConverter) + callTree.event.register('callTreeBuildFailed', (error) => { + st.fail(error) + }) + callTree.event.register('callTreeReady', (scopes, scopeStarts) => { + helper.decodeLocals(st, 49, traceManager, callTree, function (locals) { + try { + st.equals(locals['dynbytes'].value, '0x64796e616d69636279746573') + st.equals(locals['smallstring'].value, 'test_test_test') + st.equals(Object.keys(locals).length, 2) + } catch (e) { + st.fail(e.message) } - web3.eth.getTransaction(hash, function (error, tx) { - if (error) { - return st.fail(error) - } - tx.to = contractCreationToken('0') - const traceManager = new TraceManager({ web3 }) - const codeManager = new CodeManager(traceManager) - codeManager.clear() - const solidityProxy = new SolidityProxy({ - getCurrentCalledAddressAt: traceManager.getCurrentCalledAddressAt.bind(traceManager), - getCode: codeManager.getCode.bind(codeManager), - compilationResult: () => compilationResult - }) - const debuggerEvent = new EventManager() - const offsetToLineColumnConverter = { - offsetToLineColumn: (rawLocation) => { - return new Promise((resolve) => { - const lineBreaks = sourceMappingDecoder.getLinebreakPositions(contractCode) - resolve(sourceMappingDecoder.convertOffsetToLineColumn(rawLocation, lineBreaks)) - }) - } - } - const callTree = new InternalCallTree(debuggerEvent, traceManager, solidityProxy, codeManager, { includeLocalVariables: true }, offsetToLineColumnConverter) - callTree.event.register('callTreeBuildFailed', (error) => { - st.fail(error) - }) - callTree.event.register('callTreeReady', (scopes, scopeStarts) => { - helper.decodeLocals(st, 49, traceManager, callTree, function (locals) { - try { - st.equals(locals['dynbytes'].value, '0x64796e616d69636279746573') - st.equals(locals['smallstring'].value, 'test_test_test') - st.equals(Object.keys(locals).length, 2) - } catch (e) { - st.fail(e.message) - } - }) + }) - helper.decodeLocals(st, 7, traceManager, callTree, function (locals) { - try { - // st.equals(Object.keys(locals).length, 0) - st.equals(0, 0) - } catch (e) { - st.fail(e.message) - } - resolve({}) - }) - }) - traceManager.resolveTrace(tx).then(() => { - debuggerEvent.trigger('newTraceLoaded', [traceManager.trace]) - }).catch((error) => { - st.fail(error) - }) - }) + helper.decodeLocals(st, 7, traceManager, callTree, function (locals) { + try { + // st.equals(Object.keys(locals).length, 0) + st.equals(0, 0) + } catch (e) { + st.fail(e.message) + } + resolve({}) + }) + }) + traceManager.resolveTrace(tx).then(() => { + debuggerEvent.trigger('newTraceLoaded', [traceManager.trace]) + }).catch((error) => { + st.fail(error) }) - }) + }) + }) + }) } diff --git a/libs/remix-debug/test/decoder/localsTests/structArray.ts b/libs/remix-debug/test/decoder/localsTests/structArray.ts index f060199cbe..188c3a6ea9 100644 --- a/libs/remix-debug/test/decoder/localsTests/structArray.ts +++ b/libs/remix-debug/test/decoder/localsTests/structArray.ts @@ -10,126 +10,126 @@ import { CodeManager } from '../../../src/code/codeManager' import * as sourceMappingDecoder from '../../../src/source/sourceMappingDecoder' module.exports = function (st, privateKey, contractBytecode, compilationResult,contractCode) { - // eslint-disable-next-line no-async-promise-executor - return new Promise(async (resolve) => { - const web3 = await (vmCall as any).getWeb3(); - (vmCall as any).sendTx(web3, { nonce: 0, privateKey: privateKey }, null, 0, contractBytecode, function (error, hash) { - if (error) { - return st.fail(error) - } - web3.eth.getTransaction(hash, function (error, tx) { - if (error) { - return st.fail(error) - } - tx.to = contractCreationToken('0') - const traceManager = new TraceManager({ web3 }) - const codeManager = new CodeManager(traceManager) - codeManager.clear() - const solidityProxy = new SolidityProxy({ - getCurrentCalledAddressAt: traceManager.getCurrentCalledAddressAt.bind(traceManager), - getCode: codeManager.getCode.bind(codeManager), - compilationResult: () => compilationResult - }) - const debuggerEvent = new EventManager() - const offsetToLineColumnConverter = { - offsetToLineColumn: (rawLocation) => { - return new Promise((resolve) => { - const lineBreaks = sourceMappingDecoder.getLinebreakPositions(contractCode) - resolve(sourceMappingDecoder.convertOffsetToLineColumn(rawLocation, lineBreaks)) - }) - } - } - const callTree = new InternalCallTree(debuggerEvent, traceManager, solidityProxy, codeManager, { includeLocalVariables: true }, offsetToLineColumnConverter) - callTree.event.register('callTreeBuildFailed', (error) => { - st.fail(error) - }) - callTree.event.register('callTreeReady', (scopes, scopeStarts) => { - helper.decodeLocals(st, 1622, traceManager, callTree, function (locals) { - try { - console.log('at 1622', locals) - st.equals(locals['bytesSimple'].length, '0x14') - st.equals(locals['bytesSimple'].value, '0x746573745f7375706572') - st.equals(locals['e'].value['a'].value, 'test') - st.equals(locals['e'].value['a'].length, '0x8') - st.equals(locals['e'].value['a'].raw, '0x74657374') - st.equals(locals['e'].value['b'].value, '5') - st.equals(locals['e'].value['c'].length, '0x220') - st.equals(locals['e'].value['c'].raw, '0x746573745f6c6f6e675f746573745f6c6f6e675f746573745f6c6f6e675f74657374746573745f6c6f6e675f746573745f6c6f6e675f746573745f6c6f6e675f74657374746573745f6c6f6e675f746573745f6c6f6e675f746573745f6c6f6e675f74657374746573745f6c6f6e675f746573745f6c6f6e675f746573745f6c6f6e675f74657374746573745f6c6f6e675f746573745f6c6f6e675f746573745f6c6f6e675f74657374746573745f6c6f6e675f746573745f6c6f6e675f746573745f6c6f6e675f74657374746573745f6c6f6e675f746573745f6c6f6e675f746573745f6c6f6e675f74657374746573745f6c6f6e675f746573745f6c6f6e675f746573745f6c6f6e675f74657374') - st.equals(locals['e'].value['c'].value, 'test_long_test_long_test_long_testtest_long_test_long_test_long_testtest_long_test_long_test_long_testtest_long_test_long_test_long_testtest_long_test_long_test_long_testtest_long_test_long_test_long_testtest_long_test_long_test_long_testtest_long_test_long_test_long_test') - st.equals(locals['e'].value['d'].value, '3') - st.equals(locals['f'].length, '0x1b8') - st.equals(locals['f'].raw, '0x746573745f6c6f6e675f746573745f6c6f6e675f746573745f6c6f6e675f746573745f6c6f6e675f746573745f6c6f6e675f746573745f6c6f6e675f746573745f6c6f6e675f746573745f6c6f6e675f746573745f6c6f6e675f746573745f6c6f6e675f746573745f6c6f6e675f746573745f6c6f6e675f746573745f6c6f6e675f746573745f6c6f6e675f746573745f6c6f6e675f746573745f6c6f6e675f746573745f6c6f6e675f746573745f6c6f6e675f746573745f6c6f6e675f746573745f6c6f6e675f746573745f6c6f6e675f746573745f6c6f6e675f') - st.equals(locals['f'].value, 'test_long_test_long_test_long_test_long_test_long_test_long_test_long_test_long_test_long_test_long_test_long_test_long_test_long_test_long_test_long_test_long_test_long_test_long_test_long_test_long_test_long_test_long_') - st.equals(locals['e'].value['e'].value, true) + // eslint-disable-next-line no-async-promise-executor + return new Promise(async (resolve) => { + const web3 = await (vmCall as any).getWeb3(); + (vmCall as any).sendTx(web3, { nonce: 0, privateKey: privateKey }, null, 0, contractBytecode, function (error, hash) { + if (error) { + return st.fail(error) + } + web3.eth.getTransaction(hash, function (error, tx) { + if (error) { + return st.fail(error) + } + tx.to = contractCreationToken('0') + const traceManager = new TraceManager({ web3 }) + const codeManager = new CodeManager(traceManager) + codeManager.clear() + const solidityProxy = new SolidityProxy({ + getCurrentCalledAddressAt: traceManager.getCurrentCalledAddressAt.bind(traceManager), + getCode: codeManager.getCode.bind(codeManager), + compilationResult: () => compilationResult + }) + const debuggerEvent = new EventManager() + const offsetToLineColumnConverter = { + offsetToLineColumn: (rawLocation) => { + return new Promise((resolve) => { + const lineBreaks = sourceMappingDecoder.getLinebreakPositions(contractCode) + resolve(sourceMappingDecoder.convertOffsetToLineColumn(rawLocation, lineBreaks)) + }) + } + } + const callTree = new InternalCallTree(debuggerEvent, traceManager, solidityProxy, codeManager, { includeLocalVariables: true }, offsetToLineColumnConverter) + callTree.event.register('callTreeBuildFailed', (error) => { + st.fail(error) + }) + callTree.event.register('callTreeReady', (scopes, scopeStarts) => { + helper.decodeLocals(st, 1622, traceManager, callTree, function (locals) { + try { + console.log('at 1622', locals) + st.equals(locals['bytesSimple'].length, '0x14') + st.equals(locals['bytesSimple'].value, '0x746573745f7375706572') + st.equals(locals['e'].value['a'].value, 'test') + st.equals(locals['e'].value['a'].length, '0x8') + st.equals(locals['e'].value['a'].raw, '0x74657374') + st.equals(locals['e'].value['b'].value, '5') + st.equals(locals['e'].value['c'].length, '0x220') + st.equals(locals['e'].value['c'].raw, '0x746573745f6c6f6e675f746573745f6c6f6e675f746573745f6c6f6e675f74657374746573745f6c6f6e675f746573745f6c6f6e675f746573745f6c6f6e675f74657374746573745f6c6f6e675f746573745f6c6f6e675f746573745f6c6f6e675f74657374746573745f6c6f6e675f746573745f6c6f6e675f746573745f6c6f6e675f74657374746573745f6c6f6e675f746573745f6c6f6e675f746573745f6c6f6e675f74657374746573745f6c6f6e675f746573745f6c6f6e675f746573745f6c6f6e675f74657374746573745f6c6f6e675f746573745f6c6f6e675f746573745f6c6f6e675f74657374746573745f6c6f6e675f746573745f6c6f6e675f746573745f6c6f6e675f74657374') + st.equals(locals['e'].value['c'].value, 'test_long_test_long_test_long_testtest_long_test_long_test_long_testtest_long_test_long_test_long_testtest_long_test_long_test_long_testtest_long_test_long_test_long_testtest_long_test_long_test_long_testtest_long_test_long_test_long_testtest_long_test_long_test_long_test') + st.equals(locals['e'].value['d'].value, '3') + st.equals(locals['f'].length, '0x1b8') + st.equals(locals['f'].raw, '0x746573745f6c6f6e675f746573745f6c6f6e675f746573745f6c6f6e675f746573745f6c6f6e675f746573745f6c6f6e675f746573745f6c6f6e675f746573745f6c6f6e675f746573745f6c6f6e675f746573745f6c6f6e675f746573745f6c6f6e675f746573745f6c6f6e675f746573745f6c6f6e675f746573745f6c6f6e675f746573745f6c6f6e675f746573745f6c6f6e675f746573745f6c6f6e675f746573745f6c6f6e675f746573745f6c6f6e675f746573745f6c6f6e675f746573745f6c6f6e675f746573745f6c6f6e675f746573745f6c6f6e675f') + st.equals(locals['f'].value, 'test_long_test_long_test_long_test_long_test_long_test_long_test_long_test_long_test_long_test_long_test_long_test_long_test_long_test_long_test_long_test_long_test_long_test_long_test_long_test_long_test_long_test_long_') + st.equals(locals['e'].value['e'].value, true) - st.equals(locals['simpleArray'].value[0].value, '45') - st.equals(locals['simpleArray'].value[1].value, '324324') - st.equals(locals['simpleArray'].value[2].value, '-333') - st.equals(locals['simpleArray'].value[3].value, '5656') - st.equals(locals['simpleArray'].value[4].value, '-1111') + st.equals(locals['simpleArray'].value[0].value, '45') + st.equals(locals['simpleArray'].value[1].value, '324324') + st.equals(locals['simpleArray'].value[2].value, '-333') + st.equals(locals['simpleArray'].value[3].value, '5656') + st.equals(locals['simpleArray'].value[4].value, '-1111') - st.equals(locals['stringArray'].value[0].value, 'long_one_long_one_long_one_long_one_long_one_long_one_long_one_long_one_long_one_long_one_long_one_long_one_long_one_long_one_long_one_') - st.equals(locals['stringArray'].value[1].value, 'two') - st.equals(locals['stringArray'].value[2].value, 'three') + st.equals(locals['stringArray'].value[0].value, 'long_one_long_one_long_one_long_one_long_one_long_one_long_one_long_one_long_one_long_one_long_one_long_one_long_one_long_one_long_one_') + st.equals(locals['stringArray'].value[1].value, 'two') + st.equals(locals['stringArray'].value[2].value, 'three') - st.equals(locals['dynArray'].value[0].value[0].value, '3423423532') - st.equals(locals['dynArray'].value[1].value[0].value, '-342343323532') - st.equals(locals['dynArray'].value[1].value[1].value, '23432') - st.equals(locals['dynArray'].value[2].value[0].value, '-432432') - st.equals(locals['dynArray'].value[2].value[1].value, '3423423532') - st.equals(locals['dynArray'].value[2].value[2].value, '-432432') + st.equals(locals['dynArray'].value[0].value[0].value, '3423423532') + st.equals(locals['dynArray'].value[1].value[0].value, '-342343323532') + st.equals(locals['dynArray'].value[1].value[1].value, '23432') + st.equals(locals['dynArray'].value[2].value[0].value, '-432432') + st.equals(locals['dynArray'].value[2].value[1].value, '3423423532') + st.equals(locals['dynArray'].value[2].value[2].value, '-432432') - st.equals(locals['structArray'].value[0].value['a'].value, 'test') - st.equals(locals['structArray'].value[0].value['a'].length, '0x8') - st.equals(locals['structArray'].value[0].value['a'].raw, '0x74657374') - st.equals(locals['structArray'].value[0].value['b'].value, '5') - st.equals(locals['structArray'].value[0].value['c'].length, '0x220') - st.equals(locals['structArray'].value[0].value['c'].raw, '0x746573745f6c6f6e675f746573745f6c6f6e675f746573745f6c6f6e675f74657374746573745f6c6f6e675f746573745f6c6f6e675f746573745f6c6f6e675f74657374746573745f6c6f6e675f746573745f6c6f6e675f746573745f6c6f6e675f74657374746573745f6c6f6e675f746573745f6c6f6e675f746573745f6c6f6e675f74657374746573745f6c6f6e675f746573745f6c6f6e675f746573745f6c6f6e675f74657374746573745f6c6f6e675f746573745f6c6f6e675f746573745f6c6f6e675f74657374746573745f6c6f6e675f746573745f6c6f6e675f746573745f6c6f6e675f74657374746573745f6c6f6e675f746573745f6c6f6e675f746573745f6c6f6e675f74657374') - st.equals(locals['structArray'].value[0].value['c'].value, 'test_long_test_long_test_long_testtest_long_test_long_test_long_testtest_long_test_long_test_long_testtest_long_test_long_test_long_testtest_long_test_long_test_long_testtest_long_test_long_test_long_testtest_long_test_long_test_long_testtest_long_test_long_test_long_test') - st.equals(locals['structArray'].value[0].value['d'].value, '3') - st.equals(locals['structArray'].value[0].value['e'].value, true) + st.equals(locals['structArray'].value[0].value['a'].value, 'test') + st.equals(locals['structArray'].value[0].value['a'].length, '0x8') + st.equals(locals['structArray'].value[0].value['a'].raw, '0x74657374') + st.equals(locals['structArray'].value[0].value['b'].value, '5') + st.equals(locals['structArray'].value[0].value['c'].length, '0x220') + st.equals(locals['structArray'].value[0].value['c'].raw, '0x746573745f6c6f6e675f746573745f6c6f6e675f746573745f6c6f6e675f74657374746573745f6c6f6e675f746573745f6c6f6e675f746573745f6c6f6e675f74657374746573745f6c6f6e675f746573745f6c6f6e675f746573745f6c6f6e675f74657374746573745f6c6f6e675f746573745f6c6f6e675f746573745f6c6f6e675f74657374746573745f6c6f6e675f746573745f6c6f6e675f746573745f6c6f6e675f74657374746573745f6c6f6e675f746573745f6c6f6e675f746573745f6c6f6e675f74657374746573745f6c6f6e675f746573745f6c6f6e675f746573745f6c6f6e675f74657374746573745f6c6f6e675f746573745f6c6f6e675f746573745f6c6f6e675f74657374') + st.equals(locals['structArray'].value[0].value['c'].value, 'test_long_test_long_test_long_testtest_long_test_long_test_long_testtest_long_test_long_test_long_testtest_long_test_long_test_long_testtest_long_test_long_test_long_testtest_long_test_long_test_long_testtest_long_test_long_test_long_testtest_long_test_long_test_long_test') + st.equals(locals['structArray'].value[0].value['d'].value, '3') + st.equals(locals['structArray'].value[0].value['e'].value, true) - st.equals(locals['structArray'].value[1].value['a'].value, 'item1 a') - st.equals(locals['structArray'].value[1].value['b'].value, '20') - st.equals(locals['structArray'].value[1].value['c'].value, 'item1 c') - st.equals(locals['structArray'].value[1].value['d'].value, '-45') - st.equals(locals['structArray'].value[1].value['e'].value, false) + st.equals(locals['structArray'].value[1].value['a'].value, 'item1 a') + st.equals(locals['structArray'].value[1].value['b'].value, '20') + st.equals(locals['structArray'].value[1].value['c'].value, 'item1 c') + st.equals(locals['structArray'].value[1].value['d'].value, '-45') + st.equals(locals['structArray'].value[1].value['e'].value, false) - st.equals(locals['structArray'].value[2].value['a'].value, 'item2 a') - st.equals(locals['structArray'].value[2].value['b'].value, '200') - st.equals(locals['structArray'].value[2].value['c'].value, 'item2 c') - st.equals(locals['structArray'].value[2].value['d'].value, '-450') - st.equals(locals['structArray'].value[2].value['e'].value, true) + st.equals(locals['structArray'].value[2].value['a'].value, 'item2 a') + st.equals(locals['structArray'].value[2].value['b'].value, '200') + st.equals(locals['structArray'].value[2].value['c'].value, 'item2 c') + st.equals(locals['structArray'].value[2].value['d'].value, '-450') + st.equals(locals['structArray'].value[2].value['e'].value, true) - st.equals(locals['arrayStruct'].value.a.value[0].value, 'string') - st.equals(locals['arrayStruct'].value.b.value[0].value, '34') - st.equals(locals['arrayStruct'].value.b.value[1].value, '-23') - st.equals(locals['arrayStruct'].value.b.value[2].value, '-3') - st.equals(locals['arrayStruct'].value.c.value, 'three') + st.equals(locals['arrayStruct'].value.a.value[0].value, 'string') + st.equals(locals['arrayStruct'].value.b.value[0].value, '34') + st.equals(locals['arrayStruct'].value.b.value[1].value, '-23') + st.equals(locals['arrayStruct'].value.b.value[2].value, '-3') + st.equals(locals['arrayStruct'].value.c.value, 'three') - st.equals(Object.keys(locals).length, 8) - } catch (e) { - st.fail(e.message) - } - }) + st.equals(Object.keys(locals).length, 8) + } catch (e) { + st.fail(e.message) + } + }) - helper.decodeLocals(st, 7, traceManager, callTree, function (locals) { - try { - console.log('at 7', locals) - st.equals(0, 0) - // st.equals(Object.keys(locals).length, 0) - } catch (e) { - st.fail(e.message) - } - resolve({}) - }) - }) - traceManager.resolveTrace(tx).then(() => { - debuggerEvent.trigger('newTraceLoaded', [traceManager.trace]) - }).catch((error) => { - st.fail(error) - }) - }) + helper.decodeLocals(st, 7, traceManager, callTree, function (locals) { + try { + console.log('at 7', locals) + st.equals(0, 0) + // st.equals(Object.keys(locals).length, 0) + } catch (e) { + st.fail(e.message) + } + resolve({}) + }) + }) + traceManager.resolveTrace(tx).then(() => { + debuggerEvent.trigger('newTraceLoaded', [traceManager.trace]) + }).catch((error) => { + st.fail(error) }) - }) + }) + }) + }) } diff --git a/libs/remix-debug/test/decoder/mockStorageResolver.ts b/libs/remix-debug/test/decoder/mockStorageResolver.ts index 63cbb6d5b3..b708288b87 100644 --- a/libs/remix-debug/test/decoder/mockStorageResolver.ts +++ b/libs/remix-debug/test/decoder/mockStorageResolver.ts @@ -3,37 +3,37 @@ import { util } from '@remix-project/remix-lib' export class MockStorageResolver { - storage - - constructor (_storage) { - this.storage = {} - for (const k in _storage) { - const hashed = util.sha3_256(k) - this.storage[hashed] = { - hashed: hashed, - key: k, - value: _storage[k] - } - } + storage + + constructor (_storage) { + this.storage = {} + for (const k in _storage) { + const hashed = util.sha3_256(k) + this.storage[hashed] = { + hashed: hashed, + key: k, + value: _storage[k] + } } + } - storageRange (callback) { - callback(null, this.storage) - } + storageRange (callback) { + callback(null, this.storage) + } - storageSlot (slot, callback) { - const hashed = util.sha3_256(slot) - callback(null, this.storage[hashed]) - } + storageSlot (slot, callback) { + const hashed = util.sha3_256(slot) + callback(null, this.storage[hashed]) + } - isComplete (address) { - return true - } + isComplete (address) { + return true + } - fromCache (address, slotKey) { - return this.storage[slotKey] - } + fromCache (address, slotKey) { + return this.storage[slotKey] + } - toCache (address, storage, complete) { - } + toCache (address, storage, complete) { + } } diff --git a/libs/remix-debug/test/decoder/stateTests/mapping.ts b/libs/remix-debug/test/decoder/stateTests/mapping.ts index 52b457e8ce..bb71bfb687 100644 --- a/libs/remix-debug/test/decoder/stateTests/mapping.ts +++ b/libs/remix-debug/test/decoder/stateTests/mapping.ts @@ -13,96 +13,96 @@ import { StorageViewer } from '../../../src/storage/storageViewer' import { Address, bufferToHex } from '@ethereumjs/util' module.exports = async function testMappingStorage (st, cb) { - const mappingStorage = require('../contracts/mappingStorage') - const privateKey = Buffer.from('503f38a9c967ed597e47fe25643985f032b072db8075426a92110f82df48dfcb', 'hex') - let output = compile(compilerInput(mappingStorage.contract)) - output = JSON.parse(output); - const sources = { - target: 'test.sol', - sources: { 'test.sol': { content: mappingStorage.contract } } - } - const compilationResults = new CompilerAbstract('json', output, sources) - const web3 = await (vmCall as any).getWeb3(); - (vmCall as any).sendTx(web3, {nonce: 0, privateKey: privateKey}, null, 0, output.contracts['test.sol']['SimpleMappingState'].evm.bytecode.object, function (error, hash) { + const mappingStorage = require('../contracts/mappingStorage') + const privateKey = Buffer.from('503f38a9c967ed597e47fe25643985f032b072db8075426a92110f82df48dfcb', 'hex') + let output = compile(compilerInput(mappingStorage.contract)) + output = JSON.parse(output); + const sources = { + target: 'test.sol', + sources: { 'test.sol': { content: mappingStorage.contract } } + } + const compilationResults = new CompilerAbstract('json', output, sources) + const web3 = await (vmCall as any).getWeb3(); + (vmCall as any).sendTx(web3, {nonce: 0, privateKey: privateKey}, null, 0, output.contracts['test.sol']['SimpleMappingState'].evm.bytecode.object, function (error, hash) { + if (error) { + console.log(error) + st.end(error) + } else { + web3.eth.getTransactionReceipt(hash, (error, tx) => { if (error) { - console.log(error) - st.end(error) + console.log(error) + st.end(error) } else { - web3.eth.getTransactionReceipt(hash, (error, tx) => { - if (error) { - console.log(error) - st.end(error) - } else { - // const storage = await this.vm.stateManager.dumpStorage(data.to) - // (vmCall as any).web3().eth.getCode(tx.contractAddress).then((code) => console.log('code:', code)) - // (vmCall as any).web3().debug.traceTransaction(hash).then((code) => console.log('trace:', code)) - testMapping(st, privateKey, tx.contractAddress, output, compilationResults, web3, cb) - // st.end() - } - }) + // const storage = await this.vm.stateManager.dumpStorage(data.to) + // (vmCall as any).web3().eth.getCode(tx.contractAddress).then((code) => console.log('code:', code)) + // (vmCall as any).web3().debug.traceTransaction(hash).then((code) => console.log('trace:', code)) + testMapping(st, privateKey, tx.contractAddress, output, compilationResults, web3, cb) + // st.end() } - }) + }) + } + }) } function testMapping (st, privateKey, contractAddress, output, compilationResults, web3, cb) { - (vmCall as any).sendTx(web3, {nonce: 1, privateKey: privateKey}, contractAddress, 0, '2fd0a83a00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000001074686973206973206120737472696e6700000000000000000000000000000000', - function (error, hash) { - if (error) { - console.log(error) - st.end(error) - } else { - web3.eth.getTransaction(hash, (error, tx) => { - if (error) { - console.log(error) - st.end(error) - } else { - const traceManager = new TraceManager({ web3 }) - const codeManager = new CodeManager(traceManager) - codeManager.clear() - console.log(compilationResults) - const solidityProxy = new SolidityProxy({ - getCurrentCalledAddressAt: traceManager.getCurrentCalledAddressAt.bind(traceManager), - getCode: codeManager.getCode.bind(codeManager), - compilationResult: () => compilationResults - }) - const debuggerEvent = new EventManager() - const callTree = new InternalCallTree(debuggerEvent, traceManager, solidityProxy, codeManager, { includeLocalVariables: true }) - callTree.event.register('callTreeBuildFailed', (error) => { - st.fail(error) - }) - callTree.event.register('callTreeNotReady', (reason) => { - st.fail(reason) - }) - callTree.event.register('callTreeReady', (scopes, scopeStarts) => { - const storageViewer = new StorageViewer({ - stepIndex: 268, - tx: tx, - address: contractAddress - }, new StorageResolver({web3}), traceManager) - const stateVars = stateDecoder.extractStateVariables('SimpleMappingState', output.sources) - stateDecoder.decodeState(stateVars, storageViewer).then((result) => { - st.equal(result['_num'].value, '1') - st.equal(result['_num'].type, 'uint256') - st.equal(result['_iBreakSolidityState'].type, 'mapping(string => uint256)') - st.equal(result['_iBreakSolidityState'].value['74686973206973206120737472696e67'].value, '1') - st.equal(result['_iBreakSolidityState'].value['74686973206973206120737472696e67'].type, 'uint256') - st.equal(result['_iBreakSolidityStateInt'].type, 'mapping(uint256 => uint256)') - st.equal(result['_iBreakSolidityStateInt'].value['0000000000000000000000000000000000000000000000000000000000000001'].value, '1') - st.equal(result['_iBreakSolidityStateInt'].value['0000000000000000000000000000000000000000000000000000000000000001'].type, 'uint256') - cb() - }, (reason) => { - console.log('fail') - st.end(reason) - }) - }) + (vmCall as any).sendTx(web3, {nonce: 1, privateKey: privateKey}, contractAddress, 0, '2fd0a83a00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000001074686973206973206120737472696e6700000000000000000000000000000000', + function (error, hash) { + if (error) { + console.log(error) + st.end(error) + } else { + web3.eth.getTransaction(hash, (error, tx) => { + if (error) { + console.log(error) + st.end(error) + } else { + const traceManager = new TraceManager({ web3 }) + const codeManager = new CodeManager(traceManager) + codeManager.clear() + console.log(compilationResults) + const solidityProxy = new SolidityProxy({ + getCurrentCalledAddressAt: traceManager.getCurrentCalledAddressAt.bind(traceManager), + getCode: codeManager.getCode.bind(codeManager), + compilationResult: () => compilationResults + }) + const debuggerEvent = new EventManager() + const callTree = new InternalCallTree(debuggerEvent, traceManager, solidityProxy, codeManager, { includeLocalVariables: true }) + callTree.event.register('callTreeBuildFailed', (error) => { + st.fail(error) + }) + callTree.event.register('callTreeNotReady', (reason) => { + st.fail(reason) + }) + callTree.event.register('callTreeReady', (scopes, scopeStarts) => { + const storageViewer = new StorageViewer({ + stepIndex: 268, + tx: tx, + address: contractAddress + }, new StorageResolver({web3}), traceManager) + const stateVars = stateDecoder.extractStateVariables('SimpleMappingState', output.sources) + stateDecoder.decodeState(stateVars, storageViewer).then((result) => { + st.equal(result['_num'].value, '1') + st.equal(result['_num'].type, 'uint256') + st.equal(result['_iBreakSolidityState'].type, 'mapping(string => uint256)') + st.equal(result['_iBreakSolidityState'].value['74686973206973206120737472696e67'].value, '1') + st.equal(result['_iBreakSolidityState'].value['74686973206973206120737472696e67'].type, 'uint256') + st.equal(result['_iBreakSolidityStateInt'].type, 'mapping(uint256 => uint256)') + st.equal(result['_iBreakSolidityStateInt'].value['0000000000000000000000000000000000000000000000000000000000000001'].value, '1') + st.equal(result['_iBreakSolidityStateInt'].value['0000000000000000000000000000000000000000000000000000000000000001'].type, 'uint256') + cb() + }, (reason) => { + console.log('fail') + st.end(reason) + }) + }) - traceManager.resolveTrace(tx).then(() => { - debuggerEvent.trigger('newTraceLoaded', [traceManager.trace]) - }).catch((error) => { - st.fail(error) - }) - } - }) - } + traceManager.resolveTrace(tx).then(() => { + debuggerEvent.trigger('newTraceLoaded', [traceManager.trace]) + }).catch((error) => { + st.fail(error) + }) + } }) + } + }) } diff --git a/libs/remix-debug/test/decoder/storageDecoder.ts b/libs/remix-debug/test/decoder/storageDecoder.ts index 9c1e3b0535..72ec3da942 100644 --- a/libs/remix-debug/test/decoder/storageDecoder.ts +++ b/libs/remix-debug/test/decoder/storageDecoder.ts @@ -7,273 +7,273 @@ import { compilerInput } from '../helpers/compilerHelper' const testMappingStorage = require('./stateTests/mapping') tape('solidity', function (t) { - t.test('storage decoder', function (st) { - console.log('test int storage') - testIntStorage(st, function () { - console.log('test byte storage') - testByteStorage(st, function () { - console.log('test struct storage') - testStructArrayStorage(st, function () { - console.log('test mapping storage') - testMappingStorage(st, function () { - st.end() - }) - }) - }) + t.test('storage decoder', function (st) { + console.log('test int storage') + testIntStorage(st, function () { + console.log('test byte storage') + testByteStorage(st, function () { + console.log('test struct storage') + testStructArrayStorage(st, function () { + console.log('test mapping storage') + testMappingStorage(st, function () { + st.end() + }) }) + }) }) + }) }) function testIntStorage (st, cb) { - const intStorage = require('./contracts/intStorage') - let output = compile(compilerInput(intStorage.contract)) - output = JSON.parse(output) - let mockStorageResolver - for (const storage of [intStorage.fullStorage, shrinkStorage(intStorage.fullStorage)]) { - mockStorageResolver = new MockStorageResolver(storage) - stateDecoder.solidityState(mockStorageResolver, output.sources, 'intStorage').then((decoded) => { - st.equal(decoded['ui8'].value, '130') - st.equal(decoded['ui16'].value, '456') - st.equal(decoded['ui32'].value, '4356') - st.equal(decoded['ui64'].value, '3543543543') - st.equal(decoded['ui128'].value, '234567') - st.equal(decoded['ui256'].value, '115792089237316195423570985008687907853269984665640564039457584007880697216513') - st.equal(decoded['ui'].value, '123545666') - st.equal(decoded['i8'].value, '-45') - st.equal(decoded['i16'].value, '-1234') - st.equal(decoded['i32'].value, '3455') - st.equal(decoded['i64'].value, '-35566') - st.equal(decoded['i128'].value, '-444444') - st.equal(decoded['i256'].value, '3434343') - st.equal(decoded['i'].value, '-32432423423') - st.equal(decoded['ishrink'].value, '2') - }) - } - - mockStorageResolver = new MockStorageResolver({}) + const intStorage = require('./contracts/intStorage') + let output = compile(compilerInput(intStorage.contract)) + output = JSON.parse(output) + let mockStorageResolver + for (const storage of [intStorage.fullStorage, shrinkStorage(intStorage.fullStorage)]) { + mockStorageResolver = new MockStorageResolver(storage) stateDecoder.solidityState(mockStorageResolver, output.sources, 'intStorage').then((decoded) => { - st.equal(decoded['ui8'].value, '0') - st.equal(decoded['ui16'].value, '0') - st.equal(decoded['ui32'].value, '0') - st.equal(decoded['ui64'].value, '0') - st.equal(decoded['ui128'].value, '0') - st.equal(decoded['ui256'].value, '0') - st.equal(decoded['ui'].value, '0') - st.equal(decoded['i8'].value, '0') - st.equal(decoded['i16'].value, '0') - st.equal(decoded['i32'].value, '0') - st.equal(decoded['i64'].value, '0') - st.equal(decoded['i128'].value, '0') - st.equal(decoded['i256'].value, '0') - st.equal(decoded['i'].value, '0') - st.equal(decoded['ishrink'].value, '0') - cb() + st.equal(decoded['ui8'].value, '130') + st.equal(decoded['ui16'].value, '456') + st.equal(decoded['ui32'].value, '4356') + st.equal(decoded['ui64'].value, '3543543543') + st.equal(decoded['ui128'].value, '234567') + st.equal(decoded['ui256'].value, '115792089237316195423570985008687907853269984665640564039457584007880697216513') + st.equal(decoded['ui'].value, '123545666') + st.equal(decoded['i8'].value, '-45') + st.equal(decoded['i16'].value, '-1234') + st.equal(decoded['i32'].value, '3455') + st.equal(decoded['i64'].value, '-35566') + st.equal(decoded['i128'].value, '-444444') + st.equal(decoded['i256'].value, '3434343') + st.equal(decoded['i'].value, '-32432423423') + st.equal(decoded['ishrink'].value, '2') }) + } + + mockStorageResolver = new MockStorageResolver({}) + stateDecoder.solidityState(mockStorageResolver, output.sources, 'intStorage').then((decoded) => { + st.equal(decoded['ui8'].value, '0') + st.equal(decoded['ui16'].value, '0') + st.equal(decoded['ui32'].value, '0') + st.equal(decoded['ui64'].value, '0') + st.equal(decoded['ui128'].value, '0') + st.equal(decoded['ui256'].value, '0') + st.equal(decoded['ui'].value, '0') + st.equal(decoded['i8'].value, '0') + st.equal(decoded['i16'].value, '0') + st.equal(decoded['i32'].value, '0') + st.equal(decoded['i64'].value, '0') + st.equal(decoded['i128'].value, '0') + st.equal(decoded['i256'].value, '0') + st.equal(decoded['i'].value, '0') + st.equal(decoded['ishrink'].value, '0') + cb() + }) } function testByteStorage (st, cb) { - const byteStorage = require('./contracts/byteStorage') - let output = compile(compilerInput(byteStorage.contract)) - output = JSON.parse(output) - let mockStorageResolver - for (const storage of [byteStorage.storage, shrinkStorage(byteStorage.storage)]) { - mockStorageResolver = new MockStorageResolver(storage) - stateDecoder.solidityState(mockStorageResolver, output.sources, 'byteStorage').then((decoded) => { - st.equal(decoded['b1'].value, false) - st.equal(decoded['a1'].value, '0xFE350F199F244AC9A79038D254400B632A633225') - st.equal(decoded['b2'].value, true) - st.equal(decoded['dynb1'].value, '0x64796e616d69636279746573') - st.equal(decoded['dynb1'].length, '0xc') - st.equal(decoded['stab'].value, '0x01') - st.equal(decoded['stab1'].value, '0x12') - st.equal(decoded['stab2'].value, '0x1579') - st.equal(decoded['stab3'].value, '0x359356') - st.equal(decoded['stab4'].value, '0x23750000') - st.equal(decoded['stab5'].value, '0x0235764500') - st.equal(decoded['stab6'].value, '0x324435000000') - st.equal(decoded['stab7'].value, '0x00432400000000') - st.equal(decoded['stab8'].value, '0x3245546457650000') - st.equal(decoded['stab9'].value, '0x034345430000000000') - st.equal(decoded['stab10'].value, '0x04543543654657000000') - st.equal(decoded['stab11'].value, '0x5435465400000000000000') - st.equal(decoded['stab12'].value, '0x030000000000000000000000') - st.equal(decoded['stab13'].value, '0x03243242345435000000000000') - st.equal(decoded['stab14'].value, '0x3245435435435300000000000000') - st.equal(decoded['stab15'].value, '0x032454434435000000000000000000') - st.equal(decoded['stab16'].value, '0x32454354440000000000000000000000') - st.equal(decoded['stab17'].value, '0x0324543432432432450000000000000000') - st.equal(decoded['stab18'].value, '0x032453432543543500000000000000000000') - st.equal(decoded['stab19'].value, '0x03245434354354350000000000000000000000') - st.equal(decoded['stab20'].value, '0x032454543543AB35000000000000000000000000') - st.equal(decoded['stab21'].value, '0x324544324234350000000000000000000000000000') - st.equal(decoded['stab22'].value, '0x324543AEF50000000000000000000000000000000000') - st.equal(decoded['stab23'].value, '0x3245435FFF000000000000000000000000000000000000') - st.equal(decoded['stab24'].value, '0x3245435F0000000000000000000000000000000000000000') - st.equal(decoded['stab25'].value, '0x3245435F000000000000000000000000000000000000000000') - st.equal(decoded['stab26'].value, '0x3245435F00000000000000000000000000000000000000000000') - st.equal(decoded['stab27'].value, '0x03245FFFFFFF000000000000000000000000000000000000000000') - st.equal(decoded['stab28'].value, '0x03241235000000000000000000000000000000000000000000000000') - st.equal(decoded['stab29'].value, '0x0325213213000000000000000000000000000000000000000000000000') - st.equal(decoded['stab30'].value, '0x032454352324230000000000000000000000000000000000000000000000') - st.equal(decoded['stab31'].value, '0x32454351230000000000000000000000000000000000000000000000000000') - st.equal(decoded['stab32'].value, '0x324324423432543543AB00000000000000000000000000000000000000000000') - st.equal(decoded['enumDec'].value, 'e240') - st.equal(decoded['str1'].value, 'short') - st.equal(decoded['str12'].value, 'шеллы') - st.equal(decoded['str2'].value, 'long__long__long__long__long__long__long__long__long__long__long__long__long__long__long__long__long__long__long__long__long__long__long__long__long__long__long__long__long__long__long__long__long__long__long__long__long__long__long__long__long__long__long__long__long__long__long__long') - }) - } - - mockStorageResolver = new MockStorageResolver({}) + const byteStorage = require('./contracts/byteStorage') + let output = compile(compilerInput(byteStorage.contract)) + output = JSON.parse(output) + let mockStorageResolver + for (const storage of [byteStorage.storage, shrinkStorage(byteStorage.storage)]) { + mockStorageResolver = new MockStorageResolver(storage) stateDecoder.solidityState(mockStorageResolver, output.sources, 'byteStorage').then((decoded) => { - st.equal(decoded['b1'].value, false) - st.equal(decoded['a1'].value, '0x0000000000000000000000000000000000000000') - st.equal(decoded['b2'].value, false) - st.equal(decoded['dynb1'].value, '0x') - st.equal(decoded['dynb1'].length, '0x0') - st.equal(decoded['stab'].value, '0x00') - st.equal(decoded['stab1'].value, '0x00') - st.equal(decoded['stab2'].value, '0x0000') - st.equal(decoded['stab3'].value, '0x000000') - st.equal(decoded['stab4'].value, '0x00000000') - st.equal(decoded['stab5'].value, '0x0000000000') - st.equal(decoded['stab6'].value, '0x000000000000') - st.equal(decoded['stab7'].value, '0x00000000000000') - st.equal(decoded['stab8'].value, '0x0000000000000000') - st.equal(decoded['stab9'].value, '0x000000000000000000') - st.equal(decoded['stab10'].value, '0x00000000000000000000') - st.equal(decoded['stab11'].value, '0x0000000000000000000000') - st.equal(decoded['stab12'].value, '0x000000000000000000000000') - st.equal(decoded['stab13'].value, '0x00000000000000000000000000') - st.equal(decoded['stab14'].value, '0x0000000000000000000000000000') - st.equal(decoded['stab15'].value, '0x000000000000000000000000000000') - st.equal(decoded['stab16'].value, '0x00000000000000000000000000000000') - st.equal(decoded['stab17'].value, '0x0000000000000000000000000000000000') - st.equal(decoded['stab18'].value, '0x000000000000000000000000000000000000') - st.equal(decoded['stab19'].value, '0x00000000000000000000000000000000000000') - st.equal(decoded['stab20'].value, '0x0000000000000000000000000000000000000000') - st.equal(decoded['stab21'].value, '0x000000000000000000000000000000000000000000') - st.equal(decoded['stab22'].value, '0x00000000000000000000000000000000000000000000') - st.equal(decoded['stab23'].value, '0x0000000000000000000000000000000000000000000000') - st.equal(decoded['stab24'].value, '0x000000000000000000000000000000000000000000000000') - st.equal(decoded['stab25'].value, '0x00000000000000000000000000000000000000000000000000') - st.equal(decoded['stab26'].value, '0x0000000000000000000000000000000000000000000000000000') - st.equal(decoded['stab27'].value, '0x000000000000000000000000000000000000000000000000000000') - st.equal(decoded['stab28'].value, '0x00000000000000000000000000000000000000000000000000000000') - st.equal(decoded['stab29'].value, '0x0000000000000000000000000000000000000000000000000000000000') - st.equal(decoded['stab30'].value, '0x000000000000000000000000000000000000000000000000000000000000') - st.equal(decoded['stab31'].value, '0x00000000000000000000000000000000000000000000000000000000000000') - st.equal(decoded['stab32'].value, '0x0000000000000000000000000000000000000000000000000000000000000000') - st.equal(decoded['enumDec'].value, 'e0') - st.equal(decoded['str1'].length, '0x0') - st.equal(decoded['str2'].length, '0x0') - st.equal(decoded['str1'].value, '') - st.equal(decoded['str12'].value, '') - st.equal(decoded['str2'].value, '') - cb() + st.equal(decoded['b1'].value, false) + st.equal(decoded['a1'].value, '0xFE350F199F244AC9A79038D254400B632A633225') + st.equal(decoded['b2'].value, true) + st.equal(decoded['dynb1'].value, '0x64796e616d69636279746573') + st.equal(decoded['dynb1'].length, '0xc') + st.equal(decoded['stab'].value, '0x01') + st.equal(decoded['stab1'].value, '0x12') + st.equal(decoded['stab2'].value, '0x1579') + st.equal(decoded['stab3'].value, '0x359356') + st.equal(decoded['stab4'].value, '0x23750000') + st.equal(decoded['stab5'].value, '0x0235764500') + st.equal(decoded['stab6'].value, '0x324435000000') + st.equal(decoded['stab7'].value, '0x00432400000000') + st.equal(decoded['stab8'].value, '0x3245546457650000') + st.equal(decoded['stab9'].value, '0x034345430000000000') + st.equal(decoded['stab10'].value, '0x04543543654657000000') + st.equal(decoded['stab11'].value, '0x5435465400000000000000') + st.equal(decoded['stab12'].value, '0x030000000000000000000000') + st.equal(decoded['stab13'].value, '0x03243242345435000000000000') + st.equal(decoded['stab14'].value, '0x3245435435435300000000000000') + st.equal(decoded['stab15'].value, '0x032454434435000000000000000000') + st.equal(decoded['stab16'].value, '0x32454354440000000000000000000000') + st.equal(decoded['stab17'].value, '0x0324543432432432450000000000000000') + st.equal(decoded['stab18'].value, '0x032453432543543500000000000000000000') + st.equal(decoded['stab19'].value, '0x03245434354354350000000000000000000000') + st.equal(decoded['stab20'].value, '0x032454543543AB35000000000000000000000000') + st.equal(decoded['stab21'].value, '0x324544324234350000000000000000000000000000') + st.equal(decoded['stab22'].value, '0x324543AEF50000000000000000000000000000000000') + st.equal(decoded['stab23'].value, '0x3245435FFF000000000000000000000000000000000000') + st.equal(decoded['stab24'].value, '0x3245435F0000000000000000000000000000000000000000') + st.equal(decoded['stab25'].value, '0x3245435F000000000000000000000000000000000000000000') + st.equal(decoded['stab26'].value, '0x3245435F00000000000000000000000000000000000000000000') + st.equal(decoded['stab27'].value, '0x03245FFFFFFF000000000000000000000000000000000000000000') + st.equal(decoded['stab28'].value, '0x03241235000000000000000000000000000000000000000000000000') + st.equal(decoded['stab29'].value, '0x0325213213000000000000000000000000000000000000000000000000') + st.equal(decoded['stab30'].value, '0x032454352324230000000000000000000000000000000000000000000000') + st.equal(decoded['stab31'].value, '0x32454351230000000000000000000000000000000000000000000000000000') + st.equal(decoded['stab32'].value, '0x324324423432543543AB00000000000000000000000000000000000000000000') + st.equal(decoded['enumDec'].value, 'e240') + st.equal(decoded['str1'].value, 'short') + st.equal(decoded['str12'].value, 'шеллы') + st.equal(decoded['str2'].value, 'long__long__long__long__long__long__long__long__long__long__long__long__long__long__long__long__long__long__long__long__long__long__long__long__long__long__long__long__long__long__long__long__long__long__long__long__long__long__long__long__long__long__long__long__long__long__long__long') }) + } + + mockStorageResolver = new MockStorageResolver({}) + stateDecoder.solidityState(mockStorageResolver, output.sources, 'byteStorage').then((decoded) => { + st.equal(decoded['b1'].value, false) + st.equal(decoded['a1'].value, '0x0000000000000000000000000000000000000000') + st.equal(decoded['b2'].value, false) + st.equal(decoded['dynb1'].value, '0x') + st.equal(decoded['dynb1'].length, '0x0') + st.equal(decoded['stab'].value, '0x00') + st.equal(decoded['stab1'].value, '0x00') + st.equal(decoded['stab2'].value, '0x0000') + st.equal(decoded['stab3'].value, '0x000000') + st.equal(decoded['stab4'].value, '0x00000000') + st.equal(decoded['stab5'].value, '0x0000000000') + st.equal(decoded['stab6'].value, '0x000000000000') + st.equal(decoded['stab7'].value, '0x00000000000000') + st.equal(decoded['stab8'].value, '0x0000000000000000') + st.equal(decoded['stab9'].value, '0x000000000000000000') + st.equal(decoded['stab10'].value, '0x00000000000000000000') + st.equal(decoded['stab11'].value, '0x0000000000000000000000') + st.equal(decoded['stab12'].value, '0x000000000000000000000000') + st.equal(decoded['stab13'].value, '0x00000000000000000000000000') + st.equal(decoded['stab14'].value, '0x0000000000000000000000000000') + st.equal(decoded['stab15'].value, '0x000000000000000000000000000000') + st.equal(decoded['stab16'].value, '0x00000000000000000000000000000000') + st.equal(decoded['stab17'].value, '0x0000000000000000000000000000000000') + st.equal(decoded['stab18'].value, '0x000000000000000000000000000000000000') + st.equal(decoded['stab19'].value, '0x00000000000000000000000000000000000000') + st.equal(decoded['stab20'].value, '0x0000000000000000000000000000000000000000') + st.equal(decoded['stab21'].value, '0x000000000000000000000000000000000000000000') + st.equal(decoded['stab22'].value, '0x00000000000000000000000000000000000000000000') + st.equal(decoded['stab23'].value, '0x0000000000000000000000000000000000000000000000') + st.equal(decoded['stab24'].value, '0x000000000000000000000000000000000000000000000000') + st.equal(decoded['stab25'].value, '0x00000000000000000000000000000000000000000000000000') + st.equal(decoded['stab26'].value, '0x0000000000000000000000000000000000000000000000000000') + st.equal(decoded['stab27'].value, '0x000000000000000000000000000000000000000000000000000000') + st.equal(decoded['stab28'].value, '0x00000000000000000000000000000000000000000000000000000000') + st.equal(decoded['stab29'].value, '0x0000000000000000000000000000000000000000000000000000000000') + st.equal(decoded['stab30'].value, '0x000000000000000000000000000000000000000000000000000000000000') + st.equal(decoded['stab31'].value, '0x00000000000000000000000000000000000000000000000000000000000000') + st.equal(decoded['stab32'].value, '0x0000000000000000000000000000000000000000000000000000000000000000') + st.equal(decoded['enumDec'].value, 'e0') + st.equal(decoded['str1'].length, '0x0') + st.equal(decoded['str2'].length, '0x0') + st.equal(decoded['str1'].value, '') + st.equal(decoded['str12'].value, '') + st.equal(decoded['str2'].value, '') + cb() + }) } function shrinkStorage (storage) { - const shrinkedStorage = {} - const regex = /0x(00)*(..)/ - for (const key in storage) { - const value = storage[key] - shrinkedStorage[key.replace(regex, '0x$2')] = value.replace(regex, '0x$2') - } - return shrinkedStorage + const shrinkedStorage = {} + const regex = /0x(00)*(..)/ + for (const key in storage) { + const value = storage[key] + shrinkedStorage[key.replace(regex, '0x$2')] = value.replace(regex, '0x$2') + } + return shrinkedStorage } function testStructArrayStorage (st, cb) { - const structArrayStorage = require('./contracts/structArrayStorage') - let output = compile(compilerInput(structArrayStorage.contract)) - output = JSON.parse(output) - const mockStorageResolver = new MockStorageResolver(structArrayStorage.storage) - stateDecoder.solidityState(mockStorageResolver, output.sources, 'structArrayStorage').then((decoded) => { - st.equal(decoded['intStructDec'].value['i8'].value, '32') - st.equal(decoded['intStructDec'].value['i16'].value, '-54') - st.equal(decoded['intStructDec'].value['ui32'].value, '128') - st.equal(decoded['intStructDec'].value['i256'].value, '-1243565465756') - st.equal(decoded['intStructDec'].value['ui16'].value, '34556') - st.equal(decoded['intStructDec'].value['i32'].value, '-345446546') - st.equal(decoded['i5'].length, '0x7') - st.equal(decoded['i5'].value[0].value, '-2134') - st.equal(decoded['i5'].value[1].value, '345') - st.equal(decoded['i5'].value[2].value, '-3246') - st.equal(decoded['i5'].value[3].value, '4357') - st.equal(decoded['i5'].value[4].value, '324') - st.equal(decoded['i5'].value[5].value, '-2344') - st.equal(decoded['i5'].value[6].value, '3254') - st.equal(decoded['idyn5'].length, '0x9') - st.equal(decoded['idyn5'].value[0].value, '-2134') - st.equal(decoded['idyn5'].value[1].value, '345') - st.equal(decoded['idyn5'].value[2].value, '-3246') - st.equal(decoded['idyn5'].value[3].value, '4357') - st.equal(decoded['idyn5'].value[4].value, '324') - st.equal(decoded['idyn5'].value[5].value, '-2344') - st.equal(decoded['idyn5'].value[6].value, '3254') - st.equal(decoded['idyn5'].value[7].value, '-254') - st.equal(decoded['idyn5'].value[8].value, '-2354') - st.equal(decoded['dyn1'].length, '0x4') - st.equal(decoded['dyn1'].value[0].length, '0x1') - st.equal(decoded['dyn1'].value[0].value[0].value, '3') - st.equal(decoded['dyn1'].value[1].length, '0x3') - st.equal(decoded['dyn1'].value[1].value[0].value, '12') - st.equal(decoded['dyn1'].value[1].value[1].value, '-12') - st.equal(decoded['dyn1'].value[1].value[2].value, '-1234') - st.equal(decoded['dyn1'].value[2].length, '0xa') - st.equal(decoded['dyn1'].value[2].value[0].value, '1') - st.equal(decoded['dyn1'].value[2].value[1].value, '12') - st.equal(decoded['dyn1'].value[2].value[2].value, '1235') - st.equal(decoded['dyn1'].value[2].value[3].value, '-12') - st.equal(decoded['dyn1'].value[2].value[4].value, '-123456') - st.equal(decoded['dyn1'].value[2].value[5].value, '-23435435') - st.equal(decoded['dyn1'].value[2].value[6].value, '543543') - st.equal(decoded['dyn1'].value[2].value[7].value, '2') - st.equal(decoded['dyn1'].value[2].value[8].value, '-1') - st.equal(decoded['dyn1'].value[2].value[9].value, '232432') - st.equal(decoded['dyn1'].value[3].length, '0x2') - st.equal(decoded['dyn1'].value[3].value[0].value, '232432') - st.equal(decoded['dyn1'].value[3].value[0].value, '232432') - st.equal(decoded['dyn2'].length, '0x2') - st.equal(decoded['dyn2'].value[0].length, '0x4') - st.equal(decoded['dyn2'].value[0].value[0].value[0].value, '23') - st.equal(decoded['dyn2'].value[0].value[0].value[1].value, '-23') - st.equal(decoded['dyn2'].value[0].value[0].value[2].value, '-28') - st.equal(decoded['dyn2'].value[0].value[1].value[0].value, '23') - st.equal(decoded['dyn2'].value[0].value[1].value[1].value, '-23') - st.equal(decoded['dyn2'].value[0].value[1].value[2].value, '-28') - st.equal(decoded['dyn2'].value[0].value[2].value[0].value, '23') - st.equal(decoded['dyn2'].value[0].value[2].value[1].value, '-23') - st.equal(decoded['dyn2'].value[0].value[2].value[2].value, '-28') - st.equal(decoded['dyn2'].value[0].value[3].value[0].value, '23') - st.equal(decoded['dyn2'].value[0].value[3].value[1].value, '-23') - st.equal(decoded['dyn2'].value[0].value[3].value[2].value, '-28') - st.equal(decoded['dyn2'].value[1].length, '0x4') - st.equal(decoded['dyn2'].value[1].value[0].value[0].value, '23') - st.equal(decoded['dyn2'].value[1].value[0].value[1].value, '-23') - st.equal(decoded['dyn2'].value[1].value[0].value[2].value, '-28') - st.equal(decoded['dyn2'].value[1].value[1].value[0].value, '23') - st.equal(decoded['dyn2'].value[1].value[1].value[1].value, '-23') - st.equal(decoded['dyn2'].value[1].value[1].value[2].value, '-28') - st.equal(decoded['dyn2'].value[1].value[2].value[0].value, '23') - st.equal(decoded['dyn2'].value[1].value[2].value[1].value, '-23') - st.equal(decoded['dyn2'].value[1].value[2].value[2].value, '-28') - st.equal(decoded['dyn2'].value[1].value[3].value[0].value, '23') - st.equal(decoded['dyn2'].value[1].value[3].value[1].value, '-23') - st.equal(decoded['dyn2'].value[1].value[3].value[2].value, '-28') - st.equal(decoded['arrayStruct'].value[0].value[0].value.i8.value, '34') - st.equal(decoded['arrayStruct'].value[0].value[0].value.str.value, 'test_str_short') - st.equal(decoded['arrayStruct'].value[0].value[1].value.i8.value, '-123') - st.equal(decoded['arrayStruct'].value[0].value[1].value.str.value, 'test_str_long test_str_lo ngtest_str_longtest_str_ longtest_str_longtest_ str_longtest_str_l ongtest_str_long') - st.equal(decoded['arrayStruct'].value[1].value[0].value.i8.value, '50') - st.equal(decoded['arrayStruct'].value[1].value[0].value.str.value, 'test_str_short') - st.equal(decoded['arrayStruct'].value[2].value[0].value.i8.value, '60') - st.equal(decoded['arrayStruct'].value[2].value[0].value.str.value, 'test_str_short') - st.equal(decoded['arrayStruct'].value[2].value[1].value.i8.value, '84') - st.equal(decoded['arrayStruct'].value[2].value[1].value.str.value, 'test_str_long test_str_lo ngtest_str_longtest_str_ longtest_str_longtest_ str_longtest_str_l ongtest_str_long') - st.equal(decoded['arrayStruct'].value[2].value[2].value.i8.value, '-34') - st.equal(decoded['arrayStruct'].value[2].value[2].value.str.value, 'test_str_short') - cb() - }) + const structArrayStorage = require('./contracts/structArrayStorage') + let output = compile(compilerInput(structArrayStorage.contract)) + output = JSON.parse(output) + const mockStorageResolver = new MockStorageResolver(structArrayStorage.storage) + stateDecoder.solidityState(mockStorageResolver, output.sources, 'structArrayStorage').then((decoded) => { + st.equal(decoded['intStructDec'].value['i8'].value, '32') + st.equal(decoded['intStructDec'].value['i16'].value, '-54') + st.equal(decoded['intStructDec'].value['ui32'].value, '128') + st.equal(decoded['intStructDec'].value['i256'].value, '-1243565465756') + st.equal(decoded['intStructDec'].value['ui16'].value, '34556') + st.equal(decoded['intStructDec'].value['i32'].value, '-345446546') + st.equal(decoded['i5'].length, '0x7') + st.equal(decoded['i5'].value[0].value, '-2134') + st.equal(decoded['i5'].value[1].value, '345') + st.equal(decoded['i5'].value[2].value, '-3246') + st.equal(decoded['i5'].value[3].value, '4357') + st.equal(decoded['i5'].value[4].value, '324') + st.equal(decoded['i5'].value[5].value, '-2344') + st.equal(decoded['i5'].value[6].value, '3254') + st.equal(decoded['idyn5'].length, '0x9') + st.equal(decoded['idyn5'].value[0].value, '-2134') + st.equal(decoded['idyn5'].value[1].value, '345') + st.equal(decoded['idyn5'].value[2].value, '-3246') + st.equal(decoded['idyn5'].value[3].value, '4357') + st.equal(decoded['idyn5'].value[4].value, '324') + st.equal(decoded['idyn5'].value[5].value, '-2344') + st.equal(decoded['idyn5'].value[6].value, '3254') + st.equal(decoded['idyn5'].value[7].value, '-254') + st.equal(decoded['idyn5'].value[8].value, '-2354') + st.equal(decoded['dyn1'].length, '0x4') + st.equal(decoded['dyn1'].value[0].length, '0x1') + st.equal(decoded['dyn1'].value[0].value[0].value, '3') + st.equal(decoded['dyn1'].value[1].length, '0x3') + st.equal(decoded['dyn1'].value[1].value[0].value, '12') + st.equal(decoded['dyn1'].value[1].value[1].value, '-12') + st.equal(decoded['dyn1'].value[1].value[2].value, '-1234') + st.equal(decoded['dyn1'].value[2].length, '0xa') + st.equal(decoded['dyn1'].value[2].value[0].value, '1') + st.equal(decoded['dyn1'].value[2].value[1].value, '12') + st.equal(decoded['dyn1'].value[2].value[2].value, '1235') + st.equal(decoded['dyn1'].value[2].value[3].value, '-12') + st.equal(decoded['dyn1'].value[2].value[4].value, '-123456') + st.equal(decoded['dyn1'].value[2].value[5].value, '-23435435') + st.equal(decoded['dyn1'].value[2].value[6].value, '543543') + st.equal(decoded['dyn1'].value[2].value[7].value, '2') + st.equal(decoded['dyn1'].value[2].value[8].value, '-1') + st.equal(decoded['dyn1'].value[2].value[9].value, '232432') + st.equal(decoded['dyn1'].value[3].length, '0x2') + st.equal(decoded['dyn1'].value[3].value[0].value, '232432') + st.equal(decoded['dyn1'].value[3].value[0].value, '232432') + st.equal(decoded['dyn2'].length, '0x2') + st.equal(decoded['dyn2'].value[0].length, '0x4') + st.equal(decoded['dyn2'].value[0].value[0].value[0].value, '23') + st.equal(decoded['dyn2'].value[0].value[0].value[1].value, '-23') + st.equal(decoded['dyn2'].value[0].value[0].value[2].value, '-28') + st.equal(decoded['dyn2'].value[0].value[1].value[0].value, '23') + st.equal(decoded['dyn2'].value[0].value[1].value[1].value, '-23') + st.equal(decoded['dyn2'].value[0].value[1].value[2].value, '-28') + st.equal(decoded['dyn2'].value[0].value[2].value[0].value, '23') + st.equal(decoded['dyn2'].value[0].value[2].value[1].value, '-23') + st.equal(decoded['dyn2'].value[0].value[2].value[2].value, '-28') + st.equal(decoded['dyn2'].value[0].value[3].value[0].value, '23') + st.equal(decoded['dyn2'].value[0].value[3].value[1].value, '-23') + st.equal(decoded['dyn2'].value[0].value[3].value[2].value, '-28') + st.equal(decoded['dyn2'].value[1].length, '0x4') + st.equal(decoded['dyn2'].value[1].value[0].value[0].value, '23') + st.equal(decoded['dyn2'].value[1].value[0].value[1].value, '-23') + st.equal(decoded['dyn2'].value[1].value[0].value[2].value, '-28') + st.equal(decoded['dyn2'].value[1].value[1].value[0].value, '23') + st.equal(decoded['dyn2'].value[1].value[1].value[1].value, '-23') + st.equal(decoded['dyn2'].value[1].value[1].value[2].value, '-28') + st.equal(decoded['dyn2'].value[1].value[2].value[0].value, '23') + st.equal(decoded['dyn2'].value[1].value[2].value[1].value, '-23') + st.equal(decoded['dyn2'].value[1].value[2].value[2].value, '-28') + st.equal(decoded['dyn2'].value[1].value[3].value[0].value, '23') + st.equal(decoded['dyn2'].value[1].value[3].value[1].value, '-23') + st.equal(decoded['dyn2'].value[1].value[3].value[2].value, '-28') + st.equal(decoded['arrayStruct'].value[0].value[0].value.i8.value, '34') + st.equal(decoded['arrayStruct'].value[0].value[0].value.str.value, 'test_str_short') + st.equal(decoded['arrayStruct'].value[0].value[1].value.i8.value, '-123') + st.equal(decoded['arrayStruct'].value[0].value[1].value.str.value, 'test_str_long test_str_lo ngtest_str_longtest_str_ longtest_str_longtest_ str_longtest_str_l ongtest_str_long') + st.equal(decoded['arrayStruct'].value[1].value[0].value.i8.value, '50') + st.equal(decoded['arrayStruct'].value[1].value[0].value.str.value, 'test_str_short') + st.equal(decoded['arrayStruct'].value[2].value[0].value.i8.value, '60') + st.equal(decoded['arrayStruct'].value[2].value[0].value.str.value, 'test_str_short') + st.equal(decoded['arrayStruct'].value[2].value[1].value.i8.value, '84') + st.equal(decoded['arrayStruct'].value[2].value[1].value.str.value, 'test_str_long test_str_lo ngtest_str_longtest_str_ longtest_str_longtest_ str_longtest_str_l ongtest_str_long') + st.equal(decoded['arrayStruct'].value[2].value[2].value.i8.value, '-34') + st.equal(decoded['arrayStruct'].value[2].value[2].value.str.value, 'test_str_short') + cb() + }) } diff --git a/libs/remix-debug/test/decoder/storageLocation.ts b/libs/remix-debug/test/decoder/storageLocation.ts index c65f368c92..57b5be1daa 100644 --- a/libs/remix-debug/test/decoder/storageLocation.ts +++ b/libs/remix-debug/test/decoder/storageLocation.ts @@ -6,53 +6,53 @@ const contracts = require('./contracts/miscContracts') const compilerInput = require('../helpers/compilerHelper').compilerInput tape('solidity', function (t) { - t.test('storage location', function (st) { - let output = compiler.compile(compilerInput(contracts)) - output = JSON.parse(output) - let stateDec = stateDecoder.extractStateVariables('contractUint', output.sources) - checkLocation(st, stateDec[0].storagelocation, 0, 0) - checkLocation(st, stateDec[1].storagelocation, 1, 0) - checkLocation(st, stateDec[2].storagelocation, 2, 0) - checkLocation(st, stateDec[3].storagelocation, 3, 0) + t.test('storage location', function (st) { + let output = compiler.compile(compilerInput(contracts)) + output = JSON.parse(output) + let stateDec = stateDecoder.extractStateVariables('contractUint', output.sources) + checkLocation(st, stateDec[0].storagelocation, 0, 0) + checkLocation(st, stateDec[1].storagelocation, 1, 0) + checkLocation(st, stateDec[2].storagelocation, 2, 0) + checkLocation(st, stateDec[3].storagelocation, 3, 0) - stateDec = stateDecoder.extractStateVariables('contractStructAndArray', output.sources) - checkLocation(st, stateDec[0].storagelocation, 0, 0) - checkLocation(st, stateDec[1].storagelocation, 2, 0) - checkLocation(st, stateDec[2].storagelocation, 8, 0) + stateDec = stateDecoder.extractStateVariables('contractStructAndArray', output.sources) + checkLocation(st, stateDec[0].storagelocation, 0, 0) + checkLocation(st, stateDec[1].storagelocation, 2, 0) + checkLocation(st, stateDec[2].storagelocation, 8, 0) - stateDec = stateDecoder.extractStateVariables('contractArray', output.sources) - checkLocation(st, stateDec[0].storagelocation, 0, 0) - checkLocation(st, stateDec[1].storagelocation, 1, 0) - checkLocation(st, stateDec[2].storagelocation, 2, 0) + stateDec = stateDecoder.extractStateVariables('contractArray', output.sources) + checkLocation(st, stateDec[0].storagelocation, 0, 0) + checkLocation(st, stateDec[1].storagelocation, 1, 0) + checkLocation(st, stateDec[2].storagelocation, 2, 0) - stateDec = stateDecoder.extractStateVariables('contractSmallVariable', output.sources) - checkLocation(st, stateDec[0].storagelocation, 0, 0) - checkLocation(st, stateDec[1].storagelocation, 0, 1) - checkLocation(st, stateDec[2].storagelocation, 0, 2) - checkLocation(st, stateDec[3].storagelocation, 0, 4) - checkLocation(st, stateDec[4].storagelocation, 1, 0) - checkLocation(st, stateDec[5].storagelocation, 2, 0) + stateDec = stateDecoder.extractStateVariables('contractSmallVariable', output.sources) + checkLocation(st, stateDec[0].storagelocation, 0, 0) + checkLocation(st, stateDec[1].storagelocation, 0, 1) + checkLocation(st, stateDec[2].storagelocation, 0, 2) + checkLocation(st, stateDec[3].storagelocation, 0, 4) + checkLocation(st, stateDec[4].storagelocation, 1, 0) + checkLocation(st, stateDec[5].storagelocation, 2, 0) - stateDec = stateDecoder.extractStateVariables('testSimpleStorage', output.sources) - checkLocation(st, stateDec[0].storagelocation, 0, 0) - checkLocation(st, stateDec[1].storagelocation, 1, 0) - checkLocation(st, stateDec[2].storagelocation, 2, 0) - checkLocation(st, stateDec[3].storagelocation, 3, 0) - checkLocation(st, stateDec[4].storagelocation, 4, 0) - checkLocation(st, stateDec[5].storagelocation, 8, 0) - checkLocation(st, stateDec[6].storagelocation, 9, 0) - checkLocation(st, stateDec[8].storagelocation, 17, 0) - checkLocation(st, stateDec[9].storagelocation, 17, 4) - checkLocation(st, stateDec[10].storagelocation, 17, 6) - checkLocation(st, stateDec[11].storagelocation, 17, 7) - checkLocation(st, stateDec[12].storagelocation, 18, 0) - checkLocation(st, stateDec[13].storagelocation, 21, 0) + stateDec = stateDecoder.extractStateVariables('testSimpleStorage', output.sources) + checkLocation(st, stateDec[0].storagelocation, 0, 0) + checkLocation(st, stateDec[1].storagelocation, 1, 0) + checkLocation(st, stateDec[2].storagelocation, 2, 0) + checkLocation(st, stateDec[3].storagelocation, 3, 0) + checkLocation(st, stateDec[4].storagelocation, 4, 0) + checkLocation(st, stateDec[5].storagelocation, 8, 0) + checkLocation(st, stateDec[6].storagelocation, 9, 0) + checkLocation(st, stateDec[8].storagelocation, 17, 0) + checkLocation(st, stateDec[9].storagelocation, 17, 4) + checkLocation(st, stateDec[10].storagelocation, 17, 6) + checkLocation(st, stateDec[11].storagelocation, 17, 7) + checkLocation(st, stateDec[12].storagelocation, 18, 0) + checkLocation(st, stateDec[13].storagelocation, 21, 0) - st.end() - }) + st.end() + }) }) function checkLocation (st, location, slot, offset) { - st.equal(location.offset, offset) - st.equal(location.slot, slot) + st.equal(location.offset, offset) + st.equal(location.slot, slot) } diff --git a/libs/remix-debug/test/disassembler.ts b/libs/remix-debug/test/disassembler.ts index 406994975d..f4081a9339 100644 --- a/libs/remix-debug/test/disassembler.ts +++ b/libs/remix-debug/test/disassembler.ts @@ -4,22 +4,22 @@ import tape from 'tape' const disassemble = require('../src/code/disassembler').disassemble tape('Disassembler', function (t) { - t.test('empty', function (st) { - st.plan(1) - st.equal(disassemble(''), '') - }) - t.test('add', function (st) { - st.plan(1) - st.equal(disassemble('0x01'), 'add') - }) - t.test('push', function (st) { - st.plan(1) - st.equal(disassemble('0x640203'), '0x0203000000') - }) - t.test('complexcode', function (st) { - st.plan(1) - const code = '60606040526009600060005055607e8060186000396000f360606040526000357c0100000000000000000000000000000000000000000000000000000000900480630dbe671f146039576035565b6002565b3460025760486004805050604a565b005b6000600090505b600a811015607a5760006000818150548092919060010191905055505b80806001019150506051565b5b5056' - const asm = `mstore(0x40, 0x60) + t.test('empty', function (st) { + st.plan(1) + st.equal(disassemble(''), '') + }) + t.test('add', function (st) { + st.plan(1) + st.equal(disassemble('0x01'), 'add') + }) + t.test('push', function (st) { + st.plan(1) + st.equal(disassemble('0x640203'), '0x0203000000') + }) + t.test('complexcode', function (st) { + st.plan(1) + const code = '60606040526009600060005055607e8060186000396000f360606040526000357c0100000000000000000000000000000000000000000000000000000000900480630dbe671f146039576035565b6002565b3460025760486004805050604a565b005b6000600090505b600a811015607a5760006000818150548092919060010191905055505b80806001019150506051565b5b5056' + const asm = `mstore(0x40, 0x60) 0x09 0x00 pop(0x00) @@ -96,6 +96,6 @@ label7: label8: pop jump` - st.equal(disassemble(code), asm) - }) + st.equal(disassemble(code), asm) + }) }) diff --git a/libs/remix-debug/test/helpers/compilerHelper.ts b/libs/remix-debug/test/helpers/compilerHelper.ts index 542d10a1b9..6f5fb449cd 100644 --- a/libs/remix-debug/test/helpers/compilerHelper.ts +++ b/libs/remix-debug/test/helpers/compilerHelper.ts @@ -1,22 +1,22 @@ export function compilerInput (contracts) { - return JSON.stringify({ - language: 'Solidity', - sources: { - 'test.sol': { - content: contracts - } - }, - settings: { - optimizer: { - enabled: false, - runs: 200 - }, - outputSelection: { - '*': { - '': [ 'ast' ], - '*': [ 'abi', 'metadata', 'evm.legacyAssembly', 'evm.bytecode', 'evm.deployedBytecode', 'evm.methodIdentifiers', 'evm.gasEstimates' ] - } - } + return JSON.stringify({ + language: 'Solidity', + sources: { + 'test.sol': { + content: contracts + } + }, + settings: { + optimizer: { + enabled: false, + runs: 200 + }, + outputSelection: { + '*': { + '': [ 'ast' ], + '*': [ 'abi', 'metadata', 'evm.legacyAssembly', 'evm.bytecode', 'evm.deployedBytecode', 'evm.methodIdentifiers', 'evm.gasEstimates' ] } - }) + } + } + }) } diff --git a/libs/remix-debug/test/init.ts b/libs/remix-debug/test/init.ts index 547d5e1d6e..3fa812e74d 100644 --- a/libs/remix-debug/test/init.ts +++ b/libs/remix-debug/test/init.ts @@ -1,29 +1,29 @@ export const init = { - overrideWeb3: function (web3, web3Override) { - web3.eth.getCode = web3Override.getCode - web3.debug.traceTransaction = web3Override.traceTransaction - web3.debug.storageRangeAt = web3Override.storageRangeAt - web3.eth.getTransaction = web3Override.getTransaction - web3.eth.getTransactionFromBlock = web3Override.getTransactionFromBlock - web3.eth.getBlockNumber = web3Override.getBlockNumber - }, + overrideWeb3: function (web3, web3Override) { + web3.eth.getCode = web3Override.getCode + web3.debug.traceTransaction = web3Override.traceTransaction + web3.debug.storageRangeAt = web3Override.storageRangeAt + web3.eth.getTransaction = web3Override.getTransaction + web3.eth.getTransactionFromBlock = web3Override.getTransactionFromBlock + web3.eth.getBlockNumber = web3Override.getBlockNumber + }, - readFile: function (filename, callback) { - const fs = require('fs') - try { - console.log('reading ' + filename) - if (callback) { - fs.readFile(filename, 'utf8', callback) - } else { - return fs.readFileSync(filename, 'utf8') - } - } catch (e) { - console.log(e) - if (callback) { - callback(e) - } else { - return e - } - } + readFile: function (filename, callback) { + const fs = require('fs') + try { + console.log('reading ' + filename) + if (callback) { + fs.readFile(filename, 'utf8', callback) + } else { + return fs.readFileSync(filename, 'utf8') + } + } catch (e) { + console.log(e) + if (callback) { + callback(e) + } else { + return e + } } + } } diff --git a/libs/remix-debug/test/resources/ast.ts b/libs/remix-debug/test/resources/ast.ts index f4ca65f47e..bf2c07ba7b 100644 --- a/libs/remix-debug/test/resources/ast.ts +++ b/libs/remix-debug/test/resources/ast.ts @@ -3,126 +3,126 @@ const node = {} node['ast'] = {"legacyAST":{"children":[{"attributes":{"fullyImplemented":true,"isLibrary":false,"linearizedBaseContracts":[5640396],"name":"test"},"children":[{"attributes":{"name":"x","type":"int256"},"children":[{"attributes":{"name":"int"},"id":5657860,"name":"ElementaryTypeName","src":"21:3:11"}],"id":5658100,"name":"VariableDeclaration","src":"21:5:11"},{"attributes":{"name":"y","type":"int256"},"children":[{"attributes":{"name":"int"},"id":5658180,"name":"ElementaryTypeName","src":"38:3:11"}],"id":5658268,"name":"VariableDeclaration","src":"38:5:11"},{"attributes":{"constant":false,"name":"set","public":true},"children":[{"children":[{"attributes":{"name":"_x","type":"int256"},"children":[{"attributes":{"name":"int"},"id":5658404,"name":"ElementaryTypeName","src":"68:3:11"}],"id":5658492,"name":"VariableDeclaration","src":"68:6:11"}],"id":5658572,"name":"ParameterList","src":"67:8:11"},{"children":[{"attributes":{"name":"_r","type":"int256"},"children":[{"attributes":{"name":"int"},"id":5658628,"name":"ElementaryTypeName","src":"85:3:11"}],"id":5658716,"name":"VariableDeclaration","src":"85:6:11"}],"id":5658796,"name":"ParameterList","src":"84:8:11"},{"children":[{"children":[{"attributes":{"operator":"=","type":"int256"},"children":[{"attributes":{"type":"int256","value":"x"},"id":5658900,"name":"Identifier","src":"108:1:11"},{"attributes":{"type":"int256","value":"_x"},"id":5658980,"name":"Identifier","src":"112:2:11"}],"id":5657492,"name":"Assignment","src":"108:6:11"}],"id":5659028,"name":"ExpressionStatement","src":"108:6:11"},{"children":[{"attributes":{"operator":"=","type":"int256"},"children":[{"attributes":{"type":"int256","value":"y"},"id":5659116,"name":"Identifier","src":"125:1:11"},{"attributes":{"string":null,"type":"int_const 10","value":"10"},"id":5659196,"name":"Literal","src":"129:2:11"}],"id":5659252,"name":"Assignment","src":"125:6:11"}],"id":5659316,"name":"ExpressionStatement","src":"125:6:11"},{"children":[{"attributes":{"operator":"=","type":"int256"},"children":[{"attributes":{"type":"int256","value":"_r"},"id":5659428,"name":"Identifier","src":"141:2:11"},{"attributes":{"type":"int256","value":"x"},"id":5639308,"name":"Identifier","src":"146:1:11"}],"id":5639356,"name":"Assignment","src":"141:6:11"}],"id":5639420,"name":"ExpressionStatement","src":"141:6:11"}],"id":5639516,"name":"Block","src":"97:57:11"}],"id":5639612,"name":"FunctionDefinition","src":"55:99:11"},{"attributes":{"constant":false,"name":"get","public":true},"children":[{"children":[],"id":5639764,"name":"ParameterList","src":"179:2:11"},{"children":[{"attributes":{"name":"x","type":"uint256"},"children":[{"attributes":{"name":"uint"},"id":5639820,"name":"ElementaryTypeName","src":"191:4:11"}],"id":5639908,"name":"VariableDeclaration","src":"191:6:11"},{"attributes":{"name":"y","type":"uint256"},"children":[{"attributes":{"name":"uint"},"id":5639988,"name":"ElementaryTypeName","src":"199:4:11"}],"id":5640076,"name":"VariableDeclaration","src":"199:6:11"}],"id":5640156,"name":"ParameterList","src":"190:16:11"},{"children":[],"id":5640212,"name":"Block","src":"212:17:11"}],"id":5640276,"name":"FunctionDefinition","src":"167:62:11"}],"id":5640396,"name":"ContractDefinition","src":"0:231:11"}],"name":"SourceUnit"}} node['ast'].ast = { - absolutePath: 'sample.sol', - exportedSymbols: { test: [ 33 ] }, - id: 34, - nodeType: 'SourceUnit', - nodes: + absolutePath: 'sample.sol', + exportedSymbols: { test: [ 33 ] }, + id: 34, + nodeType: 'SourceUnit', + nodes: [ { - abstract: false, - baseContracts: [], - contractDependencies: [], - contractKind: 'contract', - documentation: null, - fullyImplemented: true, - id: 33, - linearizedBaseContracts: [ 33 ], - name: 'test', - nodeType: 'ContractDefinition', - nodes: + abstract: false, + baseContracts: [], + contractDependencies: [], + contractKind: 'contract', + documentation: null, + fullyImplemented: true, + id: 33, + linearizedBaseContracts: [ 33 ], + name: 'test', + nodeType: 'ContractDefinition', + nodes: [ { constant: false, - id: 2, - name: 'x', - nodeType: 'VariableDeclaration', - overrides: null, - scope: 33, - src: '20:5:0', - stateVariable: true, - storageLocation: 'default', - typeDescriptions: { typeIdentifier: 't_int256', typeString: 'int256' }, - typeName: { - id: 1, - name: 'int', - nodeType: 'ElementaryTypeName', - src: '20:3:0', - typeDescriptions: [Object] - }, - value: null, - visibility: 'internal' }, + id: 2, + name: 'x', + nodeType: 'VariableDeclaration', + overrides: null, + scope: 33, + src: '20:5:0', + stateVariable: true, + storageLocation: 'default', + typeDescriptions: { typeIdentifier: 't_int256', typeString: 'int256' }, + typeName: { + id: 1, + name: 'int', + nodeType: 'ElementaryTypeName', + src: '20:3:0', + typeDescriptions: [Object] + }, + value: null, + visibility: 'internal' }, { constant: false, - id: 4, - name: 'y', - nodeType: 'VariableDeclaration', - overrides: null, - scope: 33, - src: '31:5:0', - stateVariable: true, - storageLocation: 'default', - typeDescriptions: { typeIdentifier: 't_int256', typeString: 'int256' }, - typeName: + id: 4, + name: 'y', + nodeType: 'VariableDeclaration', + overrides: null, + scope: 33, + src: '31:5:0', + stateVariable: true, + storageLocation: 'default', + typeDescriptions: { typeIdentifier: 't_int256', typeString: 'int256' }, + typeName: { id: 3, - name: 'int', - nodeType: 'ElementaryTypeName', - src: '31:3:0', - typeDescriptions: [Object] }, - value: null, - visibility: 'internal' }, + name: 'int', + nodeType: 'ElementaryTypeName', + src: '31:3:0', + typeDescriptions: [Object] }, + value: null, + visibility: 'internal' }, { body: { id: 23, - nodeType: 'Block', - src: '96:55:0', - statements: + nodeType: 'Block', + src: '96:55:0', + statements: [ { expression: { argumentTypes: null, - id: 13, - isConstant: false, - isLValue: false, - isPure: false, - lValueRequested: false, - leftHandSide: + id: 13, + isConstant: false, + isLValue: false, + isPure: false, + lValueRequested: false, + leftHandSide: { argumentTypes: null, - id: 11, - name: 'x', - nodeType: 'Identifier', - overloadedDeclarations: [], - referencedDeclaration: 2, - src: '106:1:0', - typeDescriptions: { typeIdentifier: 't_int256', typeString: 'int256' } }, - nodeType: 'Assignment', - operator: '=', - rightHandSide: + id: 11, + name: 'x', + nodeType: 'Identifier', + overloadedDeclarations: [], + referencedDeclaration: 2, + src: '106:1:0', + typeDescriptions: { typeIdentifier: 't_int256', typeString: 'int256' } }, + nodeType: 'Assignment', + operator: '=', + rightHandSide: { argumentTypes: null, - id: 12, - name: '_x', - nodeType: 'Identifier', - overloadedDeclarations: [], - referencedDeclaration: 6, - src: '110:2:0', - typeDescriptions: { typeIdentifier: 't_int256', typeString: 'int256' } }, - src: '106:6:0', - typeDescriptions: { typeIdentifier: 't_int256', typeString: 'int256' } + id: 12, + name: '_x', + nodeType: 'Identifier', + overloadedDeclarations: [], + referencedDeclaration: 6, + src: '110:2:0', + typeDescriptions: { typeIdentifier: 't_int256', typeString: 'int256' } }, + src: '106:6:0', + typeDescriptions: { typeIdentifier: 't_int256', typeString: 'int256' } }, id: 14, nodeType: 'ExpressionStatement', src: '106:6:0' }, { expression: { argumentTypes: null, - id: 17, - isConstant: false, - isLValue: false, - isPure: false, - lValueRequested: false, - leftHandSide: [Object], - nodeType: 'Assignment', - operator: '=', - rightHandSide: [Object], - src: '122:6:0', - typeDescriptions: [Object] }, + id: 17, + isConstant: false, + isLValue: false, + isPure: false, + lValueRequested: false, + leftHandSide: [Object], + nodeType: 'Assignment', + operator: '=', + rightHandSide: [Object], + src: '122:6:0', + typeDescriptions: [Object] }, id: 18, nodeType: 'ExpressionStatement', src: '122:6:0' }, { expression: { argumentTypes: null, - id: 21, - isConstant: false, - isLValue: false, - isPure: false, - lValueRequested: false, - leftHandSide: [Object], - nodeType: 'Assignment', - operator: '=', - rightHandSide: [Object], - src: '138:6:0', - typeDescriptions: [Object] }, + id: 21, + isConstant: false, + isLValue: false, + isPure: false, + lValueRequested: false, + leftHandSide: [Object], + nodeType: 'Assignment', + operator: '=', + rightHandSide: [Object], + src: '138:6:0', + typeDescriptions: [Object] }, id: 22, nodeType: 'ExpressionStatement', src: '138:6:0' } ] @@ -137,47 +137,47 @@ node['ast'].ast = { nodeType: 'FunctionDefinition', overrides: null, parameters: { id: 7, - nodeType: 'ParameterList', - parameters: [Array], - src: '59:8:0' }, + nodeType: 'ParameterList', + parameters: [Array], + src: '59:8:0' }, returnParameters: { id: 10, - nodeType: 'ParameterList', - parameters: [Array], - src: '83:8:0' }, + nodeType: 'ParameterList', + parameters: [Array], + src: '83:8:0' }, scope: 33, src: '47:104:0', stateMutability: 'nonpayable', virtual: false, visibility: 'public' }, { body: { id: 31, nodeType: 'Block', src: '214:17:0', statements: [] }, - documentation: null, - functionSelector: '6d4ce63c', - id: 32, - implemented: true, - kind: 'function', - modifiers: [], - name: 'get', - nodeType: 'FunctionDefinition', - overrides: null, - parameters: + documentation: null, + functionSelector: '6d4ce63c', + id: 32, + implemented: true, + kind: 'function', + modifiers: [], + name: 'get', + nodeType: 'FunctionDefinition', + overrides: null, + parameters: { id: 25, - nodeType: 'ParameterList', - parameters: [], - src: '175:2:0' }, - returnParameters: + nodeType: 'ParameterList', + parameters: [], + src: '175:2:0' }, + returnParameters: { id: 30, - nodeType: 'ParameterList', - parameters: [Array], - src: '193:16:0' }, - scope: 33, - src: '163:68:0', - stateMutability: 'nonpayable', - virtual: false, - visibility: 'public' } ], - scope: 34, - src: '0:233:0' + nodeType: 'ParameterList', + parameters: [Array], + src: '193:16:0' }, + scope: 33, + src: '163:68:0', + stateMutability: 'nonpayable', + virtual: false, + visibility: 'public' } ], + scope: 34, + src: '0:233:0' } ], - src: '0:233:0' + src: '0:233:0' } diff --git a/libs/remix-debug/test/resources/sourceMapping.ts b/libs/remix-debug/test/resources/sourceMapping.ts index 80f1a698ee..7f1fefa9c9 100644 --- a/libs/remix-debug/test/resources/sourceMapping.ts +++ b/libs/remix-debug/test/resources/sourceMapping.ts @@ -100,5 +100,5 @@ contract Ballot { } ` if (typeof (module) !== 'undefined' && typeof (module.exports) !== 'undefined') { - module.exports = sourceRuntimeMapping + module.exports = sourceRuntimeMapping } diff --git a/libs/remix-debug/test/resources/testWeb3.ts b/libs/remix-debug/test/resources/testWeb3.ts index e91f584fd4..f6c6461e9c 100644 --- a/libs/remix-debug/test/resources/testWeb3.ts +++ b/libs/remix-debug/test/resources/testWeb3.ts @@ -11,35 +11,35 @@ traceWithABIEncoder = data.testTraces['0x20ef65b8b186ca942fcccd634f37074dde49b541c27994fc7596740ef44cfd53'] = JSON.parse(traceWithABIEncoder) web3Override.eth.getCode = function (address, callback) { - if (callback) { - callback(null, data.testCodes[address]) - } else { - return data.testCodes[address] - } + if (callback) { + callback(null, data.testCodes[address]) + } else { + return data.testCodes[address] + } } web3Override.debug.traceTransaction = function (txHash, options, callback) { - callback(null, data.testTraces[txHash]) + callback(null, data.testTraces[txHash]) } web3Override.debug.storageRangeAt = function (blockNumber, txIndex, address, start, maxSize, callback) { - callback(null, { storage: {}, complete: true }) + callback(null, { storage: {}, complete: true }) } web3Override.eth.getTransaction = function (txHash, callback) { - if (callback) { - callback(null, data.testTxs[txHash]) - } else { - return data.testTxs[txHash] - } + if (callback) { + callback(null, data.testTxs[txHash]) + } else { + return data.testTxs[txHash] + } } web3Override.eth.getTransactionFromBlock = function (blockNumber, txIndex, callback) { - if (callback) { - callback(null, data.testTxsByBlock[blockNumber + '-' + txIndex]) - } else { - return data.testTxsByBlock[blockNumber + '-' + txIndex] - } + if (callback) { + callback(null, data.testTxsByBlock[blockNumber + '-' + txIndex]) + } else { + return data.testTxsByBlock[blockNumber + '-' + txIndex] + } } web3Override.eth.getBlockNumber = function (callback) { callback('web3 modified testing purposes :)') } @@ -51,5 +51,5 @@ web3Override.eth.providers = { 'HttpProvider': function (url) {} } web3Override.eth.currentProvider = {'host': 'test provider'} if (typeof (module) !== 'undefined' && typeof (module.exports) !== 'undefined') { - module.exports = web3Override + module.exports = web3Override } diff --git a/libs/remix-debug/test/sourceLocationTracker.ts b/libs/remix-debug/test/sourceLocationTracker.ts index 2b5ad856f9..3007b3359b 100644 --- a/libs/remix-debug/test/sourceLocationTracker.ts +++ b/libs/remix-debug/test/sourceLocationTracker.ts @@ -9,108 +9,108 @@ const compiler = require('solc') import { compilerInput } from './helpers/compilerHelper' tape('SourceLocationTracker', function (t) { - t.test('SourceLocationTracker.getSourceLocationFromVMTraceIndex - simple contract', async function (st) { + t.test('SourceLocationTracker.getSourceLocationFromVMTraceIndex - simple contract', async function (st) { - const traceManager = new TraceManager({web3: web3Test}) - const codeManager = new CodeManager(traceManager) + const traceManager = new TraceManager({web3: web3Test}) + const codeManager = new CodeManager(traceManager) - let output = compiler.compile(compilerInput(contracts)) - output = JSON.parse(output) + let output = compiler.compile(compilerInput(contracts)) + output = JSON.parse(output) - codeManager.codeResolver.cacheExecutingCode('0x0d3a18d64dfe4f927832ab58d6451cecc4e517c5', '0x' + output.contracts['test.sol']['test'].evm.deployedBytecode.object) + codeManager.codeResolver.cacheExecutingCode('0x0d3a18d64dfe4f927832ab58d6451cecc4e517c5', '0x' + output.contracts['test.sol']['test'].evm.deployedBytecode.object) - const tx = web3Test.eth.getTransaction('0x20ef65b8b186ca942fcccd634f37074dde49b541c27994fc7596740ef44cfd52') + const tx = web3Test.eth.getTransaction('0x20ef65b8b186ca942fcccd634f37074dde49b541c27994fc7596740ef44cfd52') - traceManager.resolveTrace(tx).then(async () => { + traceManager.resolveTrace(tx).then(async () => { - const sourceLocationTracker = new SourceLocationTracker(codeManager, {debugWithGeneratedSources: false}) + const sourceLocationTracker = new SourceLocationTracker(codeManager, {debugWithGeneratedSources: false}) - try { - const map = await sourceLocationTracker.getSourceLocationFromVMTraceIndex('0x0d3a18d64dfe4f927832ab58d6451cecc4e517c5', 0, output.contracts) - st.equal(map['file'], 0) - st.equal(map['start'], 0) - } catch (e) { - console.log(e) - } - st.end() - - }).catch((e) => { - t.fail(' - traceManager.resolveTrace - failed ') - console.error(e) - }) + try { + const map = await sourceLocationTracker.getSourceLocationFromVMTraceIndex('0x0d3a18d64dfe4f927832ab58d6451cecc4e517c5', 0, output.contracts) + st.equal(map['file'], 0) + st.equal(map['start'], 0) + } catch (e) { + console.log(e) + } + st.end() + + }).catch((e) => { + t.fail(' - traceManager.resolveTrace - failed ') + console.error(e) }) + }) - t.test('SourceLocationTracker.getSourceLocationFromVMTraceIndex - ABIEncoder V2 contract', async function (st) { + t.test('SourceLocationTracker.getSourceLocationFromVMTraceIndex - ABIEncoder V2 contract', async function (st) { - const traceManager = new TraceManager({web3: web3Test}) - const codeManager = new CodeManager(traceManager) + const traceManager = new TraceManager({web3: web3Test}) + const codeManager = new CodeManager(traceManager) - let output = compiler.compile(compilerInput(ABIEncoderV2)) - output = JSON.parse(output) + let output = compiler.compile(compilerInput(ABIEncoderV2)) + output = JSON.parse(output) - codeManager.codeResolver.cacheExecutingCode('0x0d3a18d64dfe4f927832ab58d6451cecc4e517c5', '0x' + output.contracts['test.sol']['test'].evm.deployedBytecode.object) + codeManager.codeResolver.cacheExecutingCode('0x0d3a18d64dfe4f927832ab58d6451cecc4e517c5', '0x' + output.contracts['test.sol']['test'].evm.deployedBytecode.object) - const tx = web3Test.eth.getTransaction('0x20ef65b8b186ca942fcccd634f37074dde49b541c27994fc7596740ef44cfd53') + const tx = web3Test.eth.getTransaction('0x20ef65b8b186ca942fcccd634f37074dde49b541c27994fc7596740ef44cfd53') - traceManager.resolveTrace(tx).then(async () => { + traceManager.resolveTrace(tx).then(async () => { - try { - // with debugWithGeneratedSources: false - const sourceLocationTracker = new SourceLocationTracker(codeManager, { debugWithGeneratedSources: false }) + try { + // with debugWithGeneratedSources: false + const sourceLocationTracker = new SourceLocationTracker(codeManager, { debugWithGeneratedSources: false }) - let map = await sourceLocationTracker.getSourceLocationFromVMTraceIndex('0x0d3a18d64dfe4f927832ab58d6451cecc4e517c5', 0, output.contracts) - console.log(map) - st.equal(map['file'], 0) - st.equal(map['start'], 35) + let map = await sourceLocationTracker.getSourceLocationFromVMTraceIndex('0x0d3a18d64dfe4f927832ab58d6451cecc4e517c5', 0, output.contracts) + console.log(map) + st.equal(map['file'], 0) + st.equal(map['start'], 35) - map = await sourceLocationTracker.getSourceLocationFromVMTraceIndex('0x0d3a18d64dfe4f927832ab58d6451cecc4e517c5', 45, output.contracts) - st.equal(map['file'], 1) // 1 refers to the generated source (pragma experimental ABIEncoderV2) + map = await sourceLocationTracker.getSourceLocationFromVMTraceIndex('0x0d3a18d64dfe4f927832ab58d6451cecc4e517c5', 45, output.contracts) + st.equal(map['file'], 1) // 1 refers to the generated source (pragma experimental ABIEncoderV2) - map = await sourceLocationTracker.getValidSourceLocationFromVMTraceIndex('0x0d3a18d64dfe4f927832ab58d6451cecc4e517c5', 45, output.contracts) - st.equal(map['file'], 0) // 1 refers to the generated source (pragma experimental ABIEncoderV2) - st.equal(map['start'], 303) - st.equal(map['length'], 448) - - map = await sourceLocationTracker.getValidSourceLocationFromVMTraceIndex('0x0d3a18d64dfe4f927832ab58d6451cecc4e517c5', 36, output.contracts) - st.equal(map['file'], 0) // 0 refers to the initial solidity code. see source below (ABIEncoderV2) - st.equal(map['start'], 303) - st.equal(map['length'], 448) - } catch (e) { - console.log(e) - } - - try { - // with debugWithGeneratedSources: true - const sourceLocationTracker = new SourceLocationTracker(codeManager, { debugWithGeneratedSources: true }) + map = await sourceLocationTracker.getValidSourceLocationFromVMTraceIndex('0x0d3a18d64dfe4f927832ab58d6451cecc4e517c5', 45, output.contracts) + st.equal(map['file'], 0) // 1 refers to the generated source (pragma experimental ABIEncoderV2) + st.equal(map['start'], 303) + st.equal(map['length'], 448) + + map = await sourceLocationTracker.getValidSourceLocationFromVMTraceIndex('0x0d3a18d64dfe4f927832ab58d6451cecc4e517c5', 36, output.contracts) + st.equal(map['file'], 0) // 0 refers to the initial solidity code. see source below (ABIEncoderV2) + st.equal(map['start'], 303) + st.equal(map['length'], 448) + } catch (e) { + console.log(e) + } + + try { + // with debugWithGeneratedSources: true + const sourceLocationTracker = new SourceLocationTracker(codeManager, { debugWithGeneratedSources: true }) - let map = await sourceLocationTracker.getSourceLocationFromVMTraceIndex('0x0d3a18d64dfe4f927832ab58d6451cecc4e517c5', 0, output.contracts) - console.log(map) - st.equal(map['file'], 0) - st.equal(map['start'], 35) - - map = await sourceLocationTracker.getSourceLocationFromVMTraceIndex('0x0d3a18d64dfe4f927832ab58d6451cecc4e517c5', 45, output.contracts) - st.equal(map['file'], 1) // 1 refers to the generated source (pragma experimental ABIEncoderV2) - - map = await sourceLocationTracker.getValidSourceLocationFromVMTraceIndex('0x0d3a18d64dfe4f927832ab58d6451cecc4e517c5', 45, output.contracts) - st.equal(map['file'], 1) // 1 refers to the generated source (pragma experimental ABIEncoderV2) - st.equal(map['start'], 1297) - st.equal(map['length'], 32) - - map = await sourceLocationTracker.getValidSourceLocationFromVMTraceIndex('0x0d3a18d64dfe4f927832ab58d6451cecc4e517c5', 36, output.contracts) - st.equal(map['file'], 0) // 0 refers to the initial solidity code. see source below (ABIEncoderV2) - st.equal(map['start'], 303) - st.equal(map['length'], 448) - } catch (e) { - console.log(e) - } - st.end() - - }).catch(() => { - t.fail(' - traceManager.resolveTrace - failed ') - }) + let map = await sourceLocationTracker.getSourceLocationFromVMTraceIndex('0x0d3a18d64dfe4f927832ab58d6451cecc4e517c5', 0, output.contracts) + console.log(map) + st.equal(map['file'], 0) + st.equal(map['start'], 35) + + map = await sourceLocationTracker.getSourceLocationFromVMTraceIndex('0x0d3a18d64dfe4f927832ab58d6451cecc4e517c5', 45, output.contracts) + st.equal(map['file'], 1) // 1 refers to the generated source (pragma experimental ABIEncoderV2) + + map = await sourceLocationTracker.getValidSourceLocationFromVMTraceIndex('0x0d3a18d64dfe4f927832ab58d6451cecc4e517c5', 45, output.contracts) + st.equal(map['file'], 1) // 1 refers to the generated source (pragma experimental ABIEncoderV2) + st.equal(map['start'], 1297) + st.equal(map['length'], 32) + + map = await sourceLocationTracker.getValidSourceLocationFromVMTraceIndex('0x0d3a18d64dfe4f927832ab58d6451cecc4e517c5', 36, output.contracts) + st.equal(map['file'], 0) // 0 refers to the initial solidity code. see source below (ABIEncoderV2) + st.equal(map['start'], 303) + st.equal(map['length'], 448) + } catch (e) { + console.log(e) + } + st.end() + + }).catch(() => { + t.fail(' - traceManager.resolveTrace - failed ') }) + }) }) const contracts = `contract test { diff --git a/libs/remix-debug/test/sourceMappingDecoder.ts b/libs/remix-debug/test/sourceMappingDecoder.ts index e9a41f128b..bc5c9ac950 100644 --- a/libs/remix-debug/test/sourceMappingDecoder.ts +++ b/libs/remix-debug/test/sourceMappingDecoder.ts @@ -6,119 +6,119 @@ const compiler = require('solc') const compilerInput = require('./helpers/compilerHelper').compilerInput tape('sourceMappingDecoder', function (t) { - t.test('sourceMappingDecoder.findNodeAtInstructionIndex', function (st) { - let output = compiler.compile(compilerInput(contracts)) - output = JSON.parse(output) - let node = sourceMappingDecoder.findNodeAtInstructionIndex('FunctionDefinition', 2, output.contracts['test.sol']['test'].evm.deployedBytecode.sourceMap, output.sources['test.sol']) - st.equal(node, null) - node = sourceMappingDecoder.findNodeAtInstructionIndex('FunctionDefinition', 80, output.contracts['test.sol']['test'].evm.deployedBytecode.sourceMap, output.sources['test.sol']) - st.notEqual(node, null) - if (node) { - st.equal(node.name, 'f1') - } - st.end() - }) + t.test('sourceMappingDecoder.findNodeAtInstructionIndex', function (st) { + let output = compiler.compile(compilerInput(contracts)) + output = JSON.parse(output) + let node = sourceMappingDecoder.findNodeAtInstructionIndex('FunctionDefinition', 2, output.contracts['test.sol']['test'].evm.deployedBytecode.sourceMap, output.sources['test.sol']) + st.equal(node, null) + node = sourceMappingDecoder.findNodeAtInstructionIndex('FunctionDefinition', 80, output.contracts['test.sol']['test'].evm.deployedBytecode.sourceMap, output.sources['test.sol']) + st.notEqual(node, null) + if (node) { + st.equal(node.name, 'f1') + } + st.end() + }) - const testSourceMapping = {} - t.test('sourceMappingDecoder', function (st) { - st.plan(36) - console.log('test decompressAll') - const result = sourceMappingDecoder.decompressAll(sourceMapping.mapping) - st.equal(result[0].start, 0) - st.equal(result[0].length, 205) - st.equal(result[0].file, 4) - st.equal(result[0].jump, '-') + const testSourceMapping = {} + t.test('sourceMappingDecoder', function (st) { + st.plan(36) + console.log('test decompressAll') + const result = sourceMappingDecoder.decompressAll(sourceMapping.mapping) + st.equal(result[0].start, 0) + st.equal(result[0].length, 205) + st.equal(result[0].file, 4) + st.equal(result[0].jump, '-') - st.equal(result[21].start, 0) - st.equal(result[21].length, 205) - st.equal(result[21].file, 4) - st.equal(result[21].jump, '-') - testSourceMapping[21] = result[21] + st.equal(result[21].start, 0) + st.equal(result[21].length, 205) + st.equal(result[21].file, 4) + st.equal(result[21].jump, '-') + testSourceMapping[21] = result[21] - st.equal(result[22].start, 55) - st.equal(result[22].length, 74) - st.equal(result[22].file, 4) - st.equal(result[22].jump, '-') + st.equal(result[22].start, 55) + st.equal(result[22].length, 74) + st.equal(result[22].file, 4) + st.equal(result[22].jump, '-') - const last = result.length - 1 - st.equal(result[last].start, 142) - st.equal(result[last].length, 61) - st.equal(result[last].file, 4) - st.equal(result[last].jump, 'o') - testSourceMapping['last'] = result[last] + const last = result.length - 1 + st.equal(result[last].start, 142) + st.equal(result[last].length, 61) + st.equal(result[last].file, 4) + st.equal(result[last].jump, 'o') + testSourceMapping['last'] = result[last] - console.log('test decompress') - const result2 = sourceMappingDecoder.atIndex(22, sourceMapping.mapping) - // console.log(result2) - st.equal(result2['start'], 55) - st.equal(result2['length'], 74) - st.equal(result2['file'], 4) - st.equal(result2['jump'], '-') - testSourceMapping[22] = result2 + console.log('test decompress') + const result2 = sourceMappingDecoder.atIndex(22, sourceMapping.mapping) + // console.log(result2) + st.equal(result2['start'], 55) + st.equal(result2['length'], 74) + st.equal(result2['file'], 4) + st.equal(result2['jump'], '-') + testSourceMapping[22] = result2 - const result3 = sourceMappingDecoder.atIndex(82, sourceMapping.mapping) - // console.log(result) - st.equal(result3['start'], 103) - st.equal(result3['length'], 2) - st.equal(result3['file'], 4) - st.equal(result3['jump'], '-') - testSourceMapping[82] = result3 + const result3 = sourceMappingDecoder.atIndex(82, sourceMapping.mapping) + // console.log(result) + st.equal(result3['start'], 103) + st.equal(result3['length'], 2) + st.equal(result3['file'], 4) + st.equal(result3['jump'], '-') + testSourceMapping[82] = result3 - const result4 = sourceMappingDecoder.atIndex(85, sourceMapping.mapping) - // console.log(result) - st.equal(result4['start'], 99) - st.equal(result4['length'], 6) - st.equal(result4['file'], 4) - st.equal(result4['jump'], '-') - testSourceMapping[85] = result4 + const result4 = sourceMappingDecoder.atIndex(85, sourceMapping.mapping) + // console.log(result) + st.equal(result4['start'], 99) + st.equal(result4['length'], 6) + st.equal(result4['file'], 4) + st.equal(result4['jump'], '-') + testSourceMapping[85] = result4 - // ballot - function deletegate(address) - const delegateSrcMap = sourceMappingDecoder.atIndex(64, sourceMapping.ballotSourceMap) - console.log(delegateSrcMap) - st.equal(delegateSrcMap['start'], 712) - st.equal(delegateSrcMap['length'], 577) - st.equal(delegateSrcMap['file'], 0) - st.equal(delegateSrcMap['jump'], '-') + // ballot - function deletegate(address) + const delegateSrcMap = sourceMappingDecoder.atIndex(64, sourceMapping.ballotSourceMap) + console.log(delegateSrcMap) + st.equal(delegateSrcMap['start'], 712) + st.equal(delegateSrcMap['length'], 577) + st.equal(delegateSrcMap['file'], 0) + st.equal(delegateSrcMap['jump'], '-') - // TokenSaleChallenge - function test(uint256) - const tokenSaleChallengeMap = sourceMappingDecoder.atIndex(170, sourceMapping.tokenSaleChallengeSourceMap) - console.log(tokenSaleChallengeMap) - st.equal(tokenSaleChallengeMap['start'], 45) - st.equal(tokenSaleChallengeMap['length'], 16) - st.equal(tokenSaleChallengeMap['file'], -1) - st.equal(tokenSaleChallengeMap['jump'], '-') - }) + // TokenSaleChallenge - function test(uint256) + const tokenSaleChallengeMap = sourceMappingDecoder.atIndex(170, sourceMapping.tokenSaleChallengeSourceMap) + console.log(tokenSaleChallengeMap) + st.equal(tokenSaleChallengeMap['start'], 45) + st.equal(tokenSaleChallengeMap['length'], 16) + st.equal(tokenSaleChallengeMap['file'], -1) + st.equal(tokenSaleChallengeMap['jump'], '-') + }) - t.test('sourceMappingLineColumnConverter', function (st) { - st.plan(14) - const linesbreak = sourceMappingDecoder.getLinebreakPositions(sourceMapping.source) - st.equal(linesbreak[0], 16) - st.equal(linesbreak[5], 84) - let result = sourceMappingDecoder.convertOffsetToLineColumn(testSourceMapping[21], linesbreak) - st.equal(result.start.line, 0) - st.equal(result.start.column, 0) - st.equal(result.end.line, 15) - st.equal(result.end.column, 1) - result = sourceMappingDecoder.convertOffsetToLineColumn(testSourceMapping[82], linesbreak) - st.equal(result.start.line, 7) - st.equal(result.start.column, 12) - st.equal(result.end.line, 7) - st.equal(result.end.column, 14) + t.test('sourceMappingLineColumnConverter', function (st) { + st.plan(14) + const linesbreak = sourceMappingDecoder.getLinebreakPositions(sourceMapping.source) + st.equal(linesbreak[0], 16) + st.equal(linesbreak[5], 84) + let result = sourceMappingDecoder.convertOffsetToLineColumn(testSourceMapping[21], linesbreak) + st.equal(result.start.line, 0) + st.equal(result.start.column, 0) + st.equal(result.end.line, 15) + st.equal(result.end.column, 1) + result = sourceMappingDecoder.convertOffsetToLineColumn(testSourceMapping[82], linesbreak) + st.equal(result.start.line, 7) + st.equal(result.start.column, 12) + st.equal(result.end.line, 7) + st.equal(result.end.column, 14) - const res = { // point to \n - start: 103, - length: 4, - file: 4, - jump: '-' - } - // case: 'file' is not yet assigned, while processing the srcmap (reverse looping) to find 'start', 'length' (etc..), we tumble on -1 for the file. - // in that case the step has to be discarded - result = sourceMappingDecoder.convertOffsetToLineColumn(res, linesbreak) - st.equal(result.start.line, 7) - st.equal(result.start.column, 12) - st.equal(result.end.line, 7) - st.equal(result.end.column, 16) - }) + const res = { // point to \n + start: 103, + length: 4, + file: 4, + jump: '-' + } + // case: 'file' is not yet assigned, while processing the srcmap (reverse looping) to find 'start', 'length' (etc..), we tumble on -1 for the file. + // in that case the step has to be discarded + result = sourceMappingDecoder.convertOffsetToLineColumn(res, linesbreak) + st.equal(result.start.line, 7) + st.equal(result.start.column, 12) + st.equal(result.end.line, 7) + st.equal(result.end.column, 16) + }) }) const contracts = `contract test { diff --git a/libs/remix-debug/test/traceManager.ts b/libs/remix-debug/test/traceManager.ts index 8128e21ac8..74e634d4ef 100644 --- a/libs/remix-debug/test/traceManager.ts +++ b/libs/remix-debug/test/traceManager.ts @@ -4,273 +4,273 @@ import tape from 'tape' const web3Test = require('./resources/testWeb3.ts') tape('TraceManager', function (t) { - let traceManager - - t.test('TraceManager.init', function (st) { - traceManager = new TraceManager({web3: web3Test}) - st.end() - }) - - t.test('TraceManager.resolveTrace', function (st) { - const tx = web3Test.eth.getTransaction('0x20ef65b8b186ca942fcccd634f37074dde49b541c27994fc7596740ef44cfd51') - traceManager.resolveTrace(tx).then(() => { - st.end() - }).catch(() => { - st.fail(' - traceManager.resolveTrace - failed ') - }) - }) - - t.test('TraceManager.getLength ', function (st) { - traceManager.getLength(function (error, result) { - if (error) { - st.fail(error) - } else { - st.end() - } - }) + let traceManager + + t.test('TraceManager.init', function (st) { + traceManager = new TraceManager({web3: web3Test}) + st.end() + }) + + t.test('TraceManager.resolveTrace', function (st) { + const tx = web3Test.eth.getTransaction('0x20ef65b8b186ca942fcccd634f37074dde49b541c27994fc7596740ef44cfd51') + traceManager.resolveTrace(tx).then(() => { + st.end() + }).catch(() => { + st.fail(' - traceManager.resolveTrace - failed ') }) + }) - t.test('TraceManager.inRange ', function (st) { - st.notOk(traceManager.inRange(-1)) - st.ok(traceManager.inRange(10)) - st.notOk(traceManager.inRange(142)) - st.ok(traceManager.inRange(141)) + t.test('TraceManager.getLength ', function (st) { + traceManager.getLength(function (error, result) { + if (error) { + st.fail(error) + } else { st.end() + } }) - - t.test('TraceManager.accumulateStorageChanges', function (st) { - const result = traceManager.accumulateStorageChanges(110, '0x0d3a18d64dfe4f927832ab58d6451cecc4e517c5', {}) - st.ok(result['0x290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e563'].value === '0x38') - st.end() - }) - - t.test('TraceManager.getCallData', function (st) { - try { - const result = traceManager.getCallDataAt(0) - st.ok(result[0] === '0x60fe47b10000000000000000000000000000000000000000000000000000000000000038') - st.end() - } catch (error) { - st.fail(error) - } - }) - - t.test('TraceManager.getCallStackAt', function (st) { - st.plan(3) - try { - const result = traceManager.getCallStackAt(0) - st.ok(result[0] === '0x0d3a18d64dfe4f927832ab58d6451cecc4e517c5') - } catch (error) { - st.fail(error) - } - - try { - const result = traceManager.getCallStackAt(64) - st.ok(result.length === 2) - st.ok(result[1] === '(Contract Creation - Step 63)') - } catch (error) { - st.fail(error) - } - }) - - t.test('TraceManager.getStackAt', function (st) { - st.plan(3) - try { - const result = traceManager.getStackAt(0) - console.log(result) - st.ok(result.length === 0) - } catch (error) { - st.fail(error) - } - - try { - const result = traceManager.getStackAt(28) - console.log(result) - st.ok(result.length === 4) - st.ok(result[3] === '0x60fe47b1') - } catch (error) { - st.fail(error) - } - }) - - t.test('TraceManager.getLastCallChangeSince', function (st) { - st.plan(3) - - try { - const result = traceManager.getLastCallChangeSince(10) - console.log(result) - st.ok(result.start === 0) - } catch (error) { - st.fail(error) - } - - try { - const result = traceManager.getLastCallChangeSince(70) - console.log(result) - st.ok(result.start === 64) - } catch (error) { - st.fail(error) - } - - try { - const result = traceManager.getLastCallChangeSince(111) - console.log(result) - st.ok(result.start === 0) - // this was 109 before: 111 is targeting the root call (starting index 0) - // this test make more sense as it is now (109 is the index of RETURN). - } catch (error) { - st.fail(error) - } - }) - - t.test('TraceManager.getCurrentCalledAddressAt', function (st) { - st.plan(3) - - try { - const result = traceManager.getCurrentCalledAddressAt(10) - console.log(result) - st.ok(result === '0x0d3a18d64dfe4f927832ab58d6451cecc4e517c5') - } catch (error) { - st.fail(error) - } - - try { - const result = traceManager.getCurrentCalledAddressAt(70) - console.log(result) - st.ok(result === '(Contract Creation - Step 63)') - } catch (error) { - st.fail(error) - } - - try { - const result = traceManager.getCurrentCalledAddressAt(111) - console.log(result) - st.ok(result === '0x0d3a18d64dfe4f927832ab58d6451cecc4e517c5') - } catch (error) { - st.fail(error) - } - }) - - t.test('TraceManager.getContractCreationCode', function (st) { // contract code has been retrieved from the memory - try { - const result = traceManager.getContractCreationCode('(Contract Creation - Step 63)') - console.log(result) - st.ok(result === '0x60606040526040516020806045833981016040528080519060200190919050505b806001016000600050819055505b50600a80603b6000396000f360606040526008565b00000000000000000000000000000000000000000000000000000000000000002d') - st.end() - } catch (error) { - st.fail(error) - } - }) - - t.test('TraceManager.getMemoryAt', function (st) { - st.plan(3) - try { - const result = traceManager.getMemoryAt(0, false) - console.log(result) - st.ok(result.length === 0) - } catch (error) { - st.fail(error) - } - - try { - const result = traceManager.getMemoryAt(34, false) - console.log(result) - st.ok(result.length === 3) - st.ok(result[2] === '0000000000000000000000000000000000000000000000000000000000000060') - } catch (error) { - st.fail(error) - } - }) - - t.test('TraceManager.getCurrentPC', function (st) { - try { - const result = traceManager.getCurrentPC(13) - console.log(result) - st.ok(result === '65') - st.end() - } catch (error) { - st.fail(error) - } - }) - - t.test('TraceManager.getCurrentStep', function (st) { - try { - const result = traceManager.getCurrentStep(66) - console.log(result) - st.ok(result === 2) - st.end() - } catch (error) { - st.fail(error) - } - }) - - t.test('TraceManager.getMemExpand', function (st) { - try { - const result = traceManager.getMemExpand(2) - console.log(result) - st.ok(result === '3') - st.end() - } catch (error) { - st.fail(error) - } - }) - - t.test('TraceManager.getStepCost', function (st) { - try { - const result = traceManager.getStepCost(23) - console.log(result) - st.ok(result === '3') - st.end() - } catch (error) { - st.fail(error) - } - }) - - t.test('TraceManager.getRemainingGas', function (st) { - try { - const result = traceManager.getRemainingGas(55) - console.log(result) - st.ok(result === '79306') - st.end() - } catch (error) { - st.fail(error) - } - }) - - t.test('TraceManager.findStepOverBack', function (st) { - const result = traceManager.findStepOverBack(116) - console.log(result) - st.ok(result === 115) - st.end() - }) - - t.test('TraceManager.findStepOverForward', function (st) { - const result = traceManager.findStepOverForward(66) - console.log(result) - st.ok(result === 67) - st.end() - }) - - t.test('TraceManager.findNextCall', function (st) { - const result = traceManager.findNextCall(10) - console.log(result) - st.ok(result === 63) - st.end() - }) - - t.test('TraceManager.getAddresses', function (st) { - const result = traceManager.getAddresses() - st.ok(result[0] === '0x0d3a18d64dfe4f927832ab58d6451cecc4e517c5') - st.ok(result[1] === '(Contract Creation - Step 63)') - st.end() - }) - - t.test('TraceManager.getReturnValue', function (st) { - try { - const result = traceManager.getReturnValue(108) - st.ok(result[0] === '0x60606040526008565b0000000000000000000000000000000000000000000000') - st.end() - } catch (error) { - st.fail(error) - } - }) + }) + + t.test('TraceManager.inRange ', function (st) { + st.notOk(traceManager.inRange(-1)) + st.ok(traceManager.inRange(10)) + st.notOk(traceManager.inRange(142)) + st.ok(traceManager.inRange(141)) + st.end() + }) + + t.test('TraceManager.accumulateStorageChanges', function (st) { + const result = traceManager.accumulateStorageChanges(110, '0x0d3a18d64dfe4f927832ab58d6451cecc4e517c5', {}) + st.ok(result['0x290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e563'].value === '0x38') + st.end() + }) + + t.test('TraceManager.getCallData', function (st) { + try { + const result = traceManager.getCallDataAt(0) + st.ok(result[0] === '0x60fe47b10000000000000000000000000000000000000000000000000000000000000038') + st.end() + } catch (error) { + st.fail(error) + } + }) + + t.test('TraceManager.getCallStackAt', function (st) { + st.plan(3) + try { + const result = traceManager.getCallStackAt(0) + st.ok(result[0] === '0x0d3a18d64dfe4f927832ab58d6451cecc4e517c5') + } catch (error) { + st.fail(error) + } + + try { + const result = traceManager.getCallStackAt(64) + st.ok(result.length === 2) + st.ok(result[1] === '(Contract Creation - Step 63)') + } catch (error) { + st.fail(error) + } + }) + + t.test('TraceManager.getStackAt', function (st) { + st.plan(3) + try { + const result = traceManager.getStackAt(0) + console.log(result) + st.ok(result.length === 0) + } catch (error) { + st.fail(error) + } + + try { + const result = traceManager.getStackAt(28) + console.log(result) + st.ok(result.length === 4) + st.ok(result[3] === '0x60fe47b1') + } catch (error) { + st.fail(error) + } + }) + + t.test('TraceManager.getLastCallChangeSince', function (st) { + st.plan(3) + + try { + const result = traceManager.getLastCallChangeSince(10) + console.log(result) + st.ok(result.start === 0) + } catch (error) { + st.fail(error) + } + + try { + const result = traceManager.getLastCallChangeSince(70) + console.log(result) + st.ok(result.start === 64) + } catch (error) { + st.fail(error) + } + + try { + const result = traceManager.getLastCallChangeSince(111) + console.log(result) + st.ok(result.start === 0) + // this was 109 before: 111 is targeting the root call (starting index 0) + // this test make more sense as it is now (109 is the index of RETURN). + } catch (error) { + st.fail(error) + } + }) + + t.test('TraceManager.getCurrentCalledAddressAt', function (st) { + st.plan(3) + + try { + const result = traceManager.getCurrentCalledAddressAt(10) + console.log(result) + st.ok(result === '0x0d3a18d64dfe4f927832ab58d6451cecc4e517c5') + } catch (error) { + st.fail(error) + } + + try { + const result = traceManager.getCurrentCalledAddressAt(70) + console.log(result) + st.ok(result === '(Contract Creation - Step 63)') + } catch (error) { + st.fail(error) + } + + try { + const result = traceManager.getCurrentCalledAddressAt(111) + console.log(result) + st.ok(result === '0x0d3a18d64dfe4f927832ab58d6451cecc4e517c5') + } catch (error) { + st.fail(error) + } + }) + + t.test('TraceManager.getContractCreationCode', function (st) { // contract code has been retrieved from the memory + try { + const result = traceManager.getContractCreationCode('(Contract Creation - Step 63)') + console.log(result) + st.ok(result === '0x60606040526040516020806045833981016040528080519060200190919050505b806001016000600050819055505b50600a80603b6000396000f360606040526008565b00000000000000000000000000000000000000000000000000000000000000002d') + st.end() + } catch (error) { + st.fail(error) + } + }) + + t.test('TraceManager.getMemoryAt', function (st) { + st.plan(3) + try { + const result = traceManager.getMemoryAt(0, false) + console.log(result) + st.ok(result.length === 0) + } catch (error) { + st.fail(error) + } + + try { + const result = traceManager.getMemoryAt(34, false) + console.log(result) + st.ok(result.length === 3) + st.ok(result[2] === '0000000000000000000000000000000000000000000000000000000000000060') + } catch (error) { + st.fail(error) + } + }) + + t.test('TraceManager.getCurrentPC', function (st) { + try { + const result = traceManager.getCurrentPC(13) + console.log(result) + st.ok(result === '65') + st.end() + } catch (error) { + st.fail(error) + } + }) + + t.test('TraceManager.getCurrentStep', function (st) { + try { + const result = traceManager.getCurrentStep(66) + console.log(result) + st.ok(result === 2) + st.end() + } catch (error) { + st.fail(error) + } + }) + + t.test('TraceManager.getMemExpand', function (st) { + try { + const result = traceManager.getMemExpand(2) + console.log(result) + st.ok(result === '3') + st.end() + } catch (error) { + st.fail(error) + } + }) + + t.test('TraceManager.getStepCost', function (st) { + try { + const result = traceManager.getStepCost(23) + console.log(result) + st.ok(result === '3') + st.end() + } catch (error) { + st.fail(error) + } + }) + + t.test('TraceManager.getRemainingGas', function (st) { + try { + const result = traceManager.getRemainingGas(55) + console.log(result) + st.ok(result === '79306') + st.end() + } catch (error) { + st.fail(error) + } + }) + + t.test('TraceManager.findStepOverBack', function (st) { + const result = traceManager.findStepOverBack(116) + console.log(result) + st.ok(result === 115) + st.end() + }) + + t.test('TraceManager.findStepOverForward', function (st) { + const result = traceManager.findStepOverForward(66) + console.log(result) + st.ok(result === 67) + st.end() + }) + + t.test('TraceManager.findNextCall', function (st) { + const result = traceManager.findNextCall(10) + console.log(result) + st.ok(result === 63) + st.end() + }) + + t.test('TraceManager.getAddresses', function (st) { + const result = traceManager.getAddresses() + st.ok(result[0] === '0x0d3a18d64dfe4f927832ab58d6451cecc4e517c5') + st.ok(result[1] === '(Contract Creation - Step 63)') + st.end() + }) + + t.test('TraceManager.getReturnValue', function (st) { + try { + const result = traceManager.getReturnValue(108) + st.ok(result[0] === '0x60606040526008565b0000000000000000000000000000000000000000000000') + st.end() + } catch (error) { + st.fail(error) + } + }) }) diff --git a/libs/remix-debug/test/vmCall.ts b/libs/remix-debug/test/vmCall.ts index 56d15729e9..64a88831ea 100644 --- a/libs/remix-debug/test/vmCall.ts +++ b/libs/remix-debug/test/vmCall.ts @@ -6,32 +6,32 @@ const Web3 = require('web3') async function getWeb3 () { - const remixSimulatorProvider = new Provider({ fork: 'berlin' }) - await remixSimulatorProvider.init() - await remixSimulatorProvider.Accounts.resetAccounts() - const web3 = new Web3(remixSimulatorProvider) - extendWeb3(web3) - return web3 + const remixSimulatorProvider = new Provider({ fork: 'berlin' }) + await remixSimulatorProvider.init() + await remixSimulatorProvider.Accounts.resetAccounts() + const web3 = new Web3(remixSimulatorProvider) + extendWeb3(web3) + return web3 } async function sendTx (web3, from, to, value, data, cb) { - try { - cb = cb || (() => {}) - const receipt = await web3.eth.sendTransaction({ - from: Address.fromPrivateKey(from.privateKey).toString(), - to, - value, - data, - gas: 7000000 - }) - cb(null, receipt.transactionHash) - return receipt.transactionHash - } catch (e) { - cb(e) - } + try { + cb = cb || (() => {}) + const receipt = await web3.eth.sendTransaction({ + from: Address.fromPrivateKey(from.privateKey).toString(), + to, + value, + data, + gas: 7000000 + }) + cb(null, receipt.transactionHash) + return receipt.transactionHash + } catch (e) { + cb(e) + } } module.exports = { - sendTx, - getWeb3 + sendTx, + getWeb3 } diff --git a/libs/remix-lib/src/eventManager.ts b/libs/remix-lib/src/eventManager.ts index 3b940e526b..0274e3378b 100644 --- a/libs/remix-lib/src/eventManager.ts +++ b/libs/remix-lib/src/eventManager.ts @@ -1,15 +1,15 @@ 'use strict' export class EventManager { - registered - anonymous + registered + anonymous - constructor () { - this.registered = {} - this.anonymous = {} - } + constructor () { + this.registered = {} + this.anonymous = {} + } - /* + /* * Unregister a listener. * Note that if obj is a function. the unregistration will be applied to the dummy obj {}. * @@ -17,22 +17,22 @@ export class EventManager { * @param {Object or Func} obj - object that will listen on this event * @param {Func} func - function of the listeners that will be executed */ - unregister (eventName, obj, func) { - if (!this.registered[eventName]) { - return - } - if (obj instanceof Function) { - func = obj - obj = this.anonymous - } - for (const reg in this.registered[eventName]) { - if ((this.registered[eventName][reg].obj === obj) && (this.registered[eventName][reg].func.toString() === func.toString())) { - this.registered[eventName].splice(reg, 1) - } - } + unregister (eventName, obj, func) { + if (!this.registered[eventName]) { + return + } + if (obj instanceof Function) { + func = obj + obj = this.anonymous + } + for (const reg in this.registered[eventName]) { + if ((this.registered[eventName][reg].obj === obj) && (this.registered[eventName][reg].func.toString() === func.toString())) { + this.registered[eventName].splice(reg, 1) + } } + } - /* + /* * Register a new listener. * Note that if obj is a function, the function registration will be associated with the dummy object {} * @@ -40,31 +40,31 @@ export class EventManager { * @param {Object or Func} obj - object that will listen on this event * @param {Func} func - function of the listeners that will be executed */ - register (eventName, obj, func?) { - if (!this.registered[eventName]) { - this.registered[eventName] = [] - } - if (obj instanceof Function) { - func = obj - obj = this.anonymous - } - this.registered[eventName].push({ obj, func }) + register (eventName, obj, func?) { + if (!this.registered[eventName]) { + this.registered[eventName] = [] } + if (obj instanceof Function) { + func = obj + obj = this.anonymous + } + this.registered[eventName].push({ obj, func }) + } - /* + /* * trigger event. * Every listener have their associated function executed * * @param {String} eventName - the event name * @param {Array}j - argument that will be passed to the executed function. */ - trigger (eventName, args) { - if (!this.registered[eventName]) { - return - } - for (const listener in this.registered[eventName]) { - const l = this.registered[eventName][listener] - if (l.func) l.func.apply(l.obj === this.anonymous ? {} : l.obj, args) - } + trigger (eventName, args) { + if (!this.registered[eventName]) { + return + } + for (const listener in this.registered[eventName]) { + const l = this.registered[eventName][listener] + if (l.func) l.func.apply(l.obj === this.anonymous ? {} : l.obj, args) } + } } diff --git a/libs/remix-lib/src/execution/eventsDecoder.ts b/libs/remix-lib/src/execution/eventsDecoder.ts index cdb794fd71..f9ba2ae71e 100644 --- a/libs/remix-lib/src/execution/eventsDecoder.ts +++ b/libs/remix-lib/src/execution/eventsDecoder.ts @@ -7,114 +7,114 @@ import { visitContracts } from './txHelper' * */ export class EventsDecoder { - resolveReceipt + resolveReceipt - constructor ({ resolveReceipt }) { - this.resolveReceipt = resolveReceipt - } + constructor ({ resolveReceipt }) { + this.resolveReceipt = resolveReceipt + } - /** + /** * use Transaction Receipt to decode logs. assume that the transaction as already been resolved by txListener. * logs are decoded only if the contract if known by remix. * * @param {Object} tx - transaction object * @param {Function} cb - callback */ - parseLogs (tx, contractName, compiledContracts, cb) { - if (tx.isCall) return cb(null, { decoded: [], raw: [] }) - this.resolveReceipt(tx, (error, receipt) => { - if (error) return cb(error) - this._decodeLogs(tx, receipt, contractName, compiledContracts, cb) - }) - } + parseLogs (tx, contractName, compiledContracts, cb) { + if (tx.isCall) return cb(null, { decoded: [], raw: [] }) + this.resolveReceipt(tx, (error, receipt) => { + if (error) return cb(error) + this._decodeLogs(tx, receipt, contractName, compiledContracts, cb) + }) + } - _decodeLogs (tx, receipt, contract, contracts, cb) { - if (!contract || !receipt) { - return cb('cannot decode logs - contract or receipt not resolved ') - } - if (!receipt.logs) { - return cb(null, { decoded: [], raw: [] }) - } - this._decodeEvents(tx, receipt.logs, contract, contracts, cb) + _decodeLogs (tx, receipt, contract, contracts, cb) { + if (!contract || !receipt) { + return cb('cannot decode logs - contract or receipt not resolved ') } - - _eventABI (contract): Record { - const eventABI: Record = {} - const abi = new ethers.utils.Interface(contract.abi) - for (const e in abi.events) { - const event = abi.getEvent(e) - eventABI[abi.getEventTopic(e).replace('0x', '')] = { event: event.name, inputs: event.inputs, object: event, abi: abi } - } - return eventABI + if (!receipt.logs) { + return cb(null, { decoded: [], raw: [] }) } + this._decodeEvents(tx, receipt.logs, contract, contracts, cb) + } - _eventsABI (compiledContracts): Record { - const eventsABI: Record = {} - visitContracts(compiledContracts, (contract) => { - eventsABI[contract.name] = this._eventABI(contract.object) - }) - return eventsABI + _eventABI (contract): Record { + const eventABI: Record = {} + const abi = new ethers.utils.Interface(contract.abi) + for (const e in abi.events) { + const event = abi.getEvent(e) + eventABI[abi.getEventTopic(e).replace('0x', '')] = { event: event.name, inputs: event.inputs, object: event, abi: abi } } + return eventABI + } - _event (hash, eventsABI) { - // get all the events responding to that hash. - const contracts = [] - for (const k in eventsABI) { - if (eventsABI[k][hash]) { - const event = eventsABI[k][hash] - for (const input of event.inputs) { - if (input.type === 'function') { - input.type = 'bytes24' - input.baseType = 'bytes24' - } - } - contracts.push(event) - } + _eventsABI (compiledContracts): Record { + const eventsABI: Record = {} + visitContracts(compiledContracts, (contract) => { + eventsABI[contract.name] = this._eventABI(contract.object) + }) + return eventsABI + } + + _event (hash, eventsABI) { + // get all the events responding to that hash. + const contracts = [] + for (const k in eventsABI) { + if (eventsABI[k][hash]) { + const event = eventsABI[k][hash] + for (const input of event.inputs) { + if (input.type === 'function') { + input.type = 'bytes24' + input.baseType = 'bytes24' + } } - return contracts + contracts.push(event) + } } + return contracts + } - _stringifyBigNumber (value): string { - return value._isBigNumber ? value.toString() : value - } + _stringifyBigNumber (value): string { + return value._isBigNumber ? value.toString() : value + } - _stringifyEvent (value) { - if (value === null || value === undefined) return ' - ' - if (value._ethersType) value.type = value._ethersType - if (Array.isArray(value)) { - // for struct && array - return value.map((item) => { return this._stringifyEvent(item) }) - } else { - return this._stringifyBigNumber(value) - } + _stringifyEvent (value) { + if (value === null || value === undefined) return ' - ' + if (value._ethersType) value.type = value._ethersType + if (Array.isArray(value)) { + // for struct && array + return value.map((item) => { return this._stringifyEvent(item) }) + } else { + return this._stringifyBigNumber(value) } + } - _decodeEvents (tx, logs, contractName, compiledContracts, cb) { - const eventsABI = this._eventsABI(compiledContracts) - const events = [] - for (const i in logs) { - // [address, topics, mem] - const log = logs[i] - const topicId = log.topics[0] - const eventAbis = this._event(topicId.replace('0x', ''), eventsABI) - for (const eventAbi of eventAbis) { - try { - if (eventAbi) { - const decodedlog = eventAbi.abi.parseLog(log) - const decoded = {} - for (const v in decodedlog.args) { - decoded[v] = this._stringifyEvent(decodedlog.args[v]) - } - events.push({ from: log.address, topic: topicId, event: eventAbi.event, args: decoded }) - } else { - events.push({ from: log.address, data: log.data, topics: log.topics }) - } - break // if one of the iteration is successful - } catch (e) { - continue - } + _decodeEvents (tx, logs, contractName, compiledContracts, cb) { + const eventsABI = this._eventsABI(compiledContracts) + const events = [] + for (const i in logs) { + // [address, topics, mem] + const log = logs[i] + const topicId = log.topics[0] + const eventAbis = this._event(topicId.replace('0x', ''), eventsABI) + for (const eventAbi of eventAbis) { + try { + if (eventAbi) { + const decodedlog = eventAbi.abi.parseLog(log) + const decoded = {} + for (const v in decodedlog.args) { + decoded[v] = this._stringifyEvent(decodedlog.args[v]) } + events.push({ from: log.address, topic: topicId, event: eventAbi.event, args: decoded }) + } else { + events.push({ from: log.address, data: log.data, topics: log.topics }) + } + break // if one of the iteration is successful + } catch (e) { + continue } - cb(null, { decoded: events, raw: logs }) + } } + cb(null, { decoded: events, raw: logs }) + } } diff --git a/libs/remix-lib/src/execution/forkAt.ts b/libs/remix-lib/src/execution/forkAt.ts index fe7b1b0804..34162bb7a7 100644 --- a/libs/remix-lib/src/execution/forkAt.ts +++ b/libs/remix-lib/src/execution/forkAt.ts @@ -8,130 +8,130 @@ * @return {String} - fork name (Berlin, Istanbul, ...) */ export function forkAt (networkId, blockNumber) { - if (forks[networkId]) { - let currentForkName = forks[networkId][0].name - for (const fork of forks[networkId]) { - if (blockNumber >= fork.number) { - currentForkName = fork.name - } - } - return currentForkName + if (forks[networkId]) { + let currentForkName = forks[networkId][0].name + for (const fork of forks[networkId]) { + if (blockNumber >= fork.number) { + currentForkName = fork.name + } } - return 'london' + return currentForkName + } + return 'london' } // see https://github.com/ethereum/go-ethereum/blob/master/params/config.go const forks = { - 1: [ - { - number: 4370000, - name: 'byzantium' - }, - { - number: 7280000, - name: 'constantinople' - }, - { - number: 7280000, - name: 'petersburg' - }, - { - number: 9069000, - name: 'istanbul' - }, - { - number: 9200000, - name: 'muirglacier' - }, - { - number: 12244000, - name: 'berlin' - }, - { - number: 12965000, - name: 'london' - }, - { - number: 13773000, - name: 'arrowGlacier' - }, - { - number: 15050000, - name: 'grayGlacier' - }, - { - number: 15537394, - name: 'merge' - } - ], - 3: [ - { - number: 1700000, - name: 'byzantium' - }, - { - number: 4230000, - name: 'constantinople' - }, - { - number: 4939394, - name: 'petersburg' - }, - { - number: 6485846, - name: 'istanbul' - }, - { - number: 7117117, - name: 'muirglacier' - }, - { - number: 9812189, - name: 'berlin' - }, - { - number: 10499401, - name: 'london' - } - ], - 4: [ - { - number: 1035301, - name: 'byzantium' - }, - { - number: 3660663, - name: 'constantinople' - }, - { - number: 4321234, - name: 'petersburg' - }, - { - number: 5435345, - name: 'istanbul' - }, - { - number: 8290928, - name: 'berlin' - }, - { - number: 8897988, - name: 'london' - } - ], - 5: [ - { - number: 1561651, - name: 'istanbul' - }, - { - number: 4460644, - name: 'berlin' - }, - { - number: 5062605, - name: 'london' - } - ] + 1: [ + { + number: 4370000, + name: 'byzantium' + }, + { + number: 7280000, + name: 'constantinople' + }, + { + number: 7280000, + name: 'petersburg' + }, + { + number: 9069000, + name: 'istanbul' + }, + { + number: 9200000, + name: 'muirglacier' + }, + { + number: 12244000, + name: 'berlin' + }, + { + number: 12965000, + name: 'london' + }, + { + number: 13773000, + name: 'arrowGlacier' + }, + { + number: 15050000, + name: 'grayGlacier' + }, + { + number: 15537394, + name: 'merge' + } + ], + 3: [ + { + number: 1700000, + name: 'byzantium' + }, + { + number: 4230000, + name: 'constantinople' + }, + { + number: 4939394, + name: 'petersburg' + }, + { + number: 6485846, + name: 'istanbul' + }, + { + number: 7117117, + name: 'muirglacier' + }, + { + number: 9812189, + name: 'berlin' + }, + { + number: 10499401, + name: 'london' + } + ], + 4: [ + { + number: 1035301, + name: 'byzantium' + }, + { + number: 3660663, + name: 'constantinople' + }, + { + number: 4321234, + name: 'petersburg' + }, + { + number: 5435345, + name: 'istanbul' + }, + { + number: 8290928, + name: 'berlin' + }, + { + number: 8897988, + name: 'london' + } + ], + 5: [ + { + number: 1561651, + name: 'istanbul' + }, + { + number: 4460644, + name: 'berlin' + }, + { + number: 5062605, + name: 'london' + } + ] } diff --git a/libs/remix-lib/src/execution/logsManager.ts b/libs/remix-lib/src/execution/logsManager.ts index 70e4ed9d41..3fdb44bf51 100644 --- a/libs/remix-lib/src/execution/logsManager.ts +++ b/libs/remix-lib/src/execution/logsManager.ts @@ -2,183 +2,183 @@ import { eachOf } from 'async' import { randomBytes } from 'crypto' export class LogsManager { - notificationCallbacks - subscriptions - filters - filterTracking - oldLogs - - constructor () { - this.notificationCallbacks = [] - this.subscriptions = {} - this.filters = {} - this.filterTracking = {} - this.oldLogs = [] - } - - checkBlock (blockNumber, block, web3) { - eachOf(block.transactions, (tx: any, i, next) => { - const txHash = '0x' + tx.hash().toString('hex') - - web3.eth.getTransactionReceipt(txHash, (_error, receipt) => { - for (const log of receipt.logs) { - this.oldLogs.push({ type: 'block', blockNumber, block, tx, log, txNumber: i }) - const subscriptions = this.getSubscriptionsFor({ type: 'block', blockNumber, block, tx, log }) - - for (const subscriptionId of subscriptions) { - const result = { - logIndex: '0x1', // 1 - blockNumber: blockNumber, - blockHash: ('0x' + block.hash().toString('hex')), - transactionHash: ('0x' + tx.hash().toString('hex')), - transactionIndex: '0x' + i.toString(16), - // TODO: if it's a contract deploy, it should be that address instead - address: log.address, - data: log.data, - topics: log.topics - } - - if (result.address === '0x') { - delete result.address - } - - const response = { jsonrpc: '2.0', method: 'eth_subscription', params: { result: result, subscription: subscriptionId } } - this.transmit(response) - } - } - }) - }, (_err) => { - }) - } - - eventMatchesFilter (changeEvent, queryType, queryFilter) { - if (queryFilter.topics.filter((logTopic) => changeEvent.log.topics.indexOf(logTopic) >= 0).length === 0) return false + notificationCallbacks + subscriptions + filters + filterTracking + oldLogs + + constructor () { + this.notificationCallbacks = [] + this.subscriptions = {} + this.filters = {} + this.filterTracking = {} + this.oldLogs = [] + } + + checkBlock (blockNumber, block, web3) { + eachOf(block.transactions, (tx: any, i, next) => { + const txHash = '0x' + tx.hash().toString('hex') + + web3.eth.getTransactionReceipt(txHash, (_error, receipt) => { + for (const log of receipt.logs) { + this.oldLogs.push({ type: 'block', blockNumber, block, tx, log, txNumber: i }) + const subscriptions = this.getSubscriptionsFor({ type: 'block', blockNumber, block, tx, log }) + + for (const subscriptionId of subscriptions) { + const result = { + logIndex: '0x1', // 1 + blockNumber: blockNumber, + blockHash: ('0x' + block.hash().toString('hex')), + transactionHash: ('0x' + tx.hash().toString('hex')), + transactionIndex: '0x' + i.toString(16), + // TODO: if it's a contract deploy, it should be that address instead + address: log.address, + data: log.data, + topics: log.topics + } - if (queryType === 'logs') { - const fromBlock = queryFilter.fromBlock || '0x0' - const toBlock = queryFilter.toBlock || this.oldLogs.length ? this.oldLogs[this.oldLogs.length - 1].blockNumber : '0x0' - if ((queryFilter.address === (changeEvent.tx.to || '').toString()) || queryFilter.address === (changeEvent.tx.getSenderAddress().toString())) { - if ((parseInt(toBlock) >= parseInt(changeEvent.blockNumber)) && (parseInt(fromBlock) <= parseInt(changeEvent.blockNumber))) { - return true - } + if (result.address === '0x') { + delete result.address } - } - return false + const response = { jsonrpc: '2.0', method: 'eth_subscription', params: { result: result, subscription: subscriptionId } } + this.transmit(response) + } + } + }) + }, (_err) => { + }) + } + + eventMatchesFilter (changeEvent, queryType, queryFilter) { + if (queryFilter.topics.filter((logTopic) => changeEvent.log.topics.indexOf(logTopic) >= 0).length === 0) return false + + if (queryType === 'logs') { + const fromBlock = queryFilter.fromBlock || '0x0' + const toBlock = queryFilter.toBlock || this.oldLogs.length ? this.oldLogs[this.oldLogs.length - 1].blockNumber : '0x0' + if ((queryFilter.address === (changeEvent.tx.to || '').toString()) || queryFilter.address === (changeEvent.tx.getSenderAddress().toString())) { + if ((parseInt(toBlock) >= parseInt(changeEvent.blockNumber)) && (parseInt(fromBlock) <= parseInt(changeEvent.blockNumber))) { + return true + } + } } - getSubscriptionsFor (changeEvent) { - const matchedSubscriptions = [] - for (const subscriptionId of Object.keys(this.subscriptions)) { - const subscriptionParams = this.subscriptions[subscriptionId] - const [queryType, queryFilter] = subscriptionParams + return false + } - if (this.eventMatchesFilter(changeEvent, queryType, queryFilter || { topics: [] })) { - matchedSubscriptions.push(subscriptionId) - } - } - return matchedSubscriptions + getSubscriptionsFor (changeEvent) { + const matchedSubscriptions = [] + for (const subscriptionId of Object.keys(this.subscriptions)) { + const subscriptionParams = this.subscriptions[subscriptionId] + const [queryType, queryFilter] = subscriptionParams + + if (this.eventMatchesFilter(changeEvent, queryType, queryFilter || { topics: [] })) { + matchedSubscriptions.push(subscriptionId) + } } + return matchedSubscriptions + } - getLogsForSubscription (subscriptionId) { - const subscriptionParams = this.subscriptions[subscriptionId] + getLogsForSubscription (subscriptionId) { + const subscriptionParams = this.subscriptions[subscriptionId] const [_queryType, queryFilter] = subscriptionParams // eslint-disable-line - return this.getLogsFor(queryFilter) + return this.getLogsFor(queryFilter) + } + + transmit (result) { + this.notificationCallbacks.forEach((callback) => { + if (result.params.result.raw) { + result.params.result.data = result.params.result.raw.data + result.params.result.topics = result.params.result.raw.topics + } + callback(result) + }) + } + + addListener (_type, cb) { + this.notificationCallbacks.push(cb) + } + + subscribe (params) { + const subscriptionId = '0x' + randomBytes(16).toString('hex') + this.subscriptions[subscriptionId] = params + return subscriptionId + } + + unsubscribe (subscriptionId) { + delete this.subscriptions[subscriptionId] + } + + newFilter (filterType, params) { + const filterId = '0x' + randomBytes(16).toString('hex') + if (filterType === 'block' || filterType === 'pendingTransactions') { + this.filters[filterId] = { filterType } } - - transmit (result) { - this.notificationCallbacks.forEach((callback) => { - if (result.params.result.raw) { - result.params.result.data = result.params.result.raw.data - result.params.result.topics = result.params.result.raw.topics - } - callback(result) - }) + if (filterType === 'filter') { + this.filters[filterId] = { filterType, params } } + this.filterTracking[filterId] = {} + return filterId + } - addListener (_type, cb) { - this.notificationCallbacks.push(cb) - } + uninstallFilter (filterId) { + delete this.filters[filterId] + } - subscribe (params) { - const subscriptionId = '0x' + randomBytes(16).toString('hex') - this.subscriptions[subscriptionId] = params - return subscriptionId - } + getLogsForFilter (filterId, logsOnly) { + const { filterType, params } = this.filters[filterId] + const tracking = this.filterTracking[filterId] - unsubscribe (subscriptionId) { - delete this.subscriptions[subscriptionId] + if (logsOnly || filterType === 'filter') { + return this.getLogsFor(params || { topics: [] }) } - - newFilter (filterType, params) { - const filterId = '0x' + randomBytes(16).toString('hex') - if (filterType === 'block' || filterType === 'pendingTransactions') { - this.filters[filterId] = { filterType } - } - if (filterType === 'filter') { - this.filters[filterId] = { filterType, params } - } - this.filterTracking[filterId] = {} - return filterId + if (filterType === 'block') { + const blocks = this.oldLogs.filter(x => x.type === 'block').filter(x => tracking.block === undefined || x.blockNumber >= tracking.block) + tracking.block = blocks[blocks.length - 1] + return blocks.map(block => ('0x' + block.hash().toString('hex'))) } - - uninstallFilter (filterId) { - delete this.filters[filterId] + if (filterType === 'pendingTransactions') { + return [] } - - getLogsForFilter (filterId, logsOnly) { - const { filterType, params } = this.filters[filterId] - const tracking = this.filterTracking[filterId] - - if (logsOnly || filterType === 'filter') { - return this.getLogsFor(params || { topics: [] }) - } - if (filterType === 'block') { - const blocks = this.oldLogs.filter(x => x.type === 'block').filter(x => tracking.block === undefined || x.blockNumber >= tracking.block) - tracking.block = blocks[blocks.length - 1] - return blocks.map(block => ('0x' + block.hash().toString('hex'))) + } + + getLogsByTxHash (hash) { + return this.oldLogs.filter((log) => '0x' + log.tx.hash().toString('hex') === hash) + .map((log) => { + return { + logIndex: '0x1', // 1 + blockNumber: log.blockNumber, + blockHash: ('0x' + log.block.hash().toString('hex')), + transactionHash: ('0x' + log.tx.hash().toString('hex')), + transactionIndex: '0x' + log.txNumber.toString(16), + // TODO: if it's a contract deploy, it should be that address instead + address: log.log.address, + data: log.log.data, + topics: log.log.topics } - if (filterType === 'pendingTransactions') { - return [] - } - } - - getLogsByTxHash (hash) { - return this.oldLogs.filter((log) => '0x' + log.tx.hash().toString('hex') === hash) - .map((log) => { - return { - logIndex: '0x1', // 1 - blockNumber: log.blockNumber, - blockHash: ('0x' + log.block.hash().toString('hex')), - transactionHash: ('0x' + log.tx.hash().toString('hex')), - transactionIndex: '0x' + log.txNumber.toString(16), - // TODO: if it's a contract deploy, it should be that address instead - address: log.log.address, - data: log.log.data, - topics: log.log.topics - } - }) + }) + } + + getLogsFor (params) { + const results = [] + for (const log of this.oldLogs) { + if (this.eventMatchesFilter(log, 'logs', params)) { + results.push({ + logIndex: '0x1', // 1 + blockNumber: log.blockNumber, + blockHash: ('0x' + log.block.hash().toString('hex')), + transactionHash: ('0x' + log.tx.hash().toString('hex')), + transactionIndex: '0x' + log.txNumber.toString(16), + // TODO: if it's a contract deploy, it should be that address instead + address: log.log.address, + data: log.log.data, + topics: log.log.topics + }) + } } - getLogsFor (params) { - const results = [] - for (const log of this.oldLogs) { - if (this.eventMatchesFilter(log, 'logs', params)) { - results.push({ - logIndex: '0x1', // 1 - blockNumber: log.blockNumber, - blockHash: ('0x' + log.block.hash().toString('hex')), - transactionHash: ('0x' + log.tx.hash().toString('hex')), - transactionIndex: '0x' + log.txNumber.toString(16), - // TODO: if it's a contract deploy, it should be that address instead - address: log.log.address, - data: log.log.data, - topics: log.log.topics - }) - } - } - - return results - } + return results + } } diff --git a/libs/remix-lib/src/execution/txExecution.ts b/libs/remix-lib/src/execution/txExecution.ts index 781d9733ce..7ef6d33c33 100644 --- a/libs/remix-lib/src/execution/txExecution.ts +++ b/libs/remix-lib/src/execution/txExecution.ts @@ -17,14 +17,14 @@ import { getFunctionFragment } from './txHelper' * @param {Function} finalCallback - last callback. */ export function createContract (from, data, value, gasLimit, txRunner, callbacks, finalCallback) { - if (!callbacks.confirmationCb || !callbacks.gasEstimationForceSend || !callbacks.promptCb) { - return finalCallback('all the callbacks must have been defined') - } - const tx = { from: from, to: null, data: data, useCall: false, value: value, gasLimit: gasLimit } - txRunner.rawRun(tx, callbacks.confirmationCb, callbacks.gasEstimationForceSend, callbacks.promptCb, (error, txResult) => { - // see universaldapp.js line 660 => 700 to check possible values of txResult (error case) - finalCallback(error, txResult) - }) + if (!callbacks.confirmationCb || !callbacks.gasEstimationForceSend || !callbacks.promptCb) { + return finalCallback('all the callbacks must have been defined') + } + const tx = { from: from, to: null, data: data, useCall: false, value: value, gasLimit: gasLimit } + txRunner.rawRun(tx, callbacks.confirmationCb, callbacks.gasEstimationForceSend, callbacks.promptCb, (error, txResult) => { + // see universaldapp.js line 660 => 700 to check possible values of txResult (error case) + finalCallback(error, txResult) + }) } /** @@ -43,12 +43,12 @@ export function createContract (from, data, value, gasLimit, txRunner, callbacks * @param {Function} finalCallback - last callback. */ export function callFunction (from, to, data, value, gasLimit, funAbi, txRunner, callbacks, finalCallback) { - const useCall = funAbi.stateMutability === 'view' || funAbi.stateMutability === 'pure' || funAbi.constant - const tx = { from, to, data, useCall, value, gasLimit } - txRunner.rawRun(tx, callbacks.confirmationCb, callbacks.gasEstimationForceSend, callbacks.promptCb, (error, txResult) => { - // see universaldapp.js line 660 => 700 to check possible values of txResult (error case) - finalCallback(error, txResult) - }) + const useCall = funAbi.stateMutability === 'view' || funAbi.stateMutability === 'pure' || funAbi.constant + const tx = { from, to, data, useCall, value, gasLimit } + txRunner.rawRun(tx, callbacks.confirmationCb, callbacks.gasEstimationForceSend, callbacks.promptCb, (error, txResult) => { + // see universaldapp.js line 660 => 700 to check possible values of txResult (error case) + finalCallback(error, txResult) + }) } /** @@ -58,118 +58,118 @@ export function callFunction (from, to, data, value, gasLimit, funAbi, txRunner, * @return {Object} - { error: true/false, message: DOMNode } */ export function checkVMError (execResult, compiledContracts) { - const errorCode = { - OUT_OF_GAS: 'out of gas', - STACK_UNDERFLOW: 'stack underflow', - STACK_OVERFLOW: 'stack overflow', - INVALID_JUMP: 'invalid JUMP', - INVALID_OPCODE: 'invalid opcode', - REVERT: 'revert', - STATIC_STATE_CHANGE: 'static state change', - INTERNAL_ERROR: 'internal error', - CREATE_COLLISION: 'create collision', - STOP: 'stop', - REFUND_EXHAUSTED: 'refund exhausted' - } - const ret = { - error: false, - message: '' - } - if (!execResult.exceptionError) { - return ret - } - const exceptionError = execResult.exceptionError.error || '' - const error = `VM error: ${exceptionError}.\n` - let msg - if (exceptionError === errorCode.INVALID_OPCODE) { - msg = '\t\n\tThe execution might have thrown.\n' - ret.error = true - } else if (exceptionError === errorCode.OUT_OF_GAS) { - msg = '\tThe transaction ran out of gas. Please increase the Gas Limit.\n' - ret.error = true - } else if (exceptionError === errorCode.REVERT) { - const returnData = execResult.returnValue - const returnDataHex = returnData.slice(2, 10) - let customError - if (compiledContracts) { - let decodedCustomErrorInputsClean - 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] + const errorCode = { + OUT_OF_GAS: 'out of gas', + STACK_UNDERFLOW: 'stack underflow', + STACK_OVERFLOW: 'stack overflow', + INVALID_JUMP: 'invalid JUMP', + INVALID_OPCODE: 'invalid opcode', + REVERT: 'revert', + STATIC_STATE_CHANGE: 'static state change', + INTERNAL_ERROR: 'internal error', + CREATE_COLLISION: 'create collision', + STOP: 'stop', + REFUND_EXHAUSTED: 'refund exhausted' + } + const ret = { + error: false, + message: '' + } + if (!execResult.exceptionError) { + return ret + } + const exceptionError = execResult.exceptionError.error || '' + const error = `VM error: ${exceptionError}.\n` + let msg + if (exceptionError === errorCode.INVALID_OPCODE) { + msg = '\t\n\tThe execution might have thrown.\n' + ret.error = true + } else if (exceptionError === errorCode.OUT_OF_GAS) { + msg = '\tThe transaction ran out of gas. Please increase the Gas Limit.\n' + ret.error = true + } else if (exceptionError === errorCode.REVERT) { + const returnData = execResult.returnValue + const returnDataHex = returnData.slice(2, 10) + let customError + if (compiledContracts) { + let decodedCustomErrorInputsClean + 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 - } - } - } + 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) { - msg = '\tThe transaction has been reverted to the initial state.\nError provided by the contract:' - msg += `\n${customError}` - msg += '\nParameters:' - msg += `\n${JSON.stringify(decodedCustomErrorInputsClean, null, ' ')}` - } - } - if (!customError) { - // It is the hash of Error(string) - if (returnData && (returnDataHex === '08c379a0')) { - const abiCoder = new ethers.utils.AbiCoder() - const reason = abiCoder.decode(['string'], '0x' + returnData.slice(10))[0] - msg = `\tThe transaction has been reverted to the initial state.\nReason provided by the contract: "${reason}".` - } else { - msg = '\tThe transaction has been reverted to the initial state.\nNote: The called function should be payable if you send value and the value you send should be less than your current balance.' - } + } } - ret.error = true - } else if (exceptionError === errorCode.STATIC_STATE_CHANGE) { - msg = '\tState changes is not allowed in Static Call context\n' - ret.error = true + } + if (decodedCustomErrorInputsClean) { + msg = '\tThe transaction has been reverted to the initial state.\nError provided by the contract:' + msg += `\n${customError}` + msg += '\nParameters:' + msg += `\n${JSON.stringify(decodedCustomErrorInputsClean, null, ' ')}` + } } - ret.message = `${error}\n${exceptionError}\n${msg}\nDebug the transaction to get more information.` - return ret + if (!customError) { + // It is the hash of Error(string) + if (returnData && (returnDataHex === '08c379a0')) { + const abiCoder = new ethers.utils.AbiCoder() + const reason = abiCoder.decode(['string'], '0x' + returnData.slice(10))[0] + msg = `\tThe transaction has been reverted to the initial state.\nReason provided by the contract: "${reason}".` + } else { + msg = '\tThe transaction has been reverted to the initial state.\nNote: The called function should be payable if you send value and the value you send should be less than your current balance.' + } + } + ret.error = true + } else if (exceptionError === errorCode.STATIC_STATE_CHANGE) { + msg = '\tState changes is not allowed in Static Call context\n' + ret.error = true + } + ret.message = `${error}\n${exceptionError}\n${msg}\nDebug the transaction to get more information.` + return ret } diff --git a/libs/remix-lib/src/execution/txFormat.ts b/libs/remix-lib/src/execution/txFormat.ts index de6ec91357..4e7c2a9abe 100644 --- a/libs/remix-lib/src/execution/txFormat.ts +++ b/libs/remix-lib/src/execution/txFormat.ts @@ -14,19 +14,19 @@ import fromExponential from 'from-exponential'; * @param {String} contractbyteCode */ export function encodeData (funABI, values, contractbyteCode) { - let encoded - let encodedHex - try { - encoded = encodeParamsHelper(funABI, values) - encodedHex = encoded.toString('hex') - } catch (e) { - return { error: 'cannot encode arguments' } - } - if (contractbyteCode) { - return { data: '0x' + contractbyteCode + encodedHex.replace('0x', '') } - } else { - return { data: encodeFunctionId(funABI) + encodedHex.replace('0x', '') } - } + let encoded + let encodedHex + try { + encoded = encodeParamsHelper(funABI, values) + encodedHex = encoded.toString('hex') + } catch (e) { + return { error: 'cannot encode arguments' } + } + if (contractbyteCode) { + return { data: '0x' + contractbyteCode + encodedHex.replace('0x', '') } + } else { + return { data: encodeFunctionId(funABI) + encodedHex.replace('0x', '') } + } } /** @@ -37,58 +37,58 @@ export function encodeData (funABI, values, contractbyteCode) { * @param {Function} callback - callback */ export function encodeParams (params, funAbi, callback?) { - return new Promise((resolve, reject) => { - let data: Buffer | string = '' - let dataHex = '' - let funArgs = [] - if (Array.isArray(params)) { - funArgs = params - if (funArgs.length > 0) { - try { - data = encodeParamsHelper(funAbi, funArgs) - dataHex = data.toString() - } catch (e) { - reject('Error encoding arguments: ' + e) - return callback && callback('Error encoding arguments: ' + e) - } - } - if (data.slice(0, 9) === 'undefined') { - dataHex = data.slice(9) - } - if (data.slice(0, 2) === '0x') { - dataHex = data.slice(2) - } - } else if (params.indexOf('raw:0x') === 0) { - // in that case we consider that the input is already encoded and *does not* contain the method signature - dataHex = params.replace('raw:0x', '') - data = Buffer.from(dataHex, 'hex') - } else { - try { - funArgs = parseFunctionParams(params) - } catch (e) { - reject('Error encoding arguments: ' + e) - return callback && callback('Error encoding arguments: ' + e) - } - try { - if (funArgs.length > 0) { - data = encodeParamsHelper(funAbi, funArgs) - dataHex = data.toString() - } - } catch (e) { - reject('Error encoding arguments: ' + e) - return callback && callback('Error encoding arguments: ' + e) - } - if (data.slice(0, 9) === 'undefined') { - dataHex = data.slice(9) - } - if (data.slice(0, 2) === '0x') { - dataHex = data.slice(2) - } + return new Promise((resolve, reject) => { + let data: Buffer | string = '' + let dataHex = '' + let funArgs = [] + if (Array.isArray(params)) { + funArgs = params + if (funArgs.length > 0) { + try { + data = encodeParamsHelper(funAbi, funArgs) + dataHex = data.toString() + } catch (e) { + reject('Error encoding arguments: ' + e) + return callback && callback('Error encoding arguments: ' + e) } - const result = { data: data, dataHex: dataHex, funArgs: funArgs } - callback && callback(null, result) - resolve(result) - }) + } + if (data.slice(0, 9) === 'undefined') { + dataHex = data.slice(9) + } + if (data.slice(0, 2) === '0x') { + dataHex = data.slice(2) + } + } else if (params.indexOf('raw:0x') === 0) { + // in that case we consider that the input is already encoded and *does not* contain the method signature + dataHex = params.replace('raw:0x', '') + data = Buffer.from(dataHex, 'hex') + } else { + try { + funArgs = parseFunctionParams(params) + } catch (e) { + reject('Error encoding arguments: ' + e) + return callback && callback('Error encoding arguments: ' + e) + } + try { + if (funArgs.length > 0) { + data = encodeParamsHelper(funAbi, funArgs) + dataHex = data.toString() + } + } catch (e) { + reject('Error encoding arguments: ' + e) + return callback && callback('Error encoding arguments: ' + e) + } + if (data.slice(0, 9) === 'undefined') { + dataHex = data.slice(9) + } + if (data.slice(0, 2) === '0x') { + dataHex = data.slice(2) + } + } + const result = { data: data, dataHex: dataHex, funArgs: funArgs } + callback && callback(null, result) + resolve(result) + }) } /** @@ -99,10 +99,10 @@ export function encodeParams (params, funAbi, callback?) { * @param {Function} callback - callback */ export function encodeFunctionCall (params, funAbi, callback) { - encodeParams(params, funAbi, (error, encodedParam) => { - if (error) return callback(error) - callback(null, { dataHex: encodeFunctionId(funAbi) + encodedParam.dataHex, funAbi, funArgs: encodedParam.funArgs }) - }) + encodeParams(params, funAbi, (error, encodedParam) => { + if (error) return callback(error) + callback(null, { dataHex: encodeFunctionId(funAbi) + encodedParam.dataHex, funAbi, funArgs: encodedParam.funArgs }) + }) } /** @@ -116,12 +116,12 @@ export function encodeFunctionCall (params, funAbi, callback) { * @param {Function} callback - callback */ export function encodeConstructorCallAndLinkLibraries (contract, params, funAbi, linkLibrariesAddresses, linkReferences, callback) { - encodeParams(params, funAbi, (error, encodedParam) => { - if (error) return callback(error) - linkLibraries(contract, linkLibrariesAddresses, linkReferences, (error, bytecodeToDeploy) => { - callback(error, { dataHex: bytecodeToDeploy + encodedParam.dataHex, funAbi, funArgs: encodedParam.funArgs, contractBytecode: contract.evm.bytecode.object }) - }) + encodeParams(params, funAbi, (error, encodedParam) => { + if (error) return callback(error) + linkLibraries(contract, linkLibrariesAddresses, linkReferences, (error, bytecodeToDeploy) => { + callback(error, { dataHex: bytecodeToDeploy + encodedParam.dataHex, funAbi, funArgs: encodedParam.funArgs, contractBytecode: contract.evm.bytecode.object }) }) + }) } /** @@ -133,22 +133,22 @@ export function encodeConstructorCallAndLinkLibraries (contract, params, funAbi, * @param {Function} callback - callback */ export function linkLibraries (contract, linkLibraries, linkReferences, callback) { - let bytecodeToDeploy = contract.evm.bytecode.object - if (bytecodeToDeploy.indexOf('_') >= 0) { - if (linkLibraries && linkReferences) { - for (const libFile in linkLibraries) { - for (const lib in linkLibraries[libFile]) { - const address = linkLibraries[libFile][lib] - if (!isValidAddress(address)) return callback(address + ' is not a valid address. Please check the provided address is valid.') - bytecodeToDeploy = linkLibraryStandardFromlinkReferences(lib, address.replace('0x', ''), bytecodeToDeploy, linkReferences) - } - } + let bytecodeToDeploy = contract.evm.bytecode.object + if (bytecodeToDeploy.indexOf('_') >= 0) { + if (linkLibraries && linkReferences) { + for (const libFile in linkLibraries) { + for (const lib in linkLibraries[libFile]) { + const address = linkLibraries[libFile][lib] + if (!isValidAddress(address)) return callback(address + ' is not a valid address. Please check the provided address is valid.') + bytecodeToDeploy = linkLibraryStandardFromlinkReferences(lib, address.replace('0x', ''), bytecodeToDeploy, linkReferences) } + } } - if (bytecodeToDeploy.indexOf('_') >= 0) { - return callback('Failed to link some libraries') - } - return callback(null, bytecodeToDeploy) + } + if (bytecodeToDeploy.indexOf('_') >= 0) { + return callback('Failed to link some libraries') + } + return callback(null, bytecodeToDeploy) } /** @@ -165,26 +165,26 @@ export function linkLibraries (contract, linkLibraries, linkReferences, callback * @param {Function} callback - callback */ export function encodeConstructorCallAndDeployLibraries (contractName, contract, contracts, params, funAbi, callback, callbackStep, callbackDeployLibrary) { - encodeParams(params, funAbi, (error, encodedParam) => { - if (error) return callback(error) - let dataHex = '' - const contractBytecode = contract.evm.bytecode.object - let bytecodeToDeploy = contract.evm.bytecode.object - if (bytecodeToDeploy.indexOf('_') >= 0) { - linkBytecode(contract, contracts, (err, bytecode) => { - if (err) { - callback('Error deploying required libraries: ' + err) - } else { - bytecodeToDeploy = bytecode + dataHex - return callback(null, { dataHex: bytecodeToDeploy, funAbi, funArgs: encodedParam.funArgs, contractBytecode, contractName: contractName }) - } - }, callbackStep, callbackDeployLibrary) - return + encodeParams(params, funAbi, (error, encodedParam) => { + if (error) return callback(error) + let dataHex = '' + const contractBytecode = contract.evm.bytecode.object + let bytecodeToDeploy = contract.evm.bytecode.object + if (bytecodeToDeploy.indexOf('_') >= 0) { + linkBytecode(contract, contracts, (err, bytecode) => { + if (err) { + callback('Error deploying required libraries: ' + err) } else { - dataHex = bytecodeToDeploy + encodedParam.dataHex + bytecodeToDeploy = bytecode + dataHex + return callback(null, { dataHex: bytecodeToDeploy, funAbi, funArgs: encodedParam.funArgs, contractBytecode, contractName: contractName }) } - callback(null, { dataHex: bytecodeToDeploy, funAbi, funArgs: encodedParam.funArgs, contractBytecode, contractName: contractName }) - }) + }, callbackStep, callbackDeployLibrary) + return + } else { + dataHex = bytecodeToDeploy + encodedParam.dataHex + } + callback(null, { dataHex: bytecodeToDeploy, funAbi, funArgs: encodedParam.funArgs, contractBytecode, contractName: contractName }) + }) } /** @@ -201,302 +201,302 @@ export function encodeConstructorCallAndDeployLibraries (contractName, contract, * @param {Function} callbackDeployLibrary - callbackDeployLibrary */ export function buildData (contractName, contract, contracts, isConstructor, funAbi, params, callback, callbackStep, callbackDeployLibrary) { - let funArgs = [] - let data: Buffer | string = '' - let dataHex = '' + let funArgs = [] + let data: Buffer | string = '' + let dataHex = '' - if (params.indexOf('raw:0x') === 0) { - // in that case we consider that the input is already encoded and *does not* contain the method signature - dataHex = params.replace('raw:0x', '') - data = Buffer.from(dataHex, 'hex') - } else { - try { - if (params.length > 0) { - funArgs = parseFunctionParams(params) - } - } catch (e) { - return callback('Error encoding arguments: ' + e) - } - try { - data = encodeParamsHelper(funAbi, funArgs) - dataHex = data.toString() - } catch (e) { - return callback('Error encoding arguments: ' + e) - } - if (data.slice(0, 9) === 'undefined') { - dataHex = data.slice(9) - } - if (data.slice(0, 2) === '0x') { - dataHex = data.slice(2) - } + if (params.indexOf('raw:0x') === 0) { + // in that case we consider that the input is already encoded and *does not* contain the method signature + dataHex = params.replace('raw:0x', '') + data = Buffer.from(dataHex, 'hex') + } else { + try { + if (params.length > 0) { + funArgs = parseFunctionParams(params) + } + } catch (e) { + return callback('Error encoding arguments: ' + e) } - let contractBytecode - if (isConstructor) { - contractBytecode = contract.evm.bytecode.object - let bytecodeToDeploy = contract.evm.bytecode.object - if (bytecodeToDeploy.indexOf('_') >= 0) { - linkBytecode(contract, contracts, (err, bytecode) => { - if (err) { - callback('Error deploying required libraries: ' + err) - } else { - bytecodeToDeploy = bytecode + dataHex - return callback(null, { dataHex: bytecodeToDeploy, funAbi, funArgs, contractBytecode, contractName: contractName }) - } - }, callbackStep, callbackDeployLibrary) - return + try { + data = encodeParamsHelper(funAbi, funArgs) + dataHex = data.toString() + } catch (e) { + return callback('Error encoding arguments: ' + e) + } + if (data.slice(0, 9) === 'undefined') { + dataHex = data.slice(9) + } + if (data.slice(0, 2) === '0x') { + dataHex = data.slice(2) + } + } + let contractBytecode + if (isConstructor) { + contractBytecode = contract.evm.bytecode.object + let bytecodeToDeploy = contract.evm.bytecode.object + if (bytecodeToDeploy.indexOf('_') >= 0) { + linkBytecode(contract, contracts, (err, bytecode) => { + if (err) { + callback('Error deploying required libraries: ' + err) } else { - dataHex = bytecodeToDeploy + dataHex + bytecodeToDeploy = bytecode + dataHex + return callback(null, { dataHex: bytecodeToDeploy, funAbi, funArgs, contractBytecode, contractName: contractName }) } + }, callbackStep, callbackDeployLibrary) + return } else { - dataHex = encodeFunctionId(funAbi) + dataHex + dataHex = bytecodeToDeploy + dataHex } - callback(null, { dataHex, funAbi, funArgs, contractBytecode, contractName: contractName }) + } else { + dataHex = encodeFunctionId(funAbi) + dataHex + } + callback(null, { dataHex, funAbi, funArgs, contractBytecode, contractName: contractName }) } export function atAddress () {} export function linkBytecodeStandard (contract, contracts, callback, callbackStep, callbackDeployLibrary) { - let contractBytecode = contract.evm.bytecode.object - eachOfSeries(contract.evm.bytecode.linkReferences, (libs, file, cbFile) => { - eachOfSeries(contract.evm.bytecode.linkReferences[file], (libRef, libName, cbLibDeployed) => { - const library = contracts[file][libName] - if (library) { - deployLibrary(file + ':' + libName, libName, library, contracts, (error, address) => { - if (error) { - return cbLibDeployed(error) - } - let hexAddress = address.toString('hex') - if (hexAddress.slice(0, 2) === '0x') { - hexAddress = hexAddress.slice(2) - } - contractBytecode = linkLibraryStandard(libName, hexAddress, contractBytecode, contract) - cbLibDeployed() - }, callbackStep, callbackDeployLibrary) - } else { - //@ts-ignore - cbLibDeployed('Cannot find compilation data of library ' + libName) - } - }, (error) => { - cbFile(error) - }) + let contractBytecode = contract.evm.bytecode.object + eachOfSeries(contract.evm.bytecode.linkReferences, (libs, file, cbFile) => { + eachOfSeries(contract.evm.bytecode.linkReferences[file], (libRef, libName, cbLibDeployed) => { + const library = contracts[file][libName] + if (library) { + deployLibrary(file + ':' + libName, libName, library, contracts, (error, address) => { + if (error) { + return cbLibDeployed(error) + } + let hexAddress = address.toString('hex') + if (hexAddress.slice(0, 2) === '0x') { + hexAddress = hexAddress.slice(2) + } + contractBytecode = linkLibraryStandard(libName, hexAddress, contractBytecode, contract) + cbLibDeployed() + }, callbackStep, callbackDeployLibrary) + } else { + //@ts-ignore + cbLibDeployed('Cannot find compilation data of library ' + libName) + } }, (error) => { - if (error) { - callbackStep(error) - } - callback(error, contractBytecode) + cbFile(error) }) + }, (error) => { + if (error) { + callbackStep(error) + } + callback(error, contractBytecode) + }) } export function linkBytecodeLegacy (contract, contracts, callback, callbackStep, callbackDeployLibrary) { - const libraryRefMatch = contract.evm.bytecode.object.match(/__([^_]{1,36})__/) - if (!libraryRefMatch) { - return callback('Invalid bytecode format.') - } - const libraryName = libraryRefMatch[1] - // file_name:library_name - const libRef = libraryName.match(/(.*):(.*)/) - if (!libRef) { - return callback('Cannot extract library reference ' + libraryName) - } - if (!contracts[libRef[1]] || !contracts[libRef[1]][libRef[2]]) { - return callback('Cannot find library reference ' + libraryName) + const libraryRefMatch = contract.evm.bytecode.object.match(/__([^_]{1,36})__/) + if (!libraryRefMatch) { + return callback('Invalid bytecode format.') + } + const libraryName = libraryRefMatch[1] + // file_name:library_name + const libRef = libraryName.match(/(.*):(.*)/) + if (!libRef) { + return callback('Cannot extract library reference ' + libraryName) + } + if (!contracts[libRef[1]] || !contracts[libRef[1]][libRef[2]]) { + return callback('Cannot find library reference ' + libraryName) + } + const libraryShortName = libRef[2] + const library = contracts[libRef[1]][libraryShortName] + if (!library) { + return callback('Library ' + libraryName + ' not found.') + } + deployLibrary(libraryName, libraryShortName, library, contracts, (err, address) => { + if (err) { + return callback(err) } - const libraryShortName = libRef[2] - const library = contracts[libRef[1]][libraryShortName] - if (!library) { - return callback('Library ' + libraryName + ' not found.') + let hexAddress = address.toString('hex') + if (hexAddress.slice(0, 2) === '0x') { + hexAddress = hexAddress.slice(2) } - deployLibrary(libraryName, libraryShortName, library, contracts, (err, address) => { - if (err) { - return callback(err) - } - let hexAddress = address.toString('hex') - if (hexAddress.slice(0, 2) === '0x') { - hexAddress = hexAddress.slice(2) - } - contract.evm.bytecode.object = linkLibrary(libraryName, hexAddress, contract.evm.bytecode.object) - linkBytecode(contract, contracts, callback, callbackStep, callbackDeployLibrary) - }, callbackStep, callbackDeployLibrary) + contract.evm.bytecode.object = linkLibrary(libraryName, hexAddress, contract.evm.bytecode.object) + linkBytecode(contract, contracts, callback, callbackStep, callbackDeployLibrary) + }, callbackStep, callbackDeployLibrary) } export function linkBytecode (contract, contracts, callback?, callbackStep?, callbackDeployLibrary?) { - if (contract.evm.bytecode.object.indexOf('_') < 0) { - return callback(null, contract.evm.bytecode.object) - } - if (contract.evm.bytecode.linkReferences && Object.keys(contract.evm.bytecode.linkReferences).length) { - linkBytecodeStandard(contract, contracts, callback, callbackStep, callbackDeployLibrary) - } else { - linkBytecodeLegacy(contract, contracts, callback, callbackStep, callbackDeployLibrary) - } + if (contract.evm.bytecode.object.indexOf('_') < 0) { + return callback(null, contract.evm.bytecode.object) + } + if (contract.evm.bytecode.linkReferences && Object.keys(contract.evm.bytecode.linkReferences).length) { + linkBytecodeStandard(contract, contracts, callback, callbackStep, callbackDeployLibrary) + } else { + linkBytecodeLegacy(contract, contracts, callback, callbackStep, callbackDeployLibrary) + } } export function deployLibrary (libraryName, libraryShortName, library, contracts, callback, callbackStep, callbackDeployLibrary) { - const address = library.address - if (address) { - return callback(null, address) - } - const bytecode = library.evm.bytecode.object - if (bytecode.indexOf('_') >= 0) { - linkBytecode(library, contracts, (err, bytecode) => { - if (err) callback(err) - else { - library.evm.bytecode.object = bytecode - deployLibrary(libraryName, libraryShortName, library, contracts, callback, callbackStep, callbackDeployLibrary) - } - }, callbackStep, callbackDeployLibrary) - } else { - callbackStep(`creation of library ${libraryName} pending...`) - const data = { dataHex: bytecode, funAbi: { type: 'constructor' }, funArgs: [], contractBytecode: bytecode, contractName: libraryShortName, contractABI: library.abi } - callbackDeployLibrary({ data: data, useCall: false }, (err, txResult) => { - if (err) { - return callback(err) - } - const address = txResult.receipt.contractAddress - library.address = address - callback(err, address) - }) - } + const address = library.address + if (address) { + return callback(null, address) + } + const bytecode = library.evm.bytecode.object + if (bytecode.indexOf('_') >= 0) { + linkBytecode(library, contracts, (err, bytecode) => { + if (err) callback(err) + else { + library.evm.bytecode.object = bytecode + deployLibrary(libraryName, libraryShortName, library, contracts, callback, callbackStep, callbackDeployLibrary) + } + }, callbackStep, callbackDeployLibrary) + } else { + callbackStep(`creation of library ${libraryName} pending...`) + const data = { dataHex: bytecode, funAbi: { type: 'constructor' }, funArgs: [], contractBytecode: bytecode, contractName: libraryShortName, contractABI: library.abi } + callbackDeployLibrary({ data: data, useCall: false }, (err, txResult) => { + if (err) { + return callback(err) + } + const address = txResult.receipt.contractAddress + library.address = address + callback(err, address) + }) + } } export function linkLibraryStandardFromlinkReferences (libraryName, address, bytecode, linkReferences) { - for (const file in linkReferences) { - for (const libName in linkReferences[file]) { - if (libraryName === libName) { - bytecode = setLibraryAddress(address, bytecode, linkReferences[file][libName]) - } - } + for (const file in linkReferences) { + for (const libName in linkReferences[file]) { + if (libraryName === libName) { + bytecode = setLibraryAddress(address, bytecode, linkReferences[file][libName]) + } } - return bytecode + } + return bytecode } export function linkLibraryStandard (libraryName, address, bytecode, contract) { - return linkLibraryStandardFromlinkReferences(libraryName, address, bytecode, contract.evm.bytecode.linkReferences) + return linkLibraryStandardFromlinkReferences(libraryName, address, bytecode, contract.evm.bytecode.linkReferences) } export function setLibraryAddress (address, bytecodeToLink, positions) { - if (positions) { - for (const pos of positions) { - const regpos = bytecodeToLink.match(new RegExp(`(.{${2 * pos.start}})(.{${2 * pos.length}})(.*)`)) - if (regpos) { - bytecodeToLink = regpos[1] + address + regpos[3] - } - } + if (positions) { + for (const pos of positions) { + const regpos = bytecodeToLink.match(new RegExp(`(.{${2 * pos.start}})(.{${2 * pos.length}})(.*)`)) + if (regpos) { + bytecodeToLink = regpos[1] + address + regpos[3] + } } - return bytecodeToLink + } + return bytecodeToLink } export function linkLibrary (libraryName, address, bytecodeToLink) { - return linkBytecodeSolc(bytecodeToLink, { [libraryName]: addHexPrefix(address) }) + return linkBytecodeSolc(bytecodeToLink, { [libraryName]: addHexPrefix(address) }) } export function decodeResponse (response, fnabi) { - // Only decode if there supposed to be fields - if (fnabi.outputs && fnabi.outputs.length > 0) { - try { - let i - const outputTypes = [] - for (i = 0; i < fnabi.outputs.length; i++) { - const type = fnabi.outputs[i].type - outputTypes.push(type.indexOf('tuple') === 0 ? makeFullTypeDefinition(fnabi.outputs[i]) : type) - } - if (!response || !response.length) response = new Uint8Array(32 * fnabi.outputs.length) // ensuring the data is at least filled by 0 cause `AbiCoder` throws if there's not engouh data - // decode data - const abiCoder = new ethers.utils.AbiCoder() - const decodedObj = abiCoder.decode(outputTypes, response) + // Only decode if there supposed to be fields + if (fnabi.outputs && fnabi.outputs.length > 0) { + try { + let i + const outputTypes = [] + for (i = 0; i < fnabi.outputs.length; i++) { + const type = fnabi.outputs[i].type + outputTypes.push(type.indexOf('tuple') === 0 ? makeFullTypeDefinition(fnabi.outputs[i]) : type) + } + if (!response || !response.length) response = new Uint8Array(32 * fnabi.outputs.length) // ensuring the data is at least filled by 0 cause `AbiCoder` throws if there's not engouh data + // decode data + const abiCoder = new ethers.utils.AbiCoder() + const decodedObj = abiCoder.decode(outputTypes, response) - const json = {} - for (i = 0; i < outputTypes.length; i++) { - const name = fnabi.outputs[i].name - json[i] = outputTypes[i] + ': ' + (name ? name + ' ' + decodedObj[i] : decodedObj[i]) - } + const json = {} + for (i = 0; i < outputTypes.length; i++) { + const name = fnabi.outputs[i].name + json[i] = outputTypes[i] + ': ' + (name ? name + ' ' + decodedObj[i] : decodedObj[i]) + } - return json - } catch (e) { - return { error: 'Failed to decode output: ' + e } - } + return json + } catch (e) { + return { error: 'Failed to decode output: ' + e } } - return {} + } + return {} } export function parseFunctionParams (params) { - const args = [] - // Check if parameter string starts with array or string - let startIndex = isArrayOrStringStart(params, 0) ? -1 : 0 - for (let i = 0; i < params.length; i++) { - // If a quote is received - if (params.charAt(i) === '"') { - startIndex = -1 - let endQuoteIndex = false - // look for closing quote. On success, push the complete string in arguments list - for (let j = i + 1; !endQuoteIndex; j++) { - if (params.charAt(j) === '"') { - args.push(normalizeParam(params.substring(i + 1, j))) - endQuoteIndex = true - i = j - } - // Throw error if end of params string is arrived but couldn't get end quote - if (!endQuoteIndex && j === params.length - 1) { - throw new Error('invalid params') - } - } - } else if (params.charAt(i) === '[') { // If an array/struct opening bracket is received - startIndex = -1 - let bracketCount = 1 - let j - for (j = i + 1; bracketCount !== 0; j++) { - // Increase count if another array opening bracket is received (To handle nested array) - if (params.charAt(j) === '[') { - bracketCount++ - } else if (params.charAt(j) === ']') { // // Decrease count if an array closing bracket is received (To handle nested array) - bracketCount-- - } - // Throw error if end of params string is arrived but couldn't get end of tuple - if (bracketCount !== 0 && j === params.length - 1) { - throw new Error('invalid tuple params') - } - if (bracketCount === 0) break - } - args.push(parseFunctionParams(params.substring(i + 1, j))) - i = j - 1 - } else if (params.charAt(i) === ',' || i === params.length - 1) { // , or end of string - // if startIndex >= 0, it means a parameter was being parsed, it can be first or other parameter - if (startIndex >= 0) { - let param = params.substring(startIndex, i === params.length - 1 ? undefined : i) - param = normalizeParam(param) - args.push(param) - } - // Register start index of a parameter to parse - startIndex = isArrayOrStringStart(params, i + 1) ? -1 : i + 1 + const args = [] + // Check if parameter string starts with array or string + let startIndex = isArrayOrStringStart(params, 0) ? -1 : 0 + for (let i = 0; i < params.length; i++) { + // If a quote is received + if (params.charAt(i) === '"') { + startIndex = -1 + let endQuoteIndex = false + // look for closing quote. On success, push the complete string in arguments list + for (let j = i + 1; !endQuoteIndex; j++) { + if (params.charAt(j) === '"') { + args.push(normalizeParam(params.substring(i + 1, j))) + endQuoteIndex = true + i = j + } + // Throw error if end of params string is arrived but couldn't get end quote + if (!endQuoteIndex && j === params.length - 1) { + throw new Error('invalid params') } + } + } else if (params.charAt(i) === '[') { // If an array/struct opening bracket is received + startIndex = -1 + let bracketCount = 1 + let j + for (j = i + 1; bracketCount !== 0; j++) { + // Increase count if another array opening bracket is received (To handle nested array) + if (params.charAt(j) === '[') { + bracketCount++ + } else if (params.charAt(j) === ']') { // // Decrease count if an array closing bracket is received (To handle nested array) + bracketCount-- + } + // Throw error if end of params string is arrived but couldn't get end of tuple + if (bracketCount !== 0 && j === params.length - 1) { + throw new Error('invalid tuple params') + } + if (bracketCount === 0) break + } + args.push(parseFunctionParams(params.substring(i + 1, j))) + i = j - 1 + } else if (params.charAt(i) === ',' || i === params.length - 1) { // , or end of string + // if startIndex >= 0, it means a parameter was being parsed, it can be first or other parameter + if (startIndex >= 0) { + let param = params.substring(startIndex, i === params.length - 1 ? undefined : i) + param = normalizeParam(param) + args.push(param) + } + // Register start index of a parameter to parse + startIndex = isArrayOrStringStart(params, i + 1) ? -1 : i + 1 } - return args + } + return args } export const normalizeParam = (param) => { - param = param.trim() - if (param.startsWith('0x')) param = `${param}` - if (/[0-9]/g.test(param)) param = `${param}` + param = param.trim() + if (param.startsWith('0x')) param = `${param}` + if (/[0-9]/g.test(param)) param = `${param}` - // fromExponential - if (!param.startsWith('0x')) { - const regSci = REGEX_SCIENTIFIC.exec(param) - const exponents = regSci ? regSci[2] : null - if (regSci && REGEX_DECIMAL.exec(exponents)) { - try { - let paramTrimmed = param.replace(/^'/g, '').replace(/'$/g, '') - paramTrimmed = paramTrimmed.replace(/^"/g, '').replace(/"$/g, '') - param = fromExponential(paramTrimmed) - } catch (e) { - console.log(e) - } - } - } - - if (typeof param === 'string') { - if (param === 'true') param = true - if (param === 'false') param = false + // fromExponential + if (!param.startsWith('0x')) { + const regSci = REGEX_SCIENTIFIC.exec(param) + const exponents = regSci ? regSci[2] : null + if (regSci && REGEX_DECIMAL.exec(exponents)) { + try { + let paramTrimmed = param.replace(/^'/g, '').replace(/'$/g, '') + paramTrimmed = paramTrimmed.replace(/^"/g, '').replace(/"$/g, '') + param = fromExponential(paramTrimmed) + } catch (e) { + console.log(e) + } } - return param + } + + if (typeof param === 'string') { + if (param === 'true') param = true + if (param === 'false') param = false + } + return param } export const REGEX_SCIENTIFIC = /^-?(\d+\.?\d*)e\d*(\d+)$/ @@ -504,5 +504,5 @@ export const REGEX_SCIENTIFIC = /^-?(\d+\.?\d*)e\d*(\d+)$/ export const REGEX_DECIMAL = /^\d*/ export function isArrayOrStringStart (str, index) { - return str.charAt(index) === '"' || str.charAt(index) === '[' + return str.charAt(index) === '"' || str.charAt(index) === '[' } diff --git a/libs/remix-lib/src/execution/txHelper.ts b/libs/remix-lib/src/execution/txHelper.ts index f3c78930b1..6115d911ff 100644 --- a/libs/remix-lib/src/execution/txHelper.ts +++ b/libs/remix-lib/src/execution/txHelper.ts @@ -2,145 +2,145 @@ import { ethers } from 'ethers' export function makeFullTypeDefinition (typeDef) { - if (typeDef && typeDef.type.indexOf('tuple') === 0 && typeDef.components) { - const innerTypes = typeDef.components.map((innerType) => { return makeFullTypeDefinition(innerType) }) - return `tuple(${innerTypes.join(',')})${extractSize(typeDef.type)}` - } - return typeDef.type + if (typeDef && typeDef.type.indexOf('tuple') === 0 && typeDef.components) { + const innerTypes = typeDef.components.map((innerType) => { return makeFullTypeDefinition(innerType) }) + return `tuple(${innerTypes.join(',')})${extractSize(typeDef.type)}` + } + return typeDef.type } export function encodeParams (funABI, args) { - const types = [] - if (funABI.inputs && funABI.inputs.length) { - for (let i = 0; i < funABI.inputs.length; i++) { - const type = funABI.inputs[i].type - // "false" will be converting to `false` and "true" will be working - // fine as abiCoder assume anything in quotes as `true` - if (type === 'bool' && args[i] === 'false') { - args[i] = false - } - types.push(type.indexOf('tuple') === 0 ? makeFullTypeDefinition(funABI.inputs[i]) : type) - if (args.length < types.length) { - args.push('') - } - } + const types = [] + if (funABI.inputs && funABI.inputs.length) { + for (let i = 0; i < funABI.inputs.length; i++) { + const type = funABI.inputs[i].type + // "false" will be converting to `false` and "true" will be working + // fine as abiCoder assume anything in quotes as `true` + if (type === 'bool' && args[i] === 'false') { + args[i] = false + } + types.push(type.indexOf('tuple') === 0 ? makeFullTypeDefinition(funABI.inputs[i]) : type) + if (args.length < types.length) { + args.push('') + } } + } - // NOTE: the caller will concatenate the bytecode and this - // it could be done here too for consistency - const abiCoder = new ethers.utils.AbiCoder() - return abiCoder.encode(types, args) + // NOTE: the caller will concatenate the bytecode and this + // it could be done here too for consistency + const abiCoder = new ethers.utils.AbiCoder() + return abiCoder.encode(types, args) } export function encodeFunctionId (funABI) { - if (funABI.type === 'fallback' || funABI.type === 'receive') return '0x' - const abi = new ethers.utils.Interface([funABI]) - return abi.getSighash(funABI.name) + if (funABI.type === 'fallback' || funABI.type === 'receive') return '0x' + const abi = new ethers.utils.Interface([funABI]) + return abi.getSighash(funABI.name) } export function getFunctionFragment (funABI): ethers.utils.Interface { - if (funABI.type === 'fallback' || funABI.type === 'receive') return null - return new ethers.utils.Interface([funABI]) + if (funABI.type === 'fallback' || funABI.type === 'receive') return null + return new ethers.utils.Interface([funABI]) } export function sortAbiFunction (contractabi) { - // Check if function is constant (introduced with Solidity 0.6.0) - const isConstant = ({ stateMutability }) => stateMutability === 'view' || stateMutability === 'pure' - // Sorts the list of ABI entries. Constant functions will appear first, - // followed by non-constant functions. Within those t wo groupings, functions - // will be sorted by their names. - return contractabi.sort(function (a, b) { - if (isConstant(a) && !isConstant(b)) { - return 1 - } else if (isConstant(b) && !isConstant(a)) { - return -1 - } - // If we reach here, either a and b are both constant or both not; sort by name then - // special case for fallback, receive and constructor function - if (a.type === 'function' && typeof a.name !== 'undefined') { - return a.name.localeCompare(b.name) - } else if (a.type === 'constructor' || a.type === 'fallback' || a.type === 'receive') { - return 1 - } - }) + // Check if function is constant (introduced with Solidity 0.6.0) + const isConstant = ({ stateMutability }) => stateMutability === 'view' || stateMutability === 'pure' + // Sorts the list of ABI entries. Constant functions will appear first, + // followed by non-constant functions. Within those t wo groupings, functions + // will be sorted by their names. + return contractabi.sort(function (a, b) { + if (isConstant(a) && !isConstant(b)) { + return 1 + } else if (isConstant(b) && !isConstant(a)) { + return -1 + } + // If we reach here, either a and b are both constant or both not; sort by name then + // special case for fallback, receive and constructor function + if (a.type === 'function' && typeof a.name !== 'undefined') { + return a.name.localeCompare(b.name) + } else if (a.type === 'constructor' || a.type === 'fallback' || a.type === 'receive') { + return 1 + } + }) } export function getConstructorInterface (abi) { - const funABI = { name: '', inputs: [], type: 'constructor', payable: false, outputs: [] } - if (typeof abi === 'string') { - try { - abi = JSON.parse(abi) - } catch (e) { - console.log('exception retrieving ctor abi ' + abi) - return funABI - } + const funABI = { name: '', inputs: [], type: 'constructor', payable: false, outputs: [] } + if (typeof abi === 'string') { + try { + abi = JSON.parse(abi) + } catch (e) { + console.log('exception retrieving ctor abi ' + abi) + return funABI } - - for (let i = 0; i < abi.length; i++) { - if (abi[i].type === 'constructor') { - funABI.inputs = abi[i].inputs || [] - funABI.payable = abi[i].payable - funABI['stateMutability'] = abi[i].stateMutability - break - } + } + + for (let i = 0; i < abi.length; i++) { + if (abi[i].type === 'constructor') { + funABI.inputs = abi[i].inputs || [] + funABI.payable = abi[i].payable + funABI['stateMutability'] = abi[i].stateMutability + break } + } - return funABI + return funABI } export function serializeInputs (fnAbi) { - let serialized = '(' - if (fnAbi.inputs && fnAbi.inputs.length) { - serialized += fnAbi.inputs.map((input) => { return input.type }).join(',') - } - serialized += ')' - return serialized + let serialized = '(' + if (fnAbi.inputs && fnAbi.inputs.length) { + serialized += fnAbi.inputs.map((input) => { return input.type }).join(',') + } + serialized += ')' + return serialized } export function extractSize (type) { - const size = type.match(/([a-zA-Z0-9])(\[.*\])/) - return size ? size[2] : '' + const size = type.match(/([a-zA-Z0-9])(\[.*\])/) + return size ? size[2] : '' } export function getFunctionLiner (fn, detailTuple: boolean = true) { - /* + /* if detailsTuple is True, this will return something like fnName((uint, string)) if detailsTuple is False, this will return something like fnName(tuple) */ - return fn.name + '(' + fn.inputs.map((value) => { - if (detailTuple && value.components) { - const fullType = makeFullTypeDefinition(value) - return fullType.replace(/tuple/g, '') // return of makeFullTypeDefinition might contain `tuple`, need to remove it cause `methodIdentifier` (fnName) does not include `tuple` keyword - } else { - return value.type - } - }).join(',') + ')' + return fn.name + '(' + fn.inputs.map((value) => { + if (detailTuple && value.components) { + const fullType = makeFullTypeDefinition(value) + return fullType.replace(/tuple/g, '') // return of makeFullTypeDefinition might contain `tuple`, need to remove it cause `methodIdentifier` (fnName) does not include `tuple` keyword + } else { + return value.type + } + }).join(',') + ')' } export function getFunction (abi, fnName) { - for (let i = 0; i < abi.length; i++) { - const fn = abi[i] - if (fn.type === 'function' && (fnName === getFunctionLiner(fn, true) || fnName === getFunctionLiner(fn, false))) { - return fn - } + for (let i = 0; i < abi.length; i++) { + const fn = abi[i] + if (fn.type === 'function' && (fnName === getFunctionLiner(fn, true) || fnName === getFunctionLiner(fn, false))) { + return fn } - return null + } + return null } export function getFallbackInterface (abi) { - for (let i = 0; i < abi.length; i++) { - if (abi[i].type === 'fallback') { - return abi[i] - } + for (let i = 0; i < abi.length; i++) { + if (abi[i].type === 'fallback') { + return abi[i] } + } } export function getReceiveInterface (abi) { - for (let i = 0; i < abi.length; i++) { - if (abi[i].type === 'receive') { - return abi[i] - } + for (let i = 0; i < abi.length; i++) { + if (abi[i].type === 'receive') { + return abi[i] } + } } /** @@ -150,12 +150,12 @@ export function getReceiveInterface (abi) { * @returns contract obj and associated file: { contract, file } or null */ export function getContract (contractName, contracts) { - for (const file in contracts) { - if (contracts[file][contractName]) { - return { object: contracts[file][contractName], file: file } - } + for (const file in contracts) { + if (contracts[file][contractName]) { + return { object: contracts[file][contractName], file: file } } - return null + } + return null } /** @@ -164,14 +164,14 @@ export function getContract (contractName, contracts) { * @param {Function} cb - callback */ export function visitContracts (contracts, cb) { - for (const file in contracts) { - for (const name in contracts[file]) { - if (cb({ name: name, object: contracts[file][name], file: file })) return - } + for (const file in contracts) { + for (const name in contracts[file]) { + if (cb({ name: name, object: contracts[file][name], file: file })) return } + } } export function inputParametersDeclarationToString (abiinputs) { - const inputs = (abiinputs || []).map((inp) => inp.type + ' ' + inp.name) - return inputs.join(', ') + const inputs = (abiinputs || []).map((inp) => inp.type + ' ' + inp.name) + return inputs.join(', ') } diff --git a/libs/remix-lib/src/execution/txListener.ts b/libs/remix-lib/src/execution/txListener.ts index 960ca63a55..99c2bac15a 100644 --- a/libs/remix-lib/src/execution/txListener.ts +++ b/libs/remix-lib/src/execution/txListener.ts @@ -7,13 +7,13 @@ import { decodeResponse } from './txFormat' import { getFunction, getReceiveInterface, getConstructorInterface, visitContracts, makeFullTypeDefinition } from './txHelper' function addExecutionCosts (txResult, tx, execResult) { - if (txResult) { - if (execResult) { - tx.returnValue = execResult.returnValue - if (execResult.executionGasUsed) tx.executionCost = execResult.executionGasUsed.toString(10) - } - if (txResult.receipt && txResult.receipt.gasUsed) tx.transactionCost = txResult.receipt.gasUsed.toString(10) + if (txResult) { + if (execResult) { + tx.returnValue = execResult.returnValue + if (execResult.executionGasUsed) tx.executionCost = execResult.executionGasUsed.toString(10) } + if (txResult.receipt && txResult.receipt.gasUsed) tx.transactionCost = txResult.receipt.gasUsed.toString(10) + } } /** @@ -24,373 +24,373 @@ function addExecutionCosts (txResult, tx, execResult) { * */ export class TxListener { - event - executionContext - _resolvedTransactions - _api - _resolvedContracts - _isListening: boolean - _listenOnNetwork:boolean - _loopId - blocks + event + executionContext + _resolvedTransactions + _api + _resolvedContracts + _isListening: boolean + _listenOnNetwork:boolean + _loopId + blocks - constructor (opt, executionContext) { - this.event = new EventManager() - // has a default for now for backwards compatability - this.executionContext = executionContext - this._api = opt.api - this._resolvedTransactions = {} - this._resolvedContracts = {} - this._isListening = false - this._listenOnNetwork = false - this._loopId = null - this.init() - this.executionContext.event.register('contextChanged', (context) => { - if (this._isListening) { - this.stopListening() - this.startListening() - } - }) + constructor (opt, executionContext) { + this.event = new EventManager() + // has a default for now for backwards compatability + this.executionContext = executionContext + this._api = opt.api + this._resolvedTransactions = {} + this._resolvedContracts = {} + this._isListening = false + this._listenOnNetwork = false + this._loopId = null + this.init() + this.executionContext.event.register('contextChanged', (context) => { + if (this._isListening) { + this.stopListening() + this.startListening() + } + }) - opt.event.udapp.register('callExecuted', async (error, from, to, data, lookupOnly, txResult) => { - if (error) return - // we go for that case if - // in VM mode - // in web3 mode && listen remix txs only - if (!this._isListening) return // we don't listen - if (this._loopId) return // we seems to already listen on a "web3" network + opt.event.udapp.register('callExecuted', async (error, from, to, data, lookupOnly, txResult) => { + if (error) return + // we go for that case if + // in VM mode + // in web3 mode && listen remix txs only + if (!this._isListening) return // we don't listen + if (this._loopId) return // we seems to already listen on a "web3" network - let returnValue - let execResult - if (this.executionContext.isVM()) { - execResult = await this.executionContext.web3().eth.getExecutionResultFromSimulator(txResult.transactionHash) - returnValue = toBuffer(execResult.returnValue) - } else { - returnValue = toBuffer(addHexPrefix(txResult.result)) - } - const call = { - from: from, - to: to, - input: data, - hash: txResult.transactionHash ? txResult.transactionHash : 'call' + (from || '') + to + data, - isCall: true, - returnValue, - envMode: this.executionContext.getProvider() - } + let returnValue + let execResult + if (this.executionContext.isVM()) { + execResult = await this.executionContext.web3().eth.getExecutionResultFromSimulator(txResult.transactionHash) + returnValue = toBuffer(execResult.returnValue) + } else { + returnValue = toBuffer(addHexPrefix(txResult.result)) + } + const call = { + from: from, + to: to, + input: data, + hash: txResult.transactionHash ? txResult.transactionHash : 'call' + (from || '') + to + data, + isCall: true, + returnValue, + envMode: this.executionContext.getProvider() + } - addExecutionCosts(txResult, call, execResult) - this._resolveTx(call, call, (error, resolvedData) => { - if (!error) { - this.event.trigger('newCall', [call]) - } - }) - }) + addExecutionCosts(txResult, call, execResult) + this._resolveTx(call, call, (error, resolvedData) => { + if (!error) { + this.event.trigger('newCall', [call]) + } + }) + }) - opt.event.udapp.register('transactionExecuted', (error, from, to, data, lookupOnly, txResult) => { - if (error) return - if (lookupOnly) return - // we go for that case if - // in VM mode - // in web3 mode && listen remix txs only - if (!this._isListening) return // we don't listen - if (this._loopId) return // we seems to already listen on a "web3" network - this.executionContext.web3().eth.getTransaction(txResult.transactionHash, async (error, tx) => { - if (error) return console.log(error) + opt.event.udapp.register('transactionExecuted', (error, from, to, data, lookupOnly, txResult) => { + if (error) return + if (lookupOnly) return + // we go for that case if + // in VM mode + // in web3 mode && listen remix txs only + if (!this._isListening) return // we don't listen + if (this._loopId) return // we seems to already listen on a "web3" network + this.executionContext.web3().eth.getTransaction(txResult.transactionHash, async (error, tx) => { + if (error) return console.log(error) - let execResult - if (this.executionContext.isVM()) { - execResult = await this.executionContext.web3().eth.getExecutionResultFromSimulator(txResult.transactionHash) - } + let execResult + if (this.executionContext.isVM()) { + execResult = await this.executionContext.web3().eth.getExecutionResultFromSimulator(txResult.transactionHash) + } - addExecutionCosts(txResult, tx, execResult) - tx.envMode = this.executionContext.getProvider() - tx.status = txResult.receipt.status - this._resolve([tx]) - }) - }) - } + addExecutionCosts(txResult, tx, execResult) + tx.envMode = this.executionContext.getProvider() + tx.status = txResult.receipt.status + this._resolve([tx]) + }) + }) + } - /** + /** * define if txlistener should listen on the network or if only tx created from remix are managed * * @param {Bool} type - true if listen on the network */ - setListenOnNetwork (listenOnNetwork) { - this._listenOnNetwork = listenOnNetwork - if (this._loopId) { - clearInterval(this._loopId) - } - this._listenOnNetwork ? this.startListening() : this.stopListening() + setListenOnNetwork (listenOnNetwork) { + this._listenOnNetwork = listenOnNetwork + if (this._loopId) { + clearInterval(this._loopId) } + this._listenOnNetwork ? this.startListening() : this.stopListening() + } - /** + /** * reset recorded transactions */ - init () { - this.blocks = [] - } + init () { + this.blocks = [] + } - /** + /** * start listening for incoming transactions * * @param {String} type - type/name of the provider to add * @param {Object} obj - provider */ - startListening () { - this.init() - this._isListening = true - if (this._listenOnNetwork && !this.executionContext.isVM()) { - this._startListenOnNetwork() - } + startListening () { + this.init() + this._isListening = true + if (this._listenOnNetwork && !this.executionContext.isVM()) { + this._startListenOnNetwork() } + } - /** + /** * stop listening for incoming transactions. do not reset the recorded pool. * * @param {String} type - type/name of the provider to add * @param {Object} obj - provider */ - stopListening () { - if (this._loopId) { - clearInterval(this._loopId) - } - this._loopId = null - this._isListening = false + stopListening () { + if (this._loopId) { + clearInterval(this._loopId) } + this._loopId = null + this._isListening = false + } - async _startListenOnNetwork () { - let lastSeenBlock = this.executionContext.lastBlock?.number - 1 - let processingBlock = false + async _startListenOnNetwork () { + let lastSeenBlock = this.executionContext.lastBlock?.number - 1 + let processingBlock = false - const processBlocks = async () => { - if (!this._isListening) return - if (processingBlock) return - processingBlock = true - const currentLoopId = this._loopId - if (this._loopId === null) { - processingBlock = false - return - } - if (!lastSeenBlock) { - lastSeenBlock = this.executionContext.lastBlock?.number // trying to resynchronize - console.log('listen on blocks, resynchronising') - processingBlock = false - return - } - const current = this.executionContext.lastBlock?.number - if (!current) { - console.log(new Error('no last block found')) - processingBlock = false - return - } - if (currentLoopId === this._loopId && lastSeenBlock < current) { - while (lastSeenBlock <= current) { - try { - if (!this._isListening) break - await this._manageBlock(lastSeenBlock) - } catch (e) { - console.log(e) - } - lastSeenBlock++ - } - lastSeenBlock = current - } - processingBlock = false + const processBlocks = async () => { + if (!this._isListening) return + if (processingBlock) return + processingBlock = true + const currentLoopId = this._loopId + if (this._loopId === null) { + processingBlock = false + return + } + if (!lastSeenBlock) { + lastSeenBlock = this.executionContext.lastBlock?.number // trying to resynchronize + console.log('listen on blocks, resynchronising') + processingBlock = false + return + } + const current = this.executionContext.lastBlock?.number + if (!current) { + console.log(new Error('no last block found')) + processingBlock = false + return + } + if (currentLoopId === this._loopId && lastSeenBlock < current) { + while (lastSeenBlock <= current) { + try { + if (!this._isListening) break + await this._manageBlock(lastSeenBlock) + } catch (e) { + console.log(e) + } + lastSeenBlock++ } - this._loopId = setInterval(processBlocks, 20000) - processBlocks() + lastSeenBlock = current + } + processingBlock = false } + this._loopId = setInterval(processBlocks, 20000) + processBlocks() + } - async _manageBlock (blockNumber) { - try { - const result = await this.executionContext.web3().eth.getBlock(blockNumber, true) - return await this._newBlock(Object.assign({ type: 'web3' }, result)) - } catch (e) {} - } + async _manageBlock (blockNumber) { + try { + const result = await this.executionContext.web3().eth.getBlock(blockNumber, true) + return await this._newBlock(Object.assign({ type: 'web3' }, result)) + } catch (e) {} + } - /** + /** * try to resolve the contract name from the given @arg address * * @param {String} address - contract address to resolve * @return {String} - contract name */ - resolvedContract (address) { - if (this._resolvedContracts[address]) return this._resolvedContracts[address].name - return null - } + resolvedContract (address) { + if (this._resolvedContracts[address]) return this._resolvedContracts[address].name + return null + } - /** + /** * try to resolve the transaction from the given @arg txHash * * @param {String} txHash - contract address to resolve * @return {String} - contract name */ - resolvedTransaction (txHash) { - return this._resolvedTransactions[txHash] - } + resolvedTransaction (txHash) { + return this._resolvedTransactions[txHash] + } - async _newBlock (block) { - this.blocks.push(block) - await this._resolve(block.transactions) - this.event.trigger('newBlock', [block]) - } + async _newBlock (block) { + this.blocks.push(block) + await this._resolve(block.transactions) + this.event.trigger('newBlock', [block]) + } - _resolveAsync (tx) { - return new Promise((resolve, reject) => { - this._api.resolveReceipt(tx, (error, receipt) => { - if (error) return reject(error) - this._resolveTx(tx, receipt, (error, resolvedData) => { - if (error) return reject(error) - if (resolvedData) { - this.event.trigger('txResolved', [tx, receipt, resolvedData]) - } - this.event.trigger('newTransaction', [tx, receipt]) - resolve({}) - }) - }) + _resolveAsync (tx) { + return new Promise((resolve, reject) => { + this._api.resolveReceipt(tx, (error, receipt) => { + if (error) return reject(error) + this._resolveTx(tx, receipt, (error, resolvedData) => { + if (error) return reject(error) + if (resolvedData) { + this.event.trigger('txResolved', [tx, receipt, resolvedData]) + } + this.event.trigger('newTransaction', [tx, receipt]) + resolve({}) }) - } + }) + }) + } - async _resolve (transactions) { - for (const tx of transactions) { - try { - if (!this._isListening) break - await this._resolveAsync(tx) - } catch (e) {} - } + async _resolve (transactions) { + for (const tx of transactions) { + try { + if (!this._isListening) break + await this._resolveAsync(tx) + } catch (e) {} } + } - _resolveTx (tx, receipt, cb) { - const contracts = this._api.contracts() - if (!contracts) return cb() - let fun - let contract - if (!tx.to || tx.to === '0x0') { // testrpc returns 0x0 in that case - // contract creation / resolve using the creation bytes code - // if web3: we have to call getTransactionReceipt to get the created address - // if VM: created address already included - const code = tx.input - contract = this._tryResolveContract(code, contracts, true) - if (contract) { - const address = receipt.contractAddress - this._resolvedContracts[address] = contract - fun = this._resolveFunction(contract, tx, true) - if (this._resolvedTransactions[tx.hash]) { - this._resolvedTransactions[tx.hash].contractAddress = address - } - return cb(null, { to: null, contractName: contract.name, function: fun, creationAddress: address }) - } - return cb() - } else { - // first check known contract, resolve against the `runtimeBytecode` if not known - contract = this._resolvedContracts[tx.to] - if (!contract) { - this.executionContext.web3().eth.getCode(tx.to, (error, code) => { - if (error) return cb(error) - if (code) { - const contract = this._tryResolveContract(code, contracts, false) - if (contract) { - this._resolvedContracts[tx.to] = contract - const fun = this._resolveFunction(contract, tx, false) - return cb(null, { to: tx.to, contractName: contract.name, function: fun }) - } - } - return cb() - }) - return - } + _resolveTx (tx, receipt, cb) { + const contracts = this._api.contracts() + if (!contracts) return cb() + let fun + let contract + if (!tx.to || tx.to === '0x0') { // testrpc returns 0x0 in that case + // contract creation / resolve using the creation bytes code + // if web3: we have to call getTransactionReceipt to get the created address + // if VM: created address already included + const code = tx.input + contract = this._tryResolveContract(code, contracts, true) + if (contract) { + const address = receipt.contractAddress + this._resolvedContracts[address] = contract + fun = this._resolveFunction(contract, tx, true) + if (this._resolvedTransactions[tx.hash]) { + this._resolvedTransactions[tx.hash].contractAddress = address + } + return cb(null, { to: null, contractName: contract.name, function: fun, creationAddress: address }) + } + return cb() + } else { + // first check known contract, resolve against the `runtimeBytecode` if not known + contract = this._resolvedContracts[tx.to] + if (!contract) { + this.executionContext.web3().eth.getCode(tx.to, (error, code) => { + if (error) return cb(error) + if (code) { + const contract = this._tryResolveContract(code, contracts, false) if (contract) { - fun = this._resolveFunction(contract, tx, false) - return cb(null, { to: tx.to, contractName: contract.name, function: fun }) + this._resolvedContracts[tx.to] = contract + const fun = this._resolveFunction(contract, tx, false) + return cb(null, { to: tx.to, contractName: contract.name, function: fun }) } - return cb() - } + } + return cb() + }) + return + } + if (contract) { + fun = this._resolveFunction(contract, tx, false) + return cb(null, { to: tx.to, contractName: contract.name, function: fun }) + } + return cb() } + } - _resolveFunction (contract, tx, isCtor) { - if (!contract) { - console.log('txListener: cannot resolve contract - contract is null') - return + _resolveFunction (contract, tx, isCtor) { + if (!contract) { + console.log('txListener: cannot resolve contract - contract is null') + return + } + const abi = contract.object.abi + const inputData = tx.input.replace('0x', '') + if (!isCtor) { + const methodIdentifiers = contract.object.evm.methodIdentifiers + for (const fn in methodIdentifiers) { + if (methodIdentifiers[fn] === inputData.substring(0, 8)) { + const fnabi = getFunction(abi, fn) + this._resolvedTransactions[tx.hash] = { + contractName: contract.name, + to: tx.to, + fn: fn, + params: this._decodeInputParams(inputData.substring(8), fnabi) + } + if (tx.returnValue) { + this._resolvedTransactions[tx.hash].decodedReturnValue = decodeResponse(tx.returnValue, fnabi) + } + return this._resolvedTransactions[tx.hash] } - const abi = contract.object.abi - const inputData = tx.input.replace('0x', '') - if (!isCtor) { - const methodIdentifiers = contract.object.evm.methodIdentifiers - for (const fn in methodIdentifiers) { - if (methodIdentifiers[fn] === inputData.substring(0, 8)) { - const fnabi = getFunction(abi, fn) - this._resolvedTransactions[tx.hash] = { - contractName: contract.name, - to: tx.to, - fn: fn, - params: this._decodeInputParams(inputData.substring(8), fnabi) - } - if (tx.returnValue) { - this._resolvedTransactions[tx.hash].decodedReturnValue = decodeResponse(tx.returnValue, fnabi) - } - return this._resolvedTransactions[tx.hash] - } - } - // receive function - if (!inputData && getReceiveInterface(abi)) { - this._resolvedTransactions[tx.hash] = { - contractName: contract.name, - to: tx.to, - fn: '(receive)', - params: null - } - } else { - // fallback function - this._resolvedTransactions[tx.hash] = { - contractName: contract.name, - to: tx.to, - fn: '(fallback)', - params: null - } - } - } else { - const bytecode = contract.object.evm.bytecode.object - let params = null - if (bytecode && bytecode.length) { - params = this._decodeInputParams(getinputParameters(inputData), getConstructorInterface(abi)) - } - this._resolvedTransactions[tx.hash] = { - contractName: contract.name, - to: null, - fn: '(constructor)', - params: params - } + } + // receive function + if (!inputData && getReceiveInterface(abi)) { + this._resolvedTransactions[tx.hash] = { + contractName: contract.name, + to: tx.to, + fn: '(receive)', + params: null } - return this._resolvedTransactions[tx.hash] + } else { + // fallback function + this._resolvedTransactions[tx.hash] = { + contractName: contract.name, + to: tx.to, + fn: '(fallback)', + params: null + } + } + } else { + const bytecode = contract.object.evm.bytecode.object + let params = null + if (bytecode && bytecode.length) { + params = this._decodeInputParams(getinputParameters(inputData), getConstructorInterface(abi)) + } + this._resolvedTransactions[tx.hash] = { + contractName: contract.name, + to: null, + fn: '(constructor)', + params: params + } } + return this._resolvedTransactions[tx.hash] + } - _tryResolveContract (codeToResolve, compiledContracts, isCreation) { - let found = null - visitContracts(compiledContracts, (contract) => { - const bytes = isCreation ? contract.object.evm.bytecode.object : contract.object.evm.deployedBytecode.object - if (compareByteCode(codeToResolve, '0x' + bytes)) { - found = contract - return true - } - }) - return found - } + _tryResolveContract (codeToResolve, compiledContracts, isCreation) { + let found = null + visitContracts(compiledContracts, (contract) => { + const bytes = isCreation ? contract.object.evm.bytecode.object : contract.object.evm.deployedBytecode.object + if (compareByteCode(codeToResolve, '0x' + bytes)) { + found = contract + return true + } + }) + return found + } - _decodeInputParams (data, abi) { - data = toBuffer(addHexPrefix(data)) - if (!data.length) data = new Uint8Array(32 * abi.inputs.length) // ensuring the data is at least filled by 0 cause `AbiCoder` throws if there's not engouh data + _decodeInputParams (data, abi) { + data = toBuffer(addHexPrefix(data)) + if (!data.length) data = new Uint8Array(32 * abi.inputs.length) // ensuring the data is at least filled by 0 cause `AbiCoder` throws if there's not engouh data - const inputTypes = [] - for (let i = 0; i < abi.inputs.length; i++) { - const type = abi.inputs[i].type - inputTypes.push(type.indexOf('tuple') === 0 ? makeFullTypeDefinition(abi.inputs[i]) : type) - } - const abiCoder = new ethers.utils.AbiCoder() - const decoded = abiCoder.decode(inputTypes, data) - const ret = {} - for (const k in abi.inputs) { - ret[abi.inputs[k].type + ' ' + abi.inputs[k].name] = decoded[k] - } - return ret + const inputTypes = [] + for (let i = 0; i < abi.inputs.length; i++) { + const type = abi.inputs[i].type + inputTypes.push(type.indexOf('tuple') === 0 ? makeFullTypeDefinition(abi.inputs[i]) : type) + } + const abiCoder = new ethers.utils.AbiCoder() + const decoded = abiCoder.decode(inputTypes, data) + const ret = {} + for (const k in abi.inputs) { + ret[abi.inputs[k].type + ' ' + abi.inputs[k].name] = decoded[k] } + return ret + } } diff --git a/libs/remix-lib/src/execution/txRunner.ts b/libs/remix-lib/src/execution/txRunner.ts index 19f643cc7d..6a22342e53 100644 --- a/libs/remix-lib/src/execution/txRunner.ts +++ b/libs/remix-lib/src/execution/txRunner.ts @@ -13,44 +13,44 @@ export type Transaction = { } export class TxRunner { - event - pendingTxs - queusTxs - opt - internalRunner - constructor (internalRunner, opt) { - this.opt = opt || {} - this.internalRunner = internalRunner - this.event = new EventManager() + event + pendingTxs + queusTxs + opt + internalRunner + constructor (internalRunner, opt) { + this.opt = opt || {} + this.internalRunner = internalRunner + this.event = new EventManager() - this.pendingTxs = {} - this.queusTxs = [] - } + this.pendingTxs = {} + this.queusTxs = [] + } - rawRun (args: Transaction, confirmationCb, gasEstimationForceSend, promptCb, cb) { - run(this, args, args.timestamp || Date.now(), confirmationCb, gasEstimationForceSend, promptCb, cb) - } + rawRun (args: Transaction, confirmationCb, gasEstimationForceSend, promptCb, cb) { + run(this, args, args.timestamp || Date.now(), confirmationCb, gasEstimationForceSend, promptCb, cb) + } - execute (args: Transaction, confirmationCb, gasEstimationForceSend, promptCb, callback) { - let data = args.data - if (data.slice(0, 2) !== '0x') { - data = '0x' + data - } - this.internalRunner.execute(args, confirmationCb, gasEstimationForceSend, promptCb, callback) + execute (args: Transaction, confirmationCb, gasEstimationForceSend, promptCb, callback) { + let data = args.data + if (data.slice(0, 2) !== '0x') { + data = '0x' + data } + this.internalRunner.execute(args, confirmationCb, gasEstimationForceSend, promptCb, callback) + } } function run (self, tx: Transaction, stamp, confirmationCb, gasEstimationForceSend = null, promptCb = null, callback = null) { - if (Object.keys(self.pendingTxs).length) { - return self.queusTxs.push({ tx, stamp, confirmationCb, gasEstimationForceSend, promptCb, callback }) + if (Object.keys(self.pendingTxs).length) { + return self.queusTxs.push({ tx, stamp, confirmationCb, gasEstimationForceSend, promptCb, callback }) + } + self.pendingTxs[stamp] = tx + self.execute(tx, confirmationCb, gasEstimationForceSend, promptCb, function (error, result) { + delete self.pendingTxs[stamp] + if (callback && typeof callback === 'function') callback(error, result) + if (self.queusTxs.length) { + const next = self.queusTxs.pop() + run(self, next.tx, next.stamp, next.confirmationCb, next.gasEstimationForceSend, next.promptCb, next.callback) } - self.pendingTxs[stamp] = tx - self.execute(tx, confirmationCb, gasEstimationForceSend, promptCb, function (error, result) { - delete self.pendingTxs[stamp] - if (callback && typeof callback === 'function') callback(error, result) - if (self.queusTxs.length) { - const next = self.queusTxs.pop() - run(self, next.tx, next.stamp, next.confirmationCb, next.gasEstimationForceSend, next.promptCb, next.callback) - } - }) + }) } diff --git a/libs/remix-lib/src/execution/txRunnerVM.ts b/libs/remix-lib/src/execution/txRunnerVM.ts index 5a800e2326..2f7da45647 100644 --- a/libs/remix-lib/src/execution/txRunnerVM.ts +++ b/libs/remix-lib/src/execution/txRunnerVM.ts @@ -19,140 +19,140 @@ export type VMexecutionResult = { export type VMExecutionCallBack = (error: string | Error, result?: VMexecutionResult) => void export class TxRunnerVM { - event - blockNumber - pendingTxs - vmaccounts - queusTxs - blocks - logsManager - commonContext - blockParentHash - nextNonceForCall: number - getVMObject: () => any + event + blockNumber + pendingTxs + vmaccounts + queusTxs + blocks + logsManager + commonContext + blockParentHash + nextNonceForCall: number + getVMObject: () => any - constructor (vmaccounts, api, getVMObject) { - this.event = new EventManager() - this.logsManager = new LogsManager() - // has a default for now for backwards compatability - this.getVMObject = getVMObject - this.commonContext = this.getVMObject().common - this.blockNumber = 0 - this.pendingTxs = {} - this.vmaccounts = vmaccounts - this.queusTxs = [] - this.blocks = [] - /* + constructor (vmaccounts, api, getVMObject) { + this.event = new EventManager() + this.logsManager = new LogsManager() + // has a default for now for backwards compatability + this.getVMObject = getVMObject + this.commonContext = this.getVMObject().common + this.blockNumber = 0 + this.pendingTxs = {} + this.vmaccounts = vmaccounts + this.queusTxs = [] + this.blocks = [] + /* txHash is generated using the nonce, in order to have unique transaction hash, we need to keep using different nonce (in case of a call) so we increment this value after each call. For this to function we also need to skip nonce validation, in the vm: `{ skipNonce: true }` */ - this.nextNonceForCall = 0 + this.nextNonceForCall = 0 - const vm = this.getVMObject().vm - this.blockParentHash = vm.blockchain.genesisBlock.hash() - } + const vm = this.getVMObject().vm + this.blockParentHash = vm.blockchain.genesisBlock.hash() + } - execute (args: InternalTransaction, confirmationCb, gasEstimationForceSend, promptCb, callback: VMExecutionCallBack) { - let data = args.data - if (data.slice(0, 2) !== '0x') { - data = '0x' + data - } + execute (args: InternalTransaction, confirmationCb, gasEstimationForceSend, promptCb, callback: VMExecutionCallBack) { + let data = args.data + if (data.slice(0, 2) !== '0x') { + data = '0x' + data + } - try { - this.runInVm(args.from, args.to, data, args.value, args.gasLimit, args.useCall, callback) - } catch (e) { - callback(e, null) - } + try { + this.runInVm(args.from, args.to, data, args.value, args.gasLimit, args.useCall, callback) + } catch (e) { + callback(e, null) } + } - runInVm (from: string, to: string, data: string, value: string, gasLimit: number, useCall: boolean, callback: VMExecutionCallBack) { - let account - if (!from && useCall && Object.keys(this.vmaccounts).length) { - from = Object.keys(this.vmaccounts)[0] - account = this.vmaccounts[from] - } else account = this.vmaccounts[from] + runInVm (from: string, to: string, data: string, value: string, gasLimit: number, useCall: boolean, callback: VMExecutionCallBack) { + let account + if (!from && useCall && Object.keys(this.vmaccounts).length) { + from = Object.keys(this.vmaccounts)[0] + account = this.vmaccounts[from] + } else account = this.vmaccounts[from] - if (!account) { - return callback('Invalid account selected') - } + if (!account) { + return callback('Invalid account selected') + } - this.getVMObject().stateManager.getAccount(Address.fromString(from)).then((res: Account) => { - const EIP1559 = this.commonContext.hardfork() !== 'berlin' // berlin is the only pre eip1559 fork that we handle. - let tx - if (!EIP1559) { - tx = Transaction.fromTxData({ - nonce: useCall ? this.nextNonceForCall : res.nonce, - gasPrice: '0x1', - gasLimit: gasLimit, - to: to, - value: value, - data: Buffer.from(data.slice(2), 'hex') - }, { common: this.commonContext }).sign(account.privateKey) - } else { - tx = FeeMarketEIP1559Transaction.fromTxData({ - nonce: useCall ? this.nextNonceForCall : res.nonce, - maxPriorityFeePerGas: '0x01', - maxFeePerGas: '0x1', - gasLimit: gasLimit, - to: to, - value: value, - data: Buffer.from(data.slice(2), 'hex') - }).sign(account.privateKey) - } - if (useCall) this.nextNonceForCall++ + this.getVMObject().stateManager.getAccount(Address.fromString(from)).then((res: Account) => { + const EIP1559 = this.commonContext.hardfork() !== 'berlin' // berlin is the only pre eip1559 fork that we handle. + let tx + if (!EIP1559) { + tx = Transaction.fromTxData({ + nonce: useCall ? this.nextNonceForCall : res.nonce, + gasPrice: '0x1', + gasLimit: gasLimit, + to: to, + value: value, + data: Buffer.from(data.slice(2), 'hex') + }, { common: this.commonContext }).sign(account.privateKey) + } else { + tx = FeeMarketEIP1559Transaction.fromTxData({ + nonce: useCall ? this.nextNonceForCall : res.nonce, + maxPriorityFeePerGas: '0x01', + maxFeePerGas: '0x1', + gasLimit: gasLimit, + to: to, + value: value, + data: Buffer.from(data.slice(2), 'hex') + }).sign(account.privateKey) + } + if (useCall) this.nextNonceForCall++ - const coinbases = ['0x0e9281e9c6a0808672eaba6bd1220e144c9bb07a', '0x8945a1288dc78a6d8952a92c77aee6730b414778', '0x94d76e24f818426ae84aa404140e8d5f60e10e7e'] - const difficulties = [69762765929000, 70762765929000, 71762765929000] - const difficulty = this.commonContext.consensusType() === ConsensusType.ProofOfStake ? 0 : difficulties[this.blockNumber % difficulties.length] + const coinbases = ['0x0e9281e9c6a0808672eaba6bd1220e144c9bb07a', '0x8945a1288dc78a6d8952a92c77aee6730b414778', '0x94d76e24f818426ae84aa404140e8d5f60e10e7e'] + const difficulties = [69762765929000, 70762765929000, 71762765929000] + const difficulty = this.commonContext.consensusType() === ConsensusType.ProofOfStake ? 0 : difficulties[this.blockNumber % difficulties.length] - const blocknumber = this.blockNumber + 1 - const block = Block.fromBlockData({ - header: { - timestamp: new Date().getTime() / 1000 | 0, - number: blocknumber, - coinbase: coinbases[blocknumber % coinbases.length], - difficulty, - gasLimit, - baseFeePerGas: EIP1559 ? '0x1' : undefined, - parentHash: this.blockParentHash - }, - transactions: [tx] - }, { common: this.commonContext, hardforkByBlockNumber: false, hardforkByTTD: undefined }) + const blocknumber = this.blockNumber + 1 + const block = Block.fromBlockData({ + header: { + timestamp: new Date().getTime() / 1000 | 0, + number: blocknumber, + coinbase: coinbases[blocknumber % coinbases.length], + difficulty, + gasLimit, + baseFeePerGas: EIP1559 ? '0x1' : undefined, + parentHash: this.blockParentHash + }, + transactions: [tx] + }, { common: this.commonContext, hardforkByBlockNumber: false, hardforkByTTD: undefined }) - if (!useCall) { - this.blockNumber = this.blockNumber + 1 - this.blockParentHash = block.hash() - this.runBlockInVm(tx, block, (err, result) => { - if (!err) this.getVMObject().vm.blockchain.putBlock(block) - callback(err, result) - }) - } else { - this.getVMObject().stateManager.checkpoint().then(() => { - this.runBlockInVm(tx, block, (err, result) => { - this.getVMObject().stateManager.revert().then(() => { - callback(err, result) - }) - }) - }) - } - }).catch((e) => { - callback(e) + if (!useCall) { + this.blockNumber = this.blockNumber + 1 + this.blockParentHash = block.hash() + this.runBlockInVm(tx, block, (err, result) => { + if (!err) this.getVMObject().vm.blockchain.putBlock(block) + callback(err, result) }) - } - - runBlockInVm (tx, block, callback) { - this.getVMObject().vm.runBlock({ block: block, generate: true, skipBlockValidation: true, skipBalance: false, skipNonce: true }).then((results: RunBlockResult) => { - const result: RunTxResult = results.results[0] - callback(null, { - result, - transactionHash: bufferToHex(Buffer.from(tx.hash())), - block, - tx + } else { + this.getVMObject().stateManager.checkpoint().then(() => { + this.runBlockInVm(tx, block, (err, result) => { + this.getVMObject().stateManager.revert().then(() => { + callback(err, result) }) - }).catch(function (err) { - callback(err) + }) }) - } + } + }).catch((e) => { + callback(e) + }) + } + + runBlockInVm (tx, block, callback) { + this.getVMObject().vm.runBlock({ block: block, generate: true, skipBlockValidation: true, skipBalance: false, skipNonce: true }).then((results: RunBlockResult) => { + const result: RunTxResult = results.results[0] + callback(null, { + result, + transactionHash: bufferToHex(Buffer.from(tx.hash())), + block, + tx + }) + }).catch(function (err) { + callback(err) + }) + } } diff --git a/libs/remix-lib/src/execution/txRunnerWeb3.ts b/libs/remix-lib/src/execution/txRunnerWeb3.ts index d5fce39b92..d6c0027a9f 100644 --- a/libs/remix-lib/src/execution/txRunnerWeb3.ts +++ b/libs/remix-lib/src/execution/txRunnerWeb3.ts @@ -4,176 +4,176 @@ import type { Transaction as InternalTransaction } from './txRunner' import Web3 from 'web3' export class TxRunnerWeb3 { - event - _api - getWeb3: () => Web3 - currentblockGasLimit: () => number + event + _api + getWeb3: () => Web3 + currentblockGasLimit: () => number - constructor (api, getWeb3, currentblockGasLimit) { - this.event = new EventManager() - this.getWeb3 = getWeb3 - this.currentblockGasLimit = currentblockGasLimit - this._api = api + constructor (api, getWeb3, currentblockGasLimit) { + this.event = new EventManager() + this.getWeb3 = getWeb3 + this.currentblockGasLimit = currentblockGasLimit + this._api = api + } + + _executeTx (tx, network, txFee, api, promptCb, callback) { + if (network && network.lastBlock && network.lastBlock.baseFeePerGas) { + // the sending stack (web3.js / metamask need to have the type defined) + // this is to avoid the following issue: https://github.com/MetaMask/metamask-extension/issues/11824 + tx.type = '0x2' + } + if (txFee) { + if (txFee.baseFeePerGas) { + tx.maxPriorityFeePerGas = this.getWeb3().utils.toHex(this.getWeb3().utils.toWei(txFee.maxPriorityFee, 'gwei')) + tx.maxFeePerGas = this.getWeb3().utils.toHex(this.getWeb3().utils.toWei(txFee.maxFee, 'gwei')) + tx.type = '0x2' + } else { + tx.gasPrice = this.getWeb3().utils.toHex(this.getWeb3().utils.toWei(txFee.gasPrice, 'gwei')) + tx.type = '0x1' + } } - _executeTx (tx, network, txFee, api, promptCb, callback) { - if (network && network.lastBlock && network.lastBlock.baseFeePerGas) { - // the sending stack (web3.js / metamask need to have the type defined) - // this is to avoid the following issue: https://github.com/MetaMask/metamask-extension/issues/11824 - tx.type = '0x2' - } - if (txFee) { - if (txFee.baseFeePerGas) { - tx.maxPriorityFeePerGas = this.getWeb3().utils.toHex(this.getWeb3().utils.toWei(txFee.maxPriorityFee, 'gwei')) - tx.maxFeePerGas = this.getWeb3().utils.toHex(this.getWeb3().utils.toWei(txFee.maxFee, 'gwei')) - tx.type = '0x2' - } else { - tx.gasPrice = this.getWeb3().utils.toHex(this.getWeb3().utils.toWei(txFee.gasPrice, 'gwei')) - tx.type = '0x1' - } + if (api.personalMode()) { + promptCb( + (value) => { + this._sendTransaction((this.getWeb3() as any).personal.sendTransaction, tx, value, callback) + }, + () => { + return callback('Canceled by user.') } + ) + } else { + this._sendTransaction(this.getWeb3().eth.sendTransaction, tx, null, callback) + } + } - if (api.personalMode()) { - promptCb( - (value) => { - this._sendTransaction((this.getWeb3() as any).personal.sendTransaction, tx, value, callback) - }, - () => { - return callback('Canceled by user.') - } - ) - } else { - this._sendTransaction(this.getWeb3().eth.sendTransaction, tx, null, callback) - } + _sendTransaction (sendTx, tx, pass, callback) { + let currentDateTime = new Date(); + const start = currentDateTime.getTime() / 1000 + const cb = (err, resp) => { + if (err) { + return callback(err, resp) + } + this.event.trigger('transactionBroadcasted', [resp]) + const listenOnResponse = () => { + // eslint-disable-next-line no-async-promise-executor + return new Promise(async (resolve, reject) => { + const receipt = await tryTillReceiptAvailable(resp, this.getWeb3()) + tx = await tryTillTxAvailable(resp, this.getWeb3()) + currentDateTime = new Date(); + const end = currentDateTime.getTime() / 1000 + console.log('tx duration', end - start) + resolve({ + receipt, + tx, + transactionHash: receipt ? receipt['transactionHash'] : null + }) + }) + } + listenOnResponse().then((txData) => { callback(null, txData) }).catch((error) => { callback(error) }) } + const args = pass !== null ? [tx, pass, cb] : [tx, cb] + try { + sendTx.apply({}, args) + } catch (e) { + return callback(`Send transaction failed: ${e.message} . if you use an injected provider, please check it is properly unlocked. `) + } + } - _sendTransaction (sendTx, tx, pass, callback) { - let currentDateTime = new Date(); - const start = currentDateTime.getTime() / 1000 - const cb = (err, resp) => { - if (err) { - return callback(err, resp) - } - this.event.trigger('transactionBroadcasted', [resp]) - const listenOnResponse = () => { - // eslint-disable-next-line no-async-promise-executor - return new Promise(async (resolve, reject) => { - const receipt = await tryTillReceiptAvailable(resp, this.getWeb3()) - tx = await tryTillTxAvailable(resp, this.getWeb3()) - currentDateTime = new Date(); - const end = currentDateTime.getTime() / 1000 - console.log('tx duration', end - start) - resolve({ - receipt, - tx, - transactionHash: receipt ? receipt['transactionHash'] : null - }) - }) - } - listenOnResponse().then((txData) => { callback(null, txData) }).catch((error) => { callback(error) }) - } - const args = pass !== null ? [tx, pass, cb] : [tx, cb] - try { - sendTx.apply({}, args) - } catch (e) { - return callback(`Send transaction failed: ${e.message} . if you use an injected provider, please check it is properly unlocked. `) - } + execute (args: InternalTransaction, confirmationCb, gasEstimationForceSend, promptCb, callback) { + let data = args.data + if (data.slice(0, 2) !== '0x') { + data = '0x' + data } - execute (args: InternalTransaction, confirmationCb, gasEstimationForceSend, promptCb, callback) { - let data = args.data - if (data.slice(0, 2) !== '0x') { - data = '0x' + data - } + return this.runInNode(args.from, args.to, data, args.value, args.gasLimit, args.useCall, args.timestamp, confirmationCb, gasEstimationForceSend, promptCb, callback) + } - return this.runInNode(args.from, args.to, data, args.value, args.gasLimit, args.useCall, args.timestamp, confirmationCb, gasEstimationForceSend, promptCb, callback) + runInNode (from, to, data, value, gasLimit, useCall, timestamp, confirmCb, gasEstimationForceSend, promptCb, callback) { + const tx = { from: from, to: to, data: data, value: value } + if (!from) return callback('the value of "from" is not defined. Please make sure an account is selected.') + if (useCall) { + tx['gas'] = gasLimit + if (this._api && this._api.isVM()) tx['timestamp'] = timestamp + return this.getWeb3().eth.call(tx, function (error, result: any) { + if (error) return callback(error) + callback(null, { + result: result + }) + }) } - - runInNode (from, to, data, value, gasLimit, useCall, timestamp, confirmCb, gasEstimationForceSend, promptCb, callback) { - const tx = { from: from, to: to, data: data, value: value } - if (!from) return callback('the value of "from" is not defined. Please make sure an account is selected.') - if (useCall) { - tx['gas'] = gasLimit - if (this._api && this._api.isVM()) tx['timestamp'] = timestamp - return this.getWeb3().eth.call(tx, function (error, result: any) { - if (error) return callback(error) - callback(null, { - result: result - }) - }) + this._api.detectNetwork((errNetWork, network) => { + if (errNetWork) { + console.log(errNetWork) + return + } + const txCopy = { ...tx, type: undefined, maxFeePerGas: undefined, gasPrice: undefined } + if (network && network.lastBlock) { + if (network.lastBlock.baseFeePerGas) { + // the sending stack (web3.js / metamask need to have the type defined) + // this is to avoid the following issue: https://github.com/MetaMask/metamask-extension/issues/11824 + txCopy.type = '0x2' + txCopy.maxFeePerGas = Math.ceil(network.lastBlock.baseFeePerGas + network.lastBlock.baseFeePerGas / 2) + } else { + txCopy.type = '0x1' + txCopy.gasPrice = network.lastBlock.baseFeePerGas } - this._api.detectNetwork((errNetWork, network) => { - if (errNetWork) { - console.log(errNetWork) - return - } - const txCopy = { ...tx, type: undefined, maxFeePerGas: undefined, gasPrice: undefined } - if (network && network.lastBlock) { - if (network.lastBlock.baseFeePerGas) { - // the sending stack (web3.js / metamask need to have the type defined) - // this is to avoid the following issue: https://github.com/MetaMask/metamask-extension/issues/11824 - txCopy.type = '0x2' - txCopy.maxFeePerGas = Math.ceil(network.lastBlock.baseFeePerGas + network.lastBlock.baseFeePerGas / 2) - } else { - txCopy.type = '0x1' - txCopy.gasPrice = network.lastBlock.baseFeePerGas - } - } - this.getWeb3().eth.estimateGas(txCopy, (err, gasEstimation) => { - if (err && err.message.indexOf('Invalid JSON RPC response') !== -1) { - // // @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.')) - } - err = network.name === 'VM' ? null : err // just send the tx if "VM" - gasEstimationForceSend(err, () => { - // callback is called whenever no error - tx['gas'] = !gasEstimation ? gasLimit : gasEstimation + } + this.getWeb3().eth.estimateGas(txCopy, (err, gasEstimation) => { + if (err && err.message.indexOf('Invalid JSON RPC response') !== -1) { + // // @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.')) + } + err = network.name === 'VM' ? null : err // just send the tx if "VM" + gasEstimationForceSend(err, () => { + // callback is called whenever no error + tx['gas'] = !gasEstimation ? gasLimit : gasEstimation - if (this._api.config.getUnpersistedProperty('doNotShowTransactionConfirmationAgain')) { - return this._executeTx(tx, network, null, this._api, promptCb, callback) - } + if (this._api.config.getUnpersistedProperty('doNotShowTransactionConfirmationAgain')) { + return this._executeTx(tx, network, null, this._api, promptCb, callback) + } - confirmCb(network, tx, tx['gas'], (txFee) => { - return this._executeTx(tx, network, txFee, this._api, promptCb, 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) + confirmCb(network, tx, tx['gas'], (txFee) => { + return this._executeTx(tx, network, txFee, this._api, promptCb, 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 + 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) - } - }) - }) + if (gasEstimation > gasLimit) { + return callback('Gas required exceeds limit: ' + gasLimit + '. ' + warnEstimation) + } + if (gasEstimation > blockGasLimit) { + return callback('Gas required exceeds block gas limit: ' + gasLimit + '. ' + warnEstimation) + } }) - } + }) + }) + } } async function tryTillReceiptAvailable (txhash, web3) { - try { - const receipt = await web3.eth.getTransactionReceipt(txhash) - if (receipt) return receipt - } catch (e) {} - await pause() - return await tryTillReceiptAvailable(txhash, web3) + try { + const receipt = await web3.eth.getTransactionReceipt(txhash) + if (receipt) return receipt + } catch (e) {} + await pause() + return await tryTillReceiptAvailable(txhash, web3) } async function tryTillTxAvailable (txhash, web3) { - try { - const tx = await web3.eth.getTransaction(txhash) - if (tx && tx.blockHash) return tx - } catch (e) {} - return await tryTillTxAvailable(txhash, web3) + try { + const tx = await web3.eth.getTransaction(txhash) + if (tx && tx.blockHash) return tx + } catch (e) {} + return await tryTillTxAvailable(txhash, web3) } async function pause () { return new Promise((resolve, reject) => { setTimeout(resolve, 500) }) } diff --git a/libs/remix-lib/src/execution/typeConversion.ts b/libs/remix-lib/src/execution/typeConversion.ts index 46a25bff8a..e43499a677 100644 --- a/libs/remix-lib/src/execution/typeConversion.ts +++ b/libs/remix-lib/src/execution/typeConversion.ts @@ -3,39 +3,39 @@ import { BN } from 'bn.js' import { bufferToHex } from '@ethereumjs/util' export function toInt (h) { - if (h.indexOf && h.indexOf('0x') === 0) { - return (new BN(h.replace('0x', ''), 16)).toString(10) - } else if ((h.constructor && h.constructor.name === 'BigNumber') || BN.isBN(h)) { - return h.toString(10) - } - return h + if (h.indexOf && h.indexOf('0x') === 0) { + return (new BN(h.replace('0x', ''), 16)).toString(10) + } else if ((h.constructor && h.constructor.name === 'BigNumber') || BN.isBN(h)) { + return h.toString(10) + } + return h } export const stringify = convertToString function convertToString (v) { - try { - if (v instanceof Array) { - const ret = [] - for (const k in v) { - ret.push(convertToString(v[k])) - } - return ret - } else if (BN.isBN(v) || (v.constructor && v.constructor.name === 'BigNumber')) { - return v.toString(10) - } else if (v._isBuffer) { - return bufferToHex(v) - } else if (typeof v === 'object') { - const retObject = {} - for (const i in v) { - retObject[i] = convertToString(v[i]) - } - return retObject - } else { - return v - } - } catch (e) { - console.log(e) - return v + try { + if (v instanceof Array) { + const ret = [] + for (const k in v) { + ret.push(convertToString(v[k])) + } + return ret + } else if (BN.isBN(v) || (v.constructor && v.constructor.name === 'BigNumber')) { + return v.toString(10) + } else if (v._isBuffer) { + return bufferToHex(v) + } else if (typeof v === 'object') { + const retObject = {} + for (const i in v) { + retObject[i] = convertToString(v[i]) + } + return retObject + } else { + return v } + } catch (e) { + console.log(e) + return v + } } diff --git a/libs/remix-lib/src/hash.ts b/libs/remix-lib/src/hash.ts index 624154be99..267ff6f204 100644 --- a/libs/remix-lib/src/hash.ts +++ b/libs/remix-lib/src/hash.ts @@ -9,24 +9,24 @@ import { toBuffer, setLengthLeft, isHexString } from '@ethereumjs/util' * @param bits (number = 256) The Keccak width */ export const keccak = function(a: Buffer, bits: number = 256): Buffer { - assertIsBuffer(a) - switch (bits) { - case 224: { - return toBuffer(keccak224(a)) - } - case 256: { - return toBuffer(k256(a)) - } - case 384: { - return toBuffer(keccak384(a)) - } - case 512: { - return toBuffer(keccak512(a)) - } - default: { - throw new Error(`Invald algorithm: keccak${bits}`) - } - } + assertIsBuffer(a) + switch (bits) { + case 224: { + return toBuffer(keccak224(a)) + } + case 256: { + return toBuffer(k256(a)) + } + case 384: { + return toBuffer(keccak384(a)) + } + case 512: { + return toBuffer(keccak512(a)) + } + default: { + throw new Error(`Invald algorithm: keccak${bits}`) + } + } } /** @@ -34,7 +34,7 @@ export const keccak = function(a: Buffer, bits: number = 256): Buffer { * @param a The input data (Buffer) */ export const keccak256 = function(a: Buffer): Buffer { - return keccak(a) + return keccak(a) } /** @@ -43,9 +43,9 @@ export const keccak256 = function(a: Buffer): Buffer { * @param bits (number = 256) The Keccak width */ export const keccakFromString = function(a: string, bits: number = 256) { - assertIsString(a) - const buf = Buffer.from(a, 'utf8') - return keccak(buf, bits) + assertIsString(a) + const buf = Buffer.from(a, 'utf8') + return keccak(buf, bits) } /** @@ -54,8 +54,8 @@ export const keccakFromString = function(a: string, bits: number = 256) { * @param bits (number = 256) The Keccak width */ export const keccakFromHexString = function(a: string, bits: number = 256) { - assertIsHexString(a) - return keccak(toBuffer(a), bits) + assertIsHexString(a) + return keccak(toBuffer(a), bits) } /** @@ -64,8 +64,8 @@ export const keccakFromHexString = function(a: string, bits: number = 256) { * @param bits (number = 256) The Keccak width */ export const keccakFromArray = function(a: number[], bits: number = 256) { - assertIsArray(a) - return keccak(toBuffer(a), bits) + assertIsArray(a) + return keccak(toBuffer(a), bits) } /** @@ -73,10 +73,10 @@ export const keccakFromArray = function(a: number[], bits: number = 256) { * @param a The input data (Buffer|Array|String) */ const _sha256 = function(a: any): Buffer { - a = toBuffer(a) - return createHash('sha256') - .update(a) - .digest() + a = toBuffer(a) + return createHash('sha256') + .update(a) + .digest() } /** @@ -84,8 +84,8 @@ const _sha256 = function(a: any): Buffer { * @param a The input data (Buffer) */ export const sha256 = function(a: Buffer): Buffer { - assertIsBuffer(a) - return _sha256(a) + assertIsBuffer(a) + return _sha256(a) } /** @@ -93,8 +93,8 @@ export const sha256 = function(a: Buffer): Buffer { * @param a The input data (string) */ export const sha256FromString = function(a: string): Buffer { - assertIsString(a) - return _sha256(a) + assertIsString(a) + return _sha256(a) } /** @@ -102,8 +102,8 @@ export const sha256FromString = function(a: string): Buffer { * @param a The input data (number[]) */ export const sha256FromArray = function(a: number[]): Buffer { - assertIsArray(a) - return _sha256(a) + assertIsArray(a) + return _sha256(a) } /** @@ -112,15 +112,15 @@ export const sha256FromArray = function(a: number[]): Buffer { * @param padded Whether it should be padded to 256 bits or not */ const _ripemd160 = function(a: any, padded: boolean): Buffer { - a = toBuffer(a) - const hash = createHash('rmd160') - .update(a) - .digest() - if (padded === true) { - return setLengthLeft(hash, 32) - } else { - return hash - } + a = toBuffer(a) + const hash = createHash('rmd160') + .update(a) + .digest() + if (padded === true) { + return setLengthLeft(hash, 32) + } else { + return hash + } } /** @@ -129,8 +129,8 @@ const _ripemd160 = function(a: any, padded: boolean): Buffer { * @param padded Whether it should be padded to 256 bits or not */ export const ripemd160 = function(a: Buffer, padded: boolean): Buffer { - assertIsBuffer(a) - return _ripemd160(a, padded) + assertIsBuffer(a) + return _ripemd160(a, padded) } /** @@ -139,8 +139,8 @@ export const ripemd160 = function(a: Buffer, padded: boolean): Buffer { * @param padded Whether it should be padded to 256 bits or not */ export const ripemd160FromString = function(a: string, padded: boolean): Buffer { - assertIsString(a) - return _ripemd160(a, padded) + assertIsString(a) + return _ripemd160(a, padded) } /** @@ -149,8 +149,8 @@ export const ripemd160FromString = function(a: string, padded: boolean): Buffer * @param padded Whether it should be padded to 256 bits or not */ export const ripemd160FromArray = function(a: number[], padded: boolean): Buffer { - assertIsArray(a) - return _ripemd160(a, padded) + assertIsArray(a) + return _ripemd160(a, padded) } /** @@ -158,7 +158,7 @@ export const ripemd160FromArray = function(a: number[], padded: boolean): Buffer * @param a The input data */ export const rlphash = function(a: Input): Buffer { - return keccak(encode(a)) + return keccak(encode(a)) } /** @@ -166,10 +166,10 @@ export const rlphash = function(a: Input): Buffer { * @param {string} input string to check hex prefix of */ export const assertIsHexString = function(input: string): void { - if (!isHexString(input)) { - const msg = `This method only supports 0x-prefixed hex strings but input was: ${input}` - throw new Error(msg) - } + if (!isHexString(input)) { + const msg = `This method only supports 0x-prefixed hex strings but input was: ${input}` + throw new Error(msg) + } } /** @@ -177,10 +177,10 @@ export const assertIsHexString = function(input: string): void { * @param {Buffer} input value to check */ export const assertIsBuffer = function(input: Buffer): void { - if (!Buffer.isBuffer(input)) { - const msg = `This method only supports Buffer but input was: ${input}` - throw new Error(msg) - } + if (!Buffer.isBuffer(input)) { + const msg = `This method only supports Buffer but input was: ${input}` + throw new Error(msg) + } } /** @@ -188,10 +188,10 @@ export const assertIsBuffer = function(input: Buffer): void { * @param {number[]} input value to check */ export const assertIsArray = function(input: number[]): void { - if (!Array.isArray(input)) { - const msg = `This method only supports number arrays but input was: ${input}` - throw new Error(msg) - } + if (!Array.isArray(input)) { + const msg = `This method only supports number arrays but input was: ${input}` + throw new Error(msg) + } } /** @@ -199,8 +199,8 @@ export const assertIsArray = function(input: number[]): void { * @param {string} input value to check */ export const assertIsString = function(input: string): void { - if (typeof input !== 'string') { - const msg = `This method only supports strings but input was: ${input}` - throw new Error(msg) - } + if (typeof input !== 'string') { + const msg = `This method only supports strings but input was: ${input}` + throw new Error(msg) + } } \ No newline at end of file diff --git a/libs/remix-lib/src/helpers/compilerHelper.ts b/libs/remix-lib/src/helpers/compilerHelper.ts index 1681520c95..501b829fb2 100644 --- a/libs/remix-lib/src/helpers/compilerHelper.ts +++ b/libs/remix-lib/src/helpers/compilerHelper.ts @@ -1,22 +1,22 @@ export function compilerInput (contracts) { - return JSON.stringify({ - language: 'Solidity', - sources: { - 'test.sol': { - content: contracts - } - }, - settings: { - optimizer: { - enabled: false, - runs: 200 - }, - outputSelection: { - '*': { - '': ['ast'], - '*': ['abi', 'metadata', 'evm.legacyAssembly', 'evm.bytecode', 'evm.deployedBytecode', 'evm.methodIdentifiers', 'evm.gasEstimates'] - } - } + return JSON.stringify({ + language: 'Solidity', + sources: { + 'test.sol': { + content: contracts + } + }, + settings: { + optimizer: { + enabled: false, + runs: 200 + }, + outputSelection: { + '*': { + '': ['ast'], + '*': ['abi', 'metadata', 'evm.legacyAssembly', 'evm.bytecode', 'evm.deployedBytecode', 'evm.methodIdentifiers', 'evm.gasEstimates'] } - }) + } + } + }) } diff --git a/libs/remix-lib/src/helpers/hhconsoleSigs.ts b/libs/remix-lib/src/helpers/hhconsoleSigs.ts index 89ef7afb56..400182b345 100644 --- a/libs/remix-lib/src/helpers/hhconsoleSigs.ts +++ b/libs/remix-lib/src/helpers/hhconsoleSigs.ts @@ -1,601 +1,601 @@ // Fetched from https://github.com/nomiclabs/hardhat/blob/ee4969a0a8f746f4775d4018326056d161066869/packages/hardhat-core/src/internal/hardhat-network/stack-traces/logger.ts#L47 export const ConsoleLogs = { - // Legacy method signatures before this PR: https://github.com/NomicFoundation/hardhat/pull/2964 - 1368866505: '()', - 1309416733: '(int)', - 4122065833: '(uint)', - 1093685164: '(string)', - 843419373: '(bool)', - 741264322: '(address)', - 199720790: '(bytes)', - 1847107880: '(bytes1)', - 3921027734: '(bytes2)', - 763578662: '(bytes3)', - 3764340945: '(bytes4)', - 2793701517: '(bytes5)', - 2927928721: '(bytes6)', - 1322614312: '(bytes7)', - 1334060334: '(bytes8)', - 2428341456: '(bytes9)', - 20780939: '(bytes10)', - 67127854: '(bytes11)', - 2258660029: '(bytes12)', - 2488442420: '(bytes13)', - 2456219775: '(bytes14)', - 3667227872: '(bytes15)', - 1717330180: '(bytes16)', - 866084666: '(bytes17)', - 3302112666: '(bytes18)', - 1584093747: '(bytes19)', - 1367925737: '(bytes20)', - 3923391840: '(bytes21)', - 3589990556: '(bytes22)', - 2879508237: '(bytes23)', - 4055063348: '(bytes24)', - 193248344: '(bytes25)', - 4172368369: '(bytes26)', - 976705501: '(bytes27)', - 3358255854: '(bytes28)', - 1265222613: '(bytes29)', - 3994207469: '(bytes30)', - 3263516050: '(bytes31)', - 666357637: '(bytes32)', - 1812949376: '(uint,uint)', - 262402885: '(uint,string)', - 510514412: '(uint,bool)', - 1491830284: '(uint,address)', - 2534451664: '(string,uint)', - 1264337527: '(string,string)', - 3283441205: '(string,bool)', - 832238387: '(string,address)', - 910912146: '(bool,uint)', - 2414527781: '(bool,string)', - 705760899: '(bool,bool)', - 2235320393: '(bool,address)', - 574869411: '(address,uint)', - 1973388987: '(address,string)', - 1974863315: '(address,bool)', - 3673216170: '(address,address)', - 3884059252: '(uint,uint,uint)', - 2104037094: '(uint,uint,string)', - 1733758967: '(uint,uint,bool)', - 3191032091: '(uint,uint,address)', - 1533929535: '(uint,string,uint)', - 1062716053: '(uint,string,string)', - 1185403086: '(uint,string,bool)', - 529592906: '(uint,string,address)', - 1515034914: '(uint,bool,uint)', - 2332955902: '(uint,bool,string)', - 3587091680: '(uint,bool,bool)', - 1112473535: '(uint,bool,address)', - 2286109610: '(uint,address,uint)', - 3464692859: '(uint,address,string)', - 2060456590: '(uint,address,bool)', - 2104993307: '(uint,address,address)', - 2526862595: '(string,uint,uint)', - 2750793529: '(string,uint,string)', - 4043501061: '(string,uint,bool)', - 3817119609: '(string,uint,address)', - 4083337817: '(string,string,uint)', - 753761519: '(string,string,string)', - 2967534005: '(string,string,bool)', - 2515337621: '(string,string,address)', - 689682896: '(string,bool,uint)', - 3801674877: '(string,bool,string)', - 2232122070: '(string,bool,bool)', - 2469116728: '(string,bool,address)', - 130552343: '(string,address,uint)', - 3773410639: '(string,address,string)', - 3374145236: '(string,address,bool)', - 4243355104: '(string,address,address)', - 995886048: '(bool,uint,uint)', - 3359211184: '(bool,uint,string)', - 464374251: '(bool,uint,bool)', - 3302110471: '(bool,uint,address)', - 3224906412: '(bool,string,uint)', - 2960557183: '(bool,string,string)', - 3686056519: '(bool,string,bool)', - 2509355347: '(bool,string,address)', - 2954061243: '(bool,bool,uint)', - 626391622: '(bool,bool,string)', - 1349555864: '(bool,bool,bool)', - 276362893: '(bool,bool,address)', - 3950005167: '(bool,address,uint)', - 3734671984: '(bool,address,string)', - 415876934: '(bool,address,bool)', - 3530962535: '(bool,address,address)', - 2273710942: '(address,uint,uint)', - 3136907337: '(address,uint,string)', - 3846889796: '(address,uint,bool)', - 2548867988: '(address,uint,address)', - 484110986: '(address,string,uint)', - 4218888805: '(address,string,string)', - 3473018801: '(address,string,bool)', - 4035396840: '(address,string,address)', - 742821141: '(address,bool,uint)', - 555898316: '(address,bool,string)', - 3951234194: '(address,bool,bool)', - 4044790253: '(address,bool,address)', - 1815506290: '(address,address,uint)', - 7426238: '(address,address,string)', - 4070990470: '(address,address,bool)', - 25986242: '(address,address,address)', - 1554033982: '(uint,uint,uint,uint)', - 2024634892: '(uint,uint,uint,string)', - 1683143115: '(uint,uint,uint,bool)', - 3766828905: '(uint,uint,uint,address)', - 949229117: '(uint,uint,string,uint)', - 2080582194: '(uint,uint,string,string)', - 2989403910: '(uint,uint,string,bool)', - 1127384482: '(uint,uint,string,address)', - 1818524812: '(uint,uint,bool,uint)', - 4024028142: '(uint,uint,bool,string)', - 2495495089: '(uint,uint,bool,bool)', - 3776410703: '(uint,uint,bool,address)', - 1628154048: '(uint,uint,address,uint)', - 3600994782: '(uint,uint,address,string)', - 2833785006: '(uint,uint,address,bool)', - 3398671136: '(uint,uint,address,address)', - 3221501959: '(uint,string,uint,uint)', - 2730232985: '(uint,string,uint,string)', - 2270850606: '(uint,string,uint,bool)', - 2877020669: '(uint,string,uint,address)', - 1995203422: '(uint,string,string,uint)', - 1474103825: '(uint,string,string,string)', - 310782872: '(uint,string,string,bool)', - 3432549024: '(uint,string,string,address)', - 2763295359: '(uint,string,bool,uint)', - 2370346144: '(uint,string,bool,string)', - 1371286465: '(uint,string,bool,bool)', - 2037328032: '(uint,string,bool,address)', - 2565338099: '(uint,string,address,uint)', - 4170733439: '(uint,string,address,string)', - 4181720887: '(uint,string,address,bool)', - 2141537675: '(uint,string,address,address)', - 1451396516: '(uint,bool,uint,uint)', - 3906845782: '(uint,bool,uint,string)', - 3534472445: '(uint,bool,uint,bool)', - 1329595790: '(uint,bool,uint,address)', - 2438978344: '(uint,bool,string,uint)', - 2754870525: '(uint,bool,string,string)', - 879671495: '(uint,bool,string,bool)', - 1231956916: '(uint,bool,string,address)', - 3173363033: '(uint,bool,bool,uint)', - 831186331: '(uint,bool,bool,string)', - 1315722005: '(uint,bool,bool,bool)', - 1392910941: '(uint,bool,bool,address)', - 1102442299: '(uint,bool,address,uint)', - 2721084958: '(uint,bool,address,string)', - 2449150530: '(uint,bool,address,bool)', - 2263728396: '(uint,bool,address,address)', - 3399106228: '(uint,address,uint,uint)', - 1054063912: '(uint,address,uint,string)', - 435581801: '(uint,address,uint,bool)', - 4256361684: '(uint,address,uint,address)', - 2697204968: '(uint,address,string,uint)', - 2373420580: '(uint,address,string,string)', - 581204390: '(uint,address,string,bool)', - 3420819197: '(uint,address,string,address)', - 2064181483: '(uint,address,bool,uint)', - 1676730946: '(uint,address,bool,string)', - 2116501773: '(uint,address,bool,bool)', - 3056677012: '(uint,address,bool,address)', - 2587672470: '(uint,address,address,uint)', - 2034490470: '(uint,address,address,string)', - 22350596: '(uint,address,address,bool)', - 1430734329: '(uint,address,address,address)', - 149837414: '(string,uint,uint,uint)', - 2773406909: '(string,uint,uint,string)', - 4147936829: '(string,uint,uint,bool)', - 3201771711: '(string,uint,uint,address)', - 2697245221: '(string,uint,string,uint)', - 1821956834: '(string,uint,string,string)', - 3919545039: '(string,uint,string,bool)', - 3144824297: '(string,uint,string,address)', - 1427009269: '(string,uint,bool,uint)', - 1993105508: '(string,uint,bool,string)', - 3816813520: '(string,uint,bool,bool)', - 3847527825: '(string,uint,bool,address)', - 1481210622: '(string,uint,address,uint)', - 844415720: '(string,uint,address,string)', - 285649143: '(string,uint,address,bool)', - 3939013249: '(string,uint,address,address)', - 3587119056: '(string,string,uint,uint)', - 2366909661: '(string,string,uint,string)', - 3864418506: '(string,string,uint,bool)', - 1565476480: '(string,string,uint,address)', - 2681211381: '(string,string,string,uint)', - 3731419658: '(string,string,string,string)', - 739726573: '(string,string,string,bool)', - 1834430276: '(string,string,string,address)', - 2256636538: '(string,string,bool,uint)', - 1585754346: '(string,string,bool,string)', - 1081628777: '(string,string,bool,bool)', - 3279013851: '(string,string,bool,address)', - 1250010474: '(string,string,address,uint)', - 3944480640: '(string,string,address,string)', - 1556958775: '(string,string,address,bool)', - 1134328815: '(string,string,address,address)', - 1572859960: '(string,bool,uint,uint)', - 1119461927: '(string,bool,uint,string)', - 1019590099: '(string,bool,uint,bool)', - 1909687565: '(string,bool,uint,address)', - 885731469: '(string,bool,string,uint)', - 2821114603: '(string,bool,string,string)', - 1066037277: '(string,bool,string,bool)', - 3764542249: '(string,bool,string,address)', - 2155164136: '(string,bool,bool,uint)', - 2636305885: '(string,bool,bool,string)', - 2304440517: '(string,bool,bool,bool)', - 1905304873: '(string,bool,bool,address)', - 685723286: '(string,bool,address,uint)', - 764294052: '(string,bool,address,string)', - 2508990662: '(string,bool,address,bool)', - 870964509: '(string,bool,address,address)', - 3668153533: '(string,address,uint,uint)', - 1280700980: '(string,address,uint,string)', - 1522647356: '(string,address,uint,bool)', - 2741431424: '(string,address,uint,address)', - 2405583849: '(string,address,string,uint)', - 609847026: '(string,address,string,string)', - 1595265676: '(string,address,string,bool)', - 2864486961: '(string,address,string,address)', - 3318856587: '(string,address,bool,uint)', - 72663161: '(string,address,bool,string)', - 2038975531: '(string,address,bool,bool)', - 573965245: '(string,address,bool,address)', - 1857524797: '(string,address,address,uint)', - 2148146279: '(string,address,address,string)', - 3047013728: '(string,address,address,bool)', - 3985582326: '(string,address,address,address)', - 853517604: '(bool,uint,uint,uint)', - 3657852616: '(bool,uint,uint,string)', - 2753397214: '(bool,uint,uint,bool)', - 4049711649: '(bool,uint,uint,address)', - 1098907931: '(bool,uint,string,uint)', - 3542771016: '(bool,uint,string,string)', - 2446522387: '(bool,uint,string,bool)', - 2781285673: '(bool,uint,string,address)', - 3554563475: '(bool,uint,bool,uint)', - 3067439572: '(bool,uint,bool,string)', - 2650928961: '(bool,uint,bool,bool)', - 1114097656: '(bool,uint,bool,address)', - 3399820138: '(bool,uint,address,uint)', - 403247937: '(bool,uint,address,string)', - 1705899016: '(bool,uint,address,bool)', - 2318373034: '(bool,uint,address,address)', - 2387273838: '(bool,string,uint,uint)', - 2007084013: '(bool,string,uint,string)', - 549177775: '(bool,string,uint,bool)', - 1529002296: '(bool,string,uint,address)', - 1574643090: '(bool,string,string,uint)', - 392356650: '(bool,string,string,string)', - 508266469: '(bool,string,string,bool)', - 2547225816: '(bool,string,string,address)', - 2372902053: '(bool,string,bool,uint)', - 1211958294: '(bool,string,bool,string)', - 3697185627: '(bool,string,bool,bool)', - 1401816747: '(bool,string,bool,address)', - 453743963: '(bool,string,address,uint)', - 316065672: '(bool,string,address,string)', - 1842623690: '(bool,string,address,bool)', - 724244700: '(bool,string,address,address)', - 1181212302: '(bool,bool,uint,uint)', - 1348569399: '(bool,bool,uint,string)', - 2874982852: '(bool,bool,uint,bool)', - 201299213: '(bool,bool,uint,address)', - 395003525: '(bool,bool,string,uint)', - 1830717265: '(bool,bool,string,string)', - 3092715066: '(bool,bool,string,bool)', - 4188875657: '(bool,bool,string,address)', - 3259532109: '(bool,bool,bool,uint)', - 719587540: '(bool,bool,bool,string)', - 992632032: '(bool,bool,bool,bool)', - 2352126746: '(bool,bool,bool,address)', - 1620281063: '(bool,bool,address,uint)', - 2695133539: '(bool,bool,address,string)', - 3231908568: '(bool,bool,address,bool)', - 4102557348: '(bool,bool,address,address)', - 2617143996: '(bool,address,uint,uint)', - 2691192883: '(bool,address,uint,string)', - 4002252402: '(bool,address,uint,bool)', - 1760647349: '(bool,address,uint,address)', - 194640930: '(bool,address,string,uint)', - 2805734838: '(bool,address,string,string)', - 3804222987: '(bool,address,string,bool)', - 1870422078: '(bool,address,string,address)', - 1287000017: '(bool,address,bool,uint)', - 1248250676: '(bool,address,bool,string)', - 1788626827: '(bool,address,bool,bool)', - 474063670: '(bool,address,bool,address)', - 1384430956: '(bool,address,address,uint)', - 3625099623: '(bool,address,address,string)', - 1180699616: '(bool,address,address,bool)', - 487903233: '(bool,address,address,address)', - 1024368100: '(address,uint,uint,uint)', - 2301889963: '(address,uint,uint,string)', - 3964381346: '(address,uint,uint,bool)', - 519451700: '(address,uint,uint,address)', - 4111650715: '(address,uint,string,uint)', - 2119616147: '(address,uint,string,string)', - 2751614737: '(address,uint,string,bool)', - 3698927108: '(address,uint,string,address)', - 1770996626: '(address,uint,bool,uint)', - 2391690869: '(address,uint,bool,string)', - 4272018778: '(address,uint,bool,bool)', - 602229106: '(address,uint,bool,address)', - 2782496616: '(address,uint,address,uint)', - 1567749022: '(address,uint,address,string)', - 4051804649: '(address,uint,address,bool)', - 3961816175: '(address,uint,address,address)', - 2764647008: '(address,string,uint,uint)', - 1561552329: '(address,string,uint,string)', - 2116357467: '(address,string,uint,bool)', - 3755464715: '(address,string,uint,address)', - 2706362425: '(address,string,string,uint)', - 1560462603: '(address,string,string,string)', - 900007711: '(address,string,string,bool)', - 2689478535: '(address,string,string,address)', - 3877655068: '(address,string,bool,uint)', - 3154862590: '(address,string,bool,string)', - 1595759775: '(address,string,bool,bool)', - 542667202: '(address,string,bool,address)', - 2350461865: '(address,string,address,uint)', - 4158874181: '(address,string,address,string)', - 233909110: '(address,string,address,bool)', - 221706784: '(address,string,address,address)', - 3255869470: '(address,bool,uint,uint)', - 2606272204: '(address,bool,uint,string)', - 2244855215: '(address,bool,uint,bool)', - 227337758: '(address,bool,uint,address)', - 2652011374: '(address,bool,string,uint)', - 1197235251: '(address,bool,string,string)', - 1353532957: '(address,bool,string,bool)', - 436029782: '(address,bool,string,address)', - 3484780374: '(address,bool,bool,uint)', - 3754205928: '(address,bool,bool,string)', - 3401856121: '(address,bool,bool,bool)', - 3476636805: '(address,bool,bool,address)', - 3698398930: '(address,bool,address,uint)', - 769095910: '(address,bool,address,string)', - 2801077007: '(address,bool,address,bool)', - 1711502813: '(address,bool,address,address)', - 1425929188: '(address,address,uint,uint)', - 2647731885: '(address,address,uint,string)', - 3270936812: '(address,address,uint,bool)', - 3603321462: '(address,address,uint,address)', - 69767936: '(address,address,string,uint)', - 566079269: '(address,address,string,string)', - 1863997774: '(address,address,string,bool)', - 2406706454: '(address,address,string,address)', - 2513854225: '(address,address,bool,uint)', - 2858762440: '(address,address,bool,string)', - 752096074: '(address,address,bool,bool)', - 2669396846: '(address,address,bool,address)', - 3982404743: '(address,address,address,uint)', - 4161329696: '(address,address,address,string)', - 238520724: '(address,address,address,bool)', - 1717301556: '(address,address,address,address)', - 4133908826: '(uint,uint)', - 3054400204: '(string,uint)', + // Legacy method signatures before this PR: https://github.com/NomicFoundation/hardhat/pull/2964 + 1368866505: '()', + 1309416733: '(int)', + 4122065833: '(uint)', + 1093685164: '(string)', + 843419373: '(bool)', + 741264322: '(address)', + 199720790: '(bytes)', + 1847107880: '(bytes1)', + 3921027734: '(bytes2)', + 763578662: '(bytes3)', + 3764340945: '(bytes4)', + 2793701517: '(bytes5)', + 2927928721: '(bytes6)', + 1322614312: '(bytes7)', + 1334060334: '(bytes8)', + 2428341456: '(bytes9)', + 20780939: '(bytes10)', + 67127854: '(bytes11)', + 2258660029: '(bytes12)', + 2488442420: '(bytes13)', + 2456219775: '(bytes14)', + 3667227872: '(bytes15)', + 1717330180: '(bytes16)', + 866084666: '(bytes17)', + 3302112666: '(bytes18)', + 1584093747: '(bytes19)', + 1367925737: '(bytes20)', + 3923391840: '(bytes21)', + 3589990556: '(bytes22)', + 2879508237: '(bytes23)', + 4055063348: '(bytes24)', + 193248344: '(bytes25)', + 4172368369: '(bytes26)', + 976705501: '(bytes27)', + 3358255854: '(bytes28)', + 1265222613: '(bytes29)', + 3994207469: '(bytes30)', + 3263516050: '(bytes31)', + 666357637: '(bytes32)', + 1812949376: '(uint,uint)', + 262402885: '(uint,string)', + 510514412: '(uint,bool)', + 1491830284: '(uint,address)', + 2534451664: '(string,uint)', + 1264337527: '(string,string)', + 3283441205: '(string,bool)', + 832238387: '(string,address)', + 910912146: '(bool,uint)', + 2414527781: '(bool,string)', + 705760899: '(bool,bool)', + 2235320393: '(bool,address)', + 574869411: '(address,uint)', + 1973388987: '(address,string)', + 1974863315: '(address,bool)', + 3673216170: '(address,address)', + 3884059252: '(uint,uint,uint)', + 2104037094: '(uint,uint,string)', + 1733758967: '(uint,uint,bool)', + 3191032091: '(uint,uint,address)', + 1533929535: '(uint,string,uint)', + 1062716053: '(uint,string,string)', + 1185403086: '(uint,string,bool)', + 529592906: '(uint,string,address)', + 1515034914: '(uint,bool,uint)', + 2332955902: '(uint,bool,string)', + 3587091680: '(uint,bool,bool)', + 1112473535: '(uint,bool,address)', + 2286109610: '(uint,address,uint)', + 3464692859: '(uint,address,string)', + 2060456590: '(uint,address,bool)', + 2104993307: '(uint,address,address)', + 2526862595: '(string,uint,uint)', + 2750793529: '(string,uint,string)', + 4043501061: '(string,uint,bool)', + 3817119609: '(string,uint,address)', + 4083337817: '(string,string,uint)', + 753761519: '(string,string,string)', + 2967534005: '(string,string,bool)', + 2515337621: '(string,string,address)', + 689682896: '(string,bool,uint)', + 3801674877: '(string,bool,string)', + 2232122070: '(string,bool,bool)', + 2469116728: '(string,bool,address)', + 130552343: '(string,address,uint)', + 3773410639: '(string,address,string)', + 3374145236: '(string,address,bool)', + 4243355104: '(string,address,address)', + 995886048: '(bool,uint,uint)', + 3359211184: '(bool,uint,string)', + 464374251: '(bool,uint,bool)', + 3302110471: '(bool,uint,address)', + 3224906412: '(bool,string,uint)', + 2960557183: '(bool,string,string)', + 3686056519: '(bool,string,bool)', + 2509355347: '(bool,string,address)', + 2954061243: '(bool,bool,uint)', + 626391622: '(bool,bool,string)', + 1349555864: '(bool,bool,bool)', + 276362893: '(bool,bool,address)', + 3950005167: '(bool,address,uint)', + 3734671984: '(bool,address,string)', + 415876934: '(bool,address,bool)', + 3530962535: '(bool,address,address)', + 2273710942: '(address,uint,uint)', + 3136907337: '(address,uint,string)', + 3846889796: '(address,uint,bool)', + 2548867988: '(address,uint,address)', + 484110986: '(address,string,uint)', + 4218888805: '(address,string,string)', + 3473018801: '(address,string,bool)', + 4035396840: '(address,string,address)', + 742821141: '(address,bool,uint)', + 555898316: '(address,bool,string)', + 3951234194: '(address,bool,bool)', + 4044790253: '(address,bool,address)', + 1815506290: '(address,address,uint)', + 7426238: '(address,address,string)', + 4070990470: '(address,address,bool)', + 25986242: '(address,address,address)', + 1554033982: '(uint,uint,uint,uint)', + 2024634892: '(uint,uint,uint,string)', + 1683143115: '(uint,uint,uint,bool)', + 3766828905: '(uint,uint,uint,address)', + 949229117: '(uint,uint,string,uint)', + 2080582194: '(uint,uint,string,string)', + 2989403910: '(uint,uint,string,bool)', + 1127384482: '(uint,uint,string,address)', + 1818524812: '(uint,uint,bool,uint)', + 4024028142: '(uint,uint,bool,string)', + 2495495089: '(uint,uint,bool,bool)', + 3776410703: '(uint,uint,bool,address)', + 1628154048: '(uint,uint,address,uint)', + 3600994782: '(uint,uint,address,string)', + 2833785006: '(uint,uint,address,bool)', + 3398671136: '(uint,uint,address,address)', + 3221501959: '(uint,string,uint,uint)', + 2730232985: '(uint,string,uint,string)', + 2270850606: '(uint,string,uint,bool)', + 2877020669: '(uint,string,uint,address)', + 1995203422: '(uint,string,string,uint)', + 1474103825: '(uint,string,string,string)', + 310782872: '(uint,string,string,bool)', + 3432549024: '(uint,string,string,address)', + 2763295359: '(uint,string,bool,uint)', + 2370346144: '(uint,string,bool,string)', + 1371286465: '(uint,string,bool,bool)', + 2037328032: '(uint,string,bool,address)', + 2565338099: '(uint,string,address,uint)', + 4170733439: '(uint,string,address,string)', + 4181720887: '(uint,string,address,bool)', + 2141537675: '(uint,string,address,address)', + 1451396516: '(uint,bool,uint,uint)', + 3906845782: '(uint,bool,uint,string)', + 3534472445: '(uint,bool,uint,bool)', + 1329595790: '(uint,bool,uint,address)', + 2438978344: '(uint,bool,string,uint)', + 2754870525: '(uint,bool,string,string)', + 879671495: '(uint,bool,string,bool)', + 1231956916: '(uint,bool,string,address)', + 3173363033: '(uint,bool,bool,uint)', + 831186331: '(uint,bool,bool,string)', + 1315722005: '(uint,bool,bool,bool)', + 1392910941: '(uint,bool,bool,address)', + 1102442299: '(uint,bool,address,uint)', + 2721084958: '(uint,bool,address,string)', + 2449150530: '(uint,bool,address,bool)', + 2263728396: '(uint,bool,address,address)', + 3399106228: '(uint,address,uint,uint)', + 1054063912: '(uint,address,uint,string)', + 435581801: '(uint,address,uint,bool)', + 4256361684: '(uint,address,uint,address)', + 2697204968: '(uint,address,string,uint)', + 2373420580: '(uint,address,string,string)', + 581204390: '(uint,address,string,bool)', + 3420819197: '(uint,address,string,address)', + 2064181483: '(uint,address,bool,uint)', + 1676730946: '(uint,address,bool,string)', + 2116501773: '(uint,address,bool,bool)', + 3056677012: '(uint,address,bool,address)', + 2587672470: '(uint,address,address,uint)', + 2034490470: '(uint,address,address,string)', + 22350596: '(uint,address,address,bool)', + 1430734329: '(uint,address,address,address)', + 149837414: '(string,uint,uint,uint)', + 2773406909: '(string,uint,uint,string)', + 4147936829: '(string,uint,uint,bool)', + 3201771711: '(string,uint,uint,address)', + 2697245221: '(string,uint,string,uint)', + 1821956834: '(string,uint,string,string)', + 3919545039: '(string,uint,string,bool)', + 3144824297: '(string,uint,string,address)', + 1427009269: '(string,uint,bool,uint)', + 1993105508: '(string,uint,bool,string)', + 3816813520: '(string,uint,bool,bool)', + 3847527825: '(string,uint,bool,address)', + 1481210622: '(string,uint,address,uint)', + 844415720: '(string,uint,address,string)', + 285649143: '(string,uint,address,bool)', + 3939013249: '(string,uint,address,address)', + 3587119056: '(string,string,uint,uint)', + 2366909661: '(string,string,uint,string)', + 3864418506: '(string,string,uint,bool)', + 1565476480: '(string,string,uint,address)', + 2681211381: '(string,string,string,uint)', + 3731419658: '(string,string,string,string)', + 739726573: '(string,string,string,bool)', + 1834430276: '(string,string,string,address)', + 2256636538: '(string,string,bool,uint)', + 1585754346: '(string,string,bool,string)', + 1081628777: '(string,string,bool,bool)', + 3279013851: '(string,string,bool,address)', + 1250010474: '(string,string,address,uint)', + 3944480640: '(string,string,address,string)', + 1556958775: '(string,string,address,bool)', + 1134328815: '(string,string,address,address)', + 1572859960: '(string,bool,uint,uint)', + 1119461927: '(string,bool,uint,string)', + 1019590099: '(string,bool,uint,bool)', + 1909687565: '(string,bool,uint,address)', + 885731469: '(string,bool,string,uint)', + 2821114603: '(string,bool,string,string)', + 1066037277: '(string,bool,string,bool)', + 3764542249: '(string,bool,string,address)', + 2155164136: '(string,bool,bool,uint)', + 2636305885: '(string,bool,bool,string)', + 2304440517: '(string,bool,bool,bool)', + 1905304873: '(string,bool,bool,address)', + 685723286: '(string,bool,address,uint)', + 764294052: '(string,bool,address,string)', + 2508990662: '(string,bool,address,bool)', + 870964509: '(string,bool,address,address)', + 3668153533: '(string,address,uint,uint)', + 1280700980: '(string,address,uint,string)', + 1522647356: '(string,address,uint,bool)', + 2741431424: '(string,address,uint,address)', + 2405583849: '(string,address,string,uint)', + 609847026: '(string,address,string,string)', + 1595265676: '(string,address,string,bool)', + 2864486961: '(string,address,string,address)', + 3318856587: '(string,address,bool,uint)', + 72663161: '(string,address,bool,string)', + 2038975531: '(string,address,bool,bool)', + 573965245: '(string,address,bool,address)', + 1857524797: '(string,address,address,uint)', + 2148146279: '(string,address,address,string)', + 3047013728: '(string,address,address,bool)', + 3985582326: '(string,address,address,address)', + 853517604: '(bool,uint,uint,uint)', + 3657852616: '(bool,uint,uint,string)', + 2753397214: '(bool,uint,uint,bool)', + 4049711649: '(bool,uint,uint,address)', + 1098907931: '(bool,uint,string,uint)', + 3542771016: '(bool,uint,string,string)', + 2446522387: '(bool,uint,string,bool)', + 2781285673: '(bool,uint,string,address)', + 3554563475: '(bool,uint,bool,uint)', + 3067439572: '(bool,uint,bool,string)', + 2650928961: '(bool,uint,bool,bool)', + 1114097656: '(bool,uint,bool,address)', + 3399820138: '(bool,uint,address,uint)', + 403247937: '(bool,uint,address,string)', + 1705899016: '(bool,uint,address,bool)', + 2318373034: '(bool,uint,address,address)', + 2387273838: '(bool,string,uint,uint)', + 2007084013: '(bool,string,uint,string)', + 549177775: '(bool,string,uint,bool)', + 1529002296: '(bool,string,uint,address)', + 1574643090: '(bool,string,string,uint)', + 392356650: '(bool,string,string,string)', + 508266469: '(bool,string,string,bool)', + 2547225816: '(bool,string,string,address)', + 2372902053: '(bool,string,bool,uint)', + 1211958294: '(bool,string,bool,string)', + 3697185627: '(bool,string,bool,bool)', + 1401816747: '(bool,string,bool,address)', + 453743963: '(bool,string,address,uint)', + 316065672: '(bool,string,address,string)', + 1842623690: '(bool,string,address,bool)', + 724244700: '(bool,string,address,address)', + 1181212302: '(bool,bool,uint,uint)', + 1348569399: '(bool,bool,uint,string)', + 2874982852: '(bool,bool,uint,bool)', + 201299213: '(bool,bool,uint,address)', + 395003525: '(bool,bool,string,uint)', + 1830717265: '(bool,bool,string,string)', + 3092715066: '(bool,bool,string,bool)', + 4188875657: '(bool,bool,string,address)', + 3259532109: '(bool,bool,bool,uint)', + 719587540: '(bool,bool,bool,string)', + 992632032: '(bool,bool,bool,bool)', + 2352126746: '(bool,bool,bool,address)', + 1620281063: '(bool,bool,address,uint)', + 2695133539: '(bool,bool,address,string)', + 3231908568: '(bool,bool,address,bool)', + 4102557348: '(bool,bool,address,address)', + 2617143996: '(bool,address,uint,uint)', + 2691192883: '(bool,address,uint,string)', + 4002252402: '(bool,address,uint,bool)', + 1760647349: '(bool,address,uint,address)', + 194640930: '(bool,address,string,uint)', + 2805734838: '(bool,address,string,string)', + 3804222987: '(bool,address,string,bool)', + 1870422078: '(bool,address,string,address)', + 1287000017: '(bool,address,bool,uint)', + 1248250676: '(bool,address,bool,string)', + 1788626827: '(bool,address,bool,bool)', + 474063670: '(bool,address,bool,address)', + 1384430956: '(bool,address,address,uint)', + 3625099623: '(bool,address,address,string)', + 1180699616: '(bool,address,address,bool)', + 487903233: '(bool,address,address,address)', + 1024368100: '(address,uint,uint,uint)', + 2301889963: '(address,uint,uint,string)', + 3964381346: '(address,uint,uint,bool)', + 519451700: '(address,uint,uint,address)', + 4111650715: '(address,uint,string,uint)', + 2119616147: '(address,uint,string,string)', + 2751614737: '(address,uint,string,bool)', + 3698927108: '(address,uint,string,address)', + 1770996626: '(address,uint,bool,uint)', + 2391690869: '(address,uint,bool,string)', + 4272018778: '(address,uint,bool,bool)', + 602229106: '(address,uint,bool,address)', + 2782496616: '(address,uint,address,uint)', + 1567749022: '(address,uint,address,string)', + 4051804649: '(address,uint,address,bool)', + 3961816175: '(address,uint,address,address)', + 2764647008: '(address,string,uint,uint)', + 1561552329: '(address,string,uint,string)', + 2116357467: '(address,string,uint,bool)', + 3755464715: '(address,string,uint,address)', + 2706362425: '(address,string,string,uint)', + 1560462603: '(address,string,string,string)', + 900007711: '(address,string,string,bool)', + 2689478535: '(address,string,string,address)', + 3877655068: '(address,string,bool,uint)', + 3154862590: '(address,string,bool,string)', + 1595759775: '(address,string,bool,bool)', + 542667202: '(address,string,bool,address)', + 2350461865: '(address,string,address,uint)', + 4158874181: '(address,string,address,string)', + 233909110: '(address,string,address,bool)', + 221706784: '(address,string,address,address)', + 3255869470: '(address,bool,uint,uint)', + 2606272204: '(address,bool,uint,string)', + 2244855215: '(address,bool,uint,bool)', + 227337758: '(address,bool,uint,address)', + 2652011374: '(address,bool,string,uint)', + 1197235251: '(address,bool,string,string)', + 1353532957: '(address,bool,string,bool)', + 436029782: '(address,bool,string,address)', + 3484780374: '(address,bool,bool,uint)', + 3754205928: '(address,bool,bool,string)', + 3401856121: '(address,bool,bool,bool)', + 3476636805: '(address,bool,bool,address)', + 3698398930: '(address,bool,address,uint)', + 769095910: '(address,bool,address,string)', + 2801077007: '(address,bool,address,bool)', + 1711502813: '(address,bool,address,address)', + 1425929188: '(address,address,uint,uint)', + 2647731885: '(address,address,uint,string)', + 3270936812: '(address,address,uint,bool)', + 3603321462: '(address,address,uint,address)', + 69767936: '(address,address,string,uint)', + 566079269: '(address,address,string,string)', + 1863997774: '(address,address,string,bool)', + 2406706454: '(address,address,string,address)', + 2513854225: '(address,address,bool,uint)', + 2858762440: '(address,address,bool,string)', + 752096074: '(address,address,bool,bool)', + 2669396846: '(address,address,bool,address)', + 3982404743: '(address,address,address,uint)', + 4161329696: '(address,address,address,string)', + 238520724: '(address,address,address,bool)', + 1717301556: '(address,address,address,address)', + 4133908826: '(uint,uint)', + 3054400204: '(string,uint)', - // Latest method signatures after updating uint to uint256 and int to int256 + // Latest method signatures after updating uint to uint256 and int to int256 - 760966329: '(int256)', - 4163653873: '(uint256)', - 1681903839: '(uint256, string)', - 480083635: '(uint256, bool)', - 1764191366: '(uint256, address)', - 965833939: '(bool, uint256)', - 2198464680: '(address, uint256)', - 3522001468: '(uint256, uint256, uint256)', - 1909476082: '(uint256, uint256, string)', - 1197922930: '(uint256, uint256, bool)', - 1553380145: '(uint256, uint256, address)', - 933920076: '(uint256, string, uint256)', - 2970968351: '(uint256, string, string)', - 1290643290: '(uint256, string, bool)', - 2063255897: '(uint256, string, address)', - 537493524: '(uint256, bool, uint256)', - 2239189025: '(uint256, bool, string)', - 544310864: '(uint256, bool, bool)', - 889741179: '(uint256, bool, address)', - 1520131797: '(uint256, address, uint256)', - 1674265081: '(uint256, address, string)', - 2607726658: '(uint256, address, bool)', - 3170737120: '(uint256, address, address)', - 3393701099: '(string, uint256, uint256)', - 1500569737: '(string, uint256, string)', - 3396809649: '(string, uint256, bool)', - 478069832: '(string, uint256, address)', - 1478619041: '(string, string, uint256)', - 3378075862: '(string, bool, uint256)', - 220641573: '(string, address, uint256)', - 923808615: '(bool, uint256, uint256)', - 3288086896: '(bool, uint256, string)', - 3906927529: '(bool, uint256, bool)', - 143587794: '(bool, uint256, address)', - 278130193: '(bool, string, uint256)', - 317855234: '(bool, bool, uint256)', - 1601936123: '(bool, address, uint256)', - 3063663350: '(address, uint256, uint256)', - 2717051050: '(address, uint256, string)', - 1736575400: '(address, uint256, bool)', - 2076235848: '(address, uint256, address)', - 1742565361: '(address, string, uint256)', - 2622462459: '(address, bool, uint256)', - 402547077: '(address, address, uint256)', - 423606272: '(uint256, uint256, uint256, uint256)', - 1506790371: '(uint256, uint256, uint256, string)', - 4202792367: '(uint256, uint256, uint256, address)', - 1570936811: '(uint256, uint256, string, uint256)', - 668512210: '(uint256, uint256, string, string)', - 2062986021: '(uint256, uint256, string, bool)', - 1121066423: '(uint256, uint256, string, address)', - 3950997458: '(uint256, uint256, bool, uint256)', - 2780101785: '(uint256, uint256, bool, string)', - 2869451494: '(uint256, uint256, bool, bool)', - 2592172675: '(uint256, uint256, bool, address)', - 2297881778: '(uint256, uint256, address, uint256)', - 1826504888: '(uint256, uint256, address, string)', - 365610102: '(uint256, uint256, address, bool)', - 1453707697: '(uint256, uint256, address, address)', - 2193775476: '(uint256, string, uint256, uint256)', - 3082360010: '(uint256, string, uint256, string)', - 1763348340: '(uint256, string, uint256, bool)', - 992115124: '(uint256, string, uint256, address)', - 2955463101: '(uint256, string, string, uint256)', - 564987523: '(uint256, string, string, string)', - 3014047421: '(uint256, string, string, bool)', - 3582182914: '(uint256, string, string, address)', - 3472922752: '(uint256, string, bool, uint256)', - 3537118157: '(uint256, string, bool, string)', - 3126025628: '(uint256, string, bool, bool)', - 2922300801: '(uint256, string, bool, address)', - 3906142605: '(uint256, string, address, uint256)', - 2621104033: '(uint256, string, address, string)', - 2428701270: '(uint256, string, address, bool)', - 1634266465: '(uint256, string, address, address)', - 3333212072: '(uint256, bool, uint256, uint256)', - 3724797812: '(uint256, bool, uint256, string)', - 2443193898: '(uint256, bool, uint256, bool)', - 2295029825: '(uint256, bool, uint256, address)', - 740099910: '(uint256, bool, string, uint256)', - 1757984957: '(uint256, bool, string, string)', - 3952250239: '(uint256, bool, string, bool)', - 4015165464: '(uint256, bool, string, address)', - 1952763427: '(uint256, bool, bool, uint256)', - 3722155361: '(uint256, bool, bool, string)', - 3069540257: '(uint256, bool, bool, bool)', - 1768164185: '(uint256, bool, bool, address)', - 125994997: '(uint256, bool, address, uint256)', - 2917159623: '(uint256, bool, address, string)', - 1162695845: '(uint256, bool, address, bool)', - 2716814523: '(uint256, bool, address, address)', - 211605953: '(uint256, address, uint256, uint256)', - 3719324961: '(uint256, address, uint256, string)', - 1601452668: '(uint256, address, uint256, bool)', - 364980149: '(uint256, address, uint256, address)', - 1182952285: '(uint256, address, string, uint256)', - 1041403043: '(uint256, address, string, string)', - 3425872647: '(uint256, address, string, bool)', - 2629472255: '(uint256, address, string, address)', - 1522374954: '(uint256, address, bool, uint256)', - 2432370346: '(uint256, address, bool, string)', - 3813741583: '(uint256, address, bool, bool)', - 4017276179: '(uint256, address, bool, address)', - 1936653238: '(uint256, address, address, uint256)', - 52195187: '(uint256, address, address, string)', - 153090805: '(uint256, address, address, bool)', - 612938772: '(uint256, address, address, address)', - 2812835923: '(string, uint256, uint256, uint256)', - 2236298390: '(string, uint256, uint256, string)', - 1982258066: '(string, uint256, uint256, bool)', - 3793609336: '(string, uint256, uint256, address)', - 3330189777: '(string, uint256, string, uint256)', - 1522028063: '(string, uint256, string, string)', - 2099530013: '(string, uint256, string, bool)', - 2084975268: '(string, uint256, string, address)', - 3827003247: '(string, uint256, bool, uint256)', - 2885106328: '(string, uint256, bool, string)', - 894187222: '(string, uint256, bool, bool)', - 3773389720: '(string, uint256, bool, address)', - 1325727174: '(string, uint256, address, uint256)', - 2684039059: '(string, uint256, address, string)', - 2182163010: '(string, uint256, address, bool)', - 1587722158: '(string, uint256, address, address)', - 4099767596: '(string, string, uint256, uint256)', - 1562023706: '(string, string, uint256, string)', - 3282609748: '(string, string, uint256, bool)', - 270792626: '(string, string, uint256, address)', - 2393878571: '(string, string, string, uint256)', - 3601791698: '(string, string, bool, uint256)', - 2093204999: '(string, string, address, uint256)', - 1689631591: '(string, bool, uint256, uint256)', - 1949134567: '(string, bool, uint256, string)', - 2331496330: '(string, bool, uint256, bool)', - 2472413631: '(string, bool, uint256, address)', - 620303461: '(string, bool, string, uint256)', - 2386524329: '(string, bool, bool, uint256)', - 1560853253: '(string, bool, address, uint256)', - 4176812830: '(string, address, uint256, uint256)', - 1514632754: '(string, address, uint256, string)', - 4232594928: '(string, address, uint256, bool)', - 1677429701: '(string, address, uint256, address)', - 2446397742: '(string, address, string, uint256)', - 1050642026: '(string, address, bool, uint256)', - 2398352281: '(string, address, address, uint256)', - 927708338: '(bool, uint256, uint256, uint256)', - 2389310301: '(bool, uint256, uint256, string)', - 3197649747: '(bool, uint256, uint256, bool)', - 14518201: '(bool, uint256, uint256, address)', - 1779538402: '(bool, uint256, string, uint256)', - 4122747465: '(bool, uint256, string, string)', - 3857124139: '(bool, uint256, string, bool)', - 4275904511: '(bool, uint256, string, address)', - 2437143473: '(bool, uint256, bool, string)', - 3468031191: '(bool, uint256, bool, bool)', - 2597139990: '(bool, uint256, bool, address)', - 355982471: '(bool, uint256, address, uint256)', - 464760986: '(bool, uint256, address, string)', - 3032683775: '(bool, uint256, address, bool)', - 653615272: '(bool, uint256, address, address)', - 679886795: '(bool, string, uint256, uint256)', - 450457062: '(bool, string, uint256, string)', - 1796103507: '(bool, string, uint256, bool)', - 362193358: '(bool, string, uint256, address)', - 2078327787: '(bool, string, string, uint256)', - 369533843: '(bool, string, bool, uint256)', - 196087467: '(bool, bool, uint256, uint256)', - 2111099104: '(bool, bool, uint256, string)', - 1637764366: '(bool, bool, uint256, bool)', - 1420274080: '(bool, bool, uint256, address)', - 3819555375: '(bool, bool, string, uint256)', - 1836074433: '(bool, bool, bool, uint256)', - 1276263767: '(bool, bool, address, uint256)', - 2079424929: '(bool, address, uint256, uint256)', - 1374724088: '(bool, address, uint256, string)', - 3590430492: '(bool, address, uint256, bool)', - 325780957: '(bool, address, uint256, address)', - 3256837319: '(bool, address, string, uint256)', - 126031106: '(bool, address, bool, uint256)', - 208064958: '(bool, address, address, uint256)', - 888202806: '(address, uint256, uint256, uint256)', - 1244184599: '(address, uint256, uint256, string)', - 1727118439: '(address, uint256, uint256, bool)', - 551786573: '(address, uint256, uint256, address)', - 3204577425: '(address, uint256, string, uint256)', - 2292761606: '(address, uint256, string, string)', - 3474460764: '(address, uint256, string, bool)', - 1547898183: '(address, uint256, string, address)', - 586594713: '(address, uint256, bool, uint256)', - 3316483577: '(address, uint256, bool, string)', - 1005970743: '(address, uint256, bool, bool)', - 2736520652: '(address, uint256, bool, address)', - 269444366: '(address, uint256, address, uint256)', - 497649386: '(address, uint256, address, string)', - 2713504179: '(address, uint256, address, bool)', - 1200430178: '(address, uint256, address, address)', - 499704248: '(address, string, uint256, uint256)', - 1149776040: '(address, string, uint256, string)', - 251125840: '(address, string, uint256, bool)', - 1662531192: '(address, string, uint256, address)', - 362776871: '(address, string, string, uint256)', - 1365129398: '(address, string, bool, uint256)', - 1166009295: '(address, string, address, uint256)', - 946861556: '(address, bool, uint256, uint256)', - 178704301: '(address, bool, uint256, string)', - 3294903840: '(address, bool, uint256, bool)', - 3438776481: '(address, bool, uint256, address)', - 2162598411: '(address, bool, string, uint256)', - 2353946086: '(address, bool, bool, uint256)', - 2807847390: '(address, bool, address, uint256)', - 3193255041: '(address, address, uint256, uint256)', - 4256496016: '(address, address, uint256, string)', - 2604815586: '(address, address, uint256, bool)', - 2376523509: '(address, address, uint256, address)', - 4011651047: '(address, address, string, uint256)', - 963766156: '(address, address, bool, uint256)', - 2485456247: '(address, address, address, uint256)', + 760966329: '(int256)', + 4163653873: '(uint256)', + 1681903839: '(uint256, string)', + 480083635: '(uint256, bool)', + 1764191366: '(uint256, address)', + 965833939: '(bool, uint256)', + 2198464680: '(address, uint256)', + 3522001468: '(uint256, uint256, uint256)', + 1909476082: '(uint256, uint256, string)', + 1197922930: '(uint256, uint256, bool)', + 1553380145: '(uint256, uint256, address)', + 933920076: '(uint256, string, uint256)', + 2970968351: '(uint256, string, string)', + 1290643290: '(uint256, string, bool)', + 2063255897: '(uint256, string, address)', + 537493524: '(uint256, bool, uint256)', + 2239189025: '(uint256, bool, string)', + 544310864: '(uint256, bool, bool)', + 889741179: '(uint256, bool, address)', + 1520131797: '(uint256, address, uint256)', + 1674265081: '(uint256, address, string)', + 2607726658: '(uint256, address, bool)', + 3170737120: '(uint256, address, address)', + 3393701099: '(string, uint256, uint256)', + 1500569737: '(string, uint256, string)', + 3396809649: '(string, uint256, bool)', + 478069832: '(string, uint256, address)', + 1478619041: '(string, string, uint256)', + 3378075862: '(string, bool, uint256)', + 220641573: '(string, address, uint256)', + 923808615: '(bool, uint256, uint256)', + 3288086896: '(bool, uint256, string)', + 3906927529: '(bool, uint256, bool)', + 143587794: '(bool, uint256, address)', + 278130193: '(bool, string, uint256)', + 317855234: '(bool, bool, uint256)', + 1601936123: '(bool, address, uint256)', + 3063663350: '(address, uint256, uint256)', + 2717051050: '(address, uint256, string)', + 1736575400: '(address, uint256, bool)', + 2076235848: '(address, uint256, address)', + 1742565361: '(address, string, uint256)', + 2622462459: '(address, bool, uint256)', + 402547077: '(address, address, uint256)', + 423606272: '(uint256, uint256, uint256, uint256)', + 1506790371: '(uint256, uint256, uint256, string)', + 4202792367: '(uint256, uint256, uint256, address)', + 1570936811: '(uint256, uint256, string, uint256)', + 668512210: '(uint256, uint256, string, string)', + 2062986021: '(uint256, uint256, string, bool)', + 1121066423: '(uint256, uint256, string, address)', + 3950997458: '(uint256, uint256, bool, uint256)', + 2780101785: '(uint256, uint256, bool, string)', + 2869451494: '(uint256, uint256, bool, bool)', + 2592172675: '(uint256, uint256, bool, address)', + 2297881778: '(uint256, uint256, address, uint256)', + 1826504888: '(uint256, uint256, address, string)', + 365610102: '(uint256, uint256, address, bool)', + 1453707697: '(uint256, uint256, address, address)', + 2193775476: '(uint256, string, uint256, uint256)', + 3082360010: '(uint256, string, uint256, string)', + 1763348340: '(uint256, string, uint256, bool)', + 992115124: '(uint256, string, uint256, address)', + 2955463101: '(uint256, string, string, uint256)', + 564987523: '(uint256, string, string, string)', + 3014047421: '(uint256, string, string, bool)', + 3582182914: '(uint256, string, string, address)', + 3472922752: '(uint256, string, bool, uint256)', + 3537118157: '(uint256, string, bool, string)', + 3126025628: '(uint256, string, bool, bool)', + 2922300801: '(uint256, string, bool, address)', + 3906142605: '(uint256, string, address, uint256)', + 2621104033: '(uint256, string, address, string)', + 2428701270: '(uint256, string, address, bool)', + 1634266465: '(uint256, string, address, address)', + 3333212072: '(uint256, bool, uint256, uint256)', + 3724797812: '(uint256, bool, uint256, string)', + 2443193898: '(uint256, bool, uint256, bool)', + 2295029825: '(uint256, bool, uint256, address)', + 740099910: '(uint256, bool, string, uint256)', + 1757984957: '(uint256, bool, string, string)', + 3952250239: '(uint256, bool, string, bool)', + 4015165464: '(uint256, bool, string, address)', + 1952763427: '(uint256, bool, bool, uint256)', + 3722155361: '(uint256, bool, bool, string)', + 3069540257: '(uint256, bool, bool, bool)', + 1768164185: '(uint256, bool, bool, address)', + 125994997: '(uint256, bool, address, uint256)', + 2917159623: '(uint256, bool, address, string)', + 1162695845: '(uint256, bool, address, bool)', + 2716814523: '(uint256, bool, address, address)', + 211605953: '(uint256, address, uint256, uint256)', + 3719324961: '(uint256, address, uint256, string)', + 1601452668: '(uint256, address, uint256, bool)', + 364980149: '(uint256, address, uint256, address)', + 1182952285: '(uint256, address, string, uint256)', + 1041403043: '(uint256, address, string, string)', + 3425872647: '(uint256, address, string, bool)', + 2629472255: '(uint256, address, string, address)', + 1522374954: '(uint256, address, bool, uint256)', + 2432370346: '(uint256, address, bool, string)', + 3813741583: '(uint256, address, bool, bool)', + 4017276179: '(uint256, address, bool, address)', + 1936653238: '(uint256, address, address, uint256)', + 52195187: '(uint256, address, address, string)', + 153090805: '(uint256, address, address, bool)', + 612938772: '(uint256, address, address, address)', + 2812835923: '(string, uint256, uint256, uint256)', + 2236298390: '(string, uint256, uint256, string)', + 1982258066: '(string, uint256, uint256, bool)', + 3793609336: '(string, uint256, uint256, address)', + 3330189777: '(string, uint256, string, uint256)', + 1522028063: '(string, uint256, string, string)', + 2099530013: '(string, uint256, string, bool)', + 2084975268: '(string, uint256, string, address)', + 3827003247: '(string, uint256, bool, uint256)', + 2885106328: '(string, uint256, bool, string)', + 894187222: '(string, uint256, bool, bool)', + 3773389720: '(string, uint256, bool, address)', + 1325727174: '(string, uint256, address, uint256)', + 2684039059: '(string, uint256, address, string)', + 2182163010: '(string, uint256, address, bool)', + 1587722158: '(string, uint256, address, address)', + 4099767596: '(string, string, uint256, uint256)', + 1562023706: '(string, string, uint256, string)', + 3282609748: '(string, string, uint256, bool)', + 270792626: '(string, string, uint256, address)', + 2393878571: '(string, string, string, uint256)', + 3601791698: '(string, string, bool, uint256)', + 2093204999: '(string, string, address, uint256)', + 1689631591: '(string, bool, uint256, uint256)', + 1949134567: '(string, bool, uint256, string)', + 2331496330: '(string, bool, uint256, bool)', + 2472413631: '(string, bool, uint256, address)', + 620303461: '(string, bool, string, uint256)', + 2386524329: '(string, bool, bool, uint256)', + 1560853253: '(string, bool, address, uint256)', + 4176812830: '(string, address, uint256, uint256)', + 1514632754: '(string, address, uint256, string)', + 4232594928: '(string, address, uint256, bool)', + 1677429701: '(string, address, uint256, address)', + 2446397742: '(string, address, string, uint256)', + 1050642026: '(string, address, bool, uint256)', + 2398352281: '(string, address, address, uint256)', + 927708338: '(bool, uint256, uint256, uint256)', + 2389310301: '(bool, uint256, uint256, string)', + 3197649747: '(bool, uint256, uint256, bool)', + 14518201: '(bool, uint256, uint256, address)', + 1779538402: '(bool, uint256, string, uint256)', + 4122747465: '(bool, uint256, string, string)', + 3857124139: '(bool, uint256, string, bool)', + 4275904511: '(bool, uint256, string, address)', + 2437143473: '(bool, uint256, bool, string)', + 3468031191: '(bool, uint256, bool, bool)', + 2597139990: '(bool, uint256, bool, address)', + 355982471: '(bool, uint256, address, uint256)', + 464760986: '(bool, uint256, address, string)', + 3032683775: '(bool, uint256, address, bool)', + 653615272: '(bool, uint256, address, address)', + 679886795: '(bool, string, uint256, uint256)', + 450457062: '(bool, string, uint256, string)', + 1796103507: '(bool, string, uint256, bool)', + 362193358: '(bool, string, uint256, address)', + 2078327787: '(bool, string, string, uint256)', + 369533843: '(bool, string, bool, uint256)', + 196087467: '(bool, bool, uint256, uint256)', + 2111099104: '(bool, bool, uint256, string)', + 1637764366: '(bool, bool, uint256, bool)', + 1420274080: '(bool, bool, uint256, address)', + 3819555375: '(bool, bool, string, uint256)', + 1836074433: '(bool, bool, bool, uint256)', + 1276263767: '(bool, bool, address, uint256)', + 2079424929: '(bool, address, uint256, uint256)', + 1374724088: '(bool, address, uint256, string)', + 3590430492: '(bool, address, uint256, bool)', + 325780957: '(bool, address, uint256, address)', + 3256837319: '(bool, address, string, uint256)', + 126031106: '(bool, address, bool, uint256)', + 208064958: '(bool, address, address, uint256)', + 888202806: '(address, uint256, uint256, uint256)', + 1244184599: '(address, uint256, uint256, string)', + 1727118439: '(address, uint256, uint256, bool)', + 551786573: '(address, uint256, uint256, address)', + 3204577425: '(address, uint256, string, uint256)', + 2292761606: '(address, uint256, string, string)', + 3474460764: '(address, uint256, string, bool)', + 1547898183: '(address, uint256, string, address)', + 586594713: '(address, uint256, bool, uint256)', + 3316483577: '(address, uint256, bool, string)', + 1005970743: '(address, uint256, bool, bool)', + 2736520652: '(address, uint256, bool, address)', + 269444366: '(address, uint256, address, uint256)', + 497649386: '(address, uint256, address, string)', + 2713504179: '(address, uint256, address, bool)', + 1200430178: '(address, uint256, address, address)', + 499704248: '(address, string, uint256, uint256)', + 1149776040: '(address, string, uint256, string)', + 251125840: '(address, string, uint256, bool)', + 1662531192: '(address, string, uint256, address)', + 362776871: '(address, string, string, uint256)', + 1365129398: '(address, string, bool, uint256)', + 1166009295: '(address, string, address, uint256)', + 946861556: '(address, bool, uint256, uint256)', + 178704301: '(address, bool, uint256, string)', + 3294903840: '(address, bool, uint256, bool)', + 3438776481: '(address, bool, uint256, address)', + 2162598411: '(address, bool, string, uint256)', + 2353946086: '(address, bool, bool, uint256)', + 2807847390: '(address, bool, address, uint256)', + 3193255041: '(address, address, uint256, uint256)', + 4256496016: '(address, address, uint256, string)', + 2604815586: '(address, address, uint256, bool)', + 2376523509: '(address, address, uint256, address)', + 4011651047: '(address, address, string, uint256)', + 963766156: '(address, address, bool, uint256)', + 2485456247: '(address, address, address, uint256)', } diff --git a/libs/remix-lib/src/helpers/txResultHelper.ts b/libs/remix-lib/src/helpers/txResultHelper.ts index a7a89163c6..3f9eb23d59 100644 --- a/libs/remix-lib/src/helpers/txResultHelper.ts +++ b/libs/remix-lib/src/helpers/txResultHelper.ts @@ -3,12 +3,12 @@ import { bufferToHex } from '@ethereumjs/util' import { isHexString } from 'ethjs-util' function convertToPrefixedHex (input) { - if (input === undefined || input === null || isHexString(input)) { - return input - } else if (Buffer.isBuffer(input)) { - return bufferToHex(input) - } - return '0x' + input.toString(16) + if (input === undefined || input === null || isHexString(input)) { + return input + } else if (Buffer.isBuffer(input)) { + return bufferToHex(input) + } + return '0x' + input.toString(16) } /* @@ -21,23 +21,23 @@ function convertToPrefixedHex (input) { So we need to normalize the values to prefixed hex strings */ export function resultToRemixTx (txResult, execResult?) { - const { receipt, transactionHash, result } = txResult - const { status, gasUsed, contractAddress } = receipt - let returnValue, errorMessage + const { receipt, transactionHash, result } = txResult + const { status, gasUsed, contractAddress } = receipt + let returnValue, errorMessage - if (isHexString(result)) { - returnValue = result - } else if (execResult !== undefined) { - returnValue = execResult.returnValue - errorMessage = execResult.exceptionError - } + if (isHexString(result)) { + returnValue = result + } else if (execResult !== undefined) { + returnValue = execResult.returnValue + errorMessage = execResult.exceptionError + } - return { - transactionHash, - status, - gasUsed: convertToPrefixedHex(gasUsed), - error: errorMessage, - return: convertToPrefixedHex(returnValue), - createdAddress: convertToPrefixedHex(contractAddress) - } + return { + transactionHash, + status, + gasUsed: convertToPrefixedHex(gasUsed), + error: errorMessage, + return: convertToPrefixedHex(returnValue), + createdAddress: convertToPrefixedHex(contractAddress) + } } diff --git a/libs/remix-lib/src/helpers/uiHelper.ts b/libs/remix-lib/src/helpers/uiHelper.ts index cab35f54a5..b76203d04d 100644 --- a/libs/remix-lib/src/helpers/uiHelper.ts +++ b/libs/remix-lib/src/helpers/uiHelper.ts @@ -1,35 +1,35 @@ 'use strict' export function formatMemory (mem, width) { - const ret = {} - if (!mem) { - return ret - } + const ret = {} + if (!mem) { + return ret + } - if (!mem.substr) { - mem = mem.join('') // geth returns an array, eth return raw string - } + if (!mem.substr) { + mem = mem.join('') // geth returns an array, eth return raw string + } - for (let k = 0; k < mem.length; k += (width * 2)) { - const memory = mem.substr(k, width * 2) - const content = tryConvertAsciiFormat(memory) - ret['0x' + (k / 2).toString(16)] = content.raw + '\t' + content.ascii - } - return ret + for (let k = 0; k < mem.length; k += (width * 2)) { + const memory = mem.substr(k, width * 2) + const content = tryConvertAsciiFormat(memory) + ret['0x' + (k / 2).toString(16)] = content.raw + '\t' + content.ascii + } + return ret } export function tryConvertAsciiFormat (memorySlot) { - const ret = { ascii: '', raw: '' } - for (let k = 0; k < memorySlot.length; k += 2) { - const raw = memorySlot.substr(k, 2) - let ascii = String.fromCharCode(parseInt(raw, 16)) - ascii = ascii.replace(/[^\w\s]/, '?') - if (ascii === '') { - ascii = '?' - } - ret.ascii += ascii - ret.raw += raw + const ret = { ascii: '', raw: '' } + for (let k = 0; k < memorySlot.length; k += 2) { + const raw = memorySlot.substr(k, 2) + let ascii = String.fromCharCode(parseInt(raw, 16)) + ascii = ascii.replace(/[^\w\s]/, '?') + if (ascii === '') { + ascii = '?' } - return ret + ret.ascii += ascii + ret.raw += raw + } + return ret } /** @@ -44,41 +44,41 @@ export function tryConvertAsciiFormat (memorySlot) { * used if multiple occurences of the same key is needed */ export function formatCss (css1, css2) { - let ret = '' + let ret = '' for (const arg in arguments) { // eslint-disable-line for (const k in arguments[arg]) { // eslint-disable-line if (arguments[arg][k] && ret.indexOf(k) === -1) { // eslint-disable-line - if (k.indexOf('*') === 0) { + if (k.indexOf('*') === 0) { ret += arguments[arg][k] // eslint-disable-line - } else { + } else { ret += k + ':' + arguments[arg][k] + ';' // eslint-disable-line - } - } } + } } - return ret + } + return ret } export function normalizeHex (hex) { - if (hex.indexOf('0x') === 0) { - hex = hex.replace('0x', '') - } - hex = hex.replace(/^0+/, '') - return '0x' + hex + if (hex.indexOf('0x') === 0) { + hex = hex.replace('0x', '') + } + hex = hex.replace(/^0+/, '') + return '0x' + hex } export function normalizeHexAddress (hex) { - if (hex.indexOf('0x') === 0) hex = hex.replace('0x', '') - if (hex.length >= 40) { - const reg = /(.{40})$/.exec(hex) - if (reg) { - return '0x' + reg[0] - } - } else { - return '0x' + (new Array(40 - hex.length + 1).join('0')) + hex + if (hex.indexOf('0x') === 0) hex = hex.replace('0x', '') + if (hex.length >= 40) { + const reg = /(.{40})$/.exec(hex) + if (reg) { + return '0x' + reg[0] } + } else { + return '0x' + (new Array(40 - hex.length + 1).join('0')) + hex + } } export function runInBrowser () { - return typeof window !== 'undefined' + return typeof window !== 'undefined' } diff --git a/libs/remix-lib/src/index.ts b/libs/remix-lib/src/index.ts index d3f28e470a..416ac2a34b 100644 --- a/libs/remix-lib/src/index.ts +++ b/libs/remix-lib/src/index.ts @@ -22,21 +22,21 @@ export { QueryParams } from './query-params' export { VMexecutionResult } from './execution/txRunnerVM' const helpers = { - ui: uiHelper, - compiler: compilerHelper, - txResultHelper + ui: uiHelper, + compiler: compilerHelper, + txResultHelper } const execution = { - EventsDecoder: EventsDecoder, - txExecution: txExecution, - txHelper: txHelper, - txFormat: txFormat, - txListener: TxListener, - TxRunner: TxRunner, - TxRunnerWeb3: TxRunnerWeb3, - TxRunnerVM: TxRunnerVM, - typeConversion: typeConversion, - LogsManager, - forkAt + EventsDecoder: EventsDecoder, + txExecution: txExecution, + txHelper: txHelper, + txFormat: txFormat, + txListener: TxListener, + TxRunner: TxRunner, + TxRunnerWeb3: TxRunnerWeb3, + TxRunnerVM: TxRunnerVM, + typeConversion: typeConversion, + LogsManager, + forkAt } export { EventManager, helpers, Storage, util, execution, hash } diff --git a/libs/remix-lib/src/init.ts b/libs/remix-lib/src/init.ts index b59d10b4a4..58caa5ffe2 100644 --- a/libs/remix-lib/src/init.ts +++ b/libs/remix-lib/src/init.ts @@ -2,53 +2,53 @@ import Web3 from 'web3' export function loadWeb3 (url = 'http://localhost:8545') { - const web3 = new Web3() - web3.setProvider(new Web3.providers.HttpProvider(url)) - extend(web3) - return web3 + const web3 = new Web3() + web3.setProvider(new Web3.providers.HttpProvider(url)) + extend(web3) + return web3 } export function extendWeb3 (web3) { - extend(web3) + extend(web3) } export function extend (web3) { - if (!web3.extend) { - return - } - // DEBUG - const methods = [] - if (!(web3.debug && web3.debug.preimage)) { - methods.push(new web3.extend.Method({ - name: 'preimage', - call: 'debug_preimage', - inputFormatter: [null], - params: 1 - })) - } + if (!web3.extend) { + return + } + // DEBUG + const methods = [] + if (!(web3.debug && web3.debug.preimage)) { + methods.push(new web3.extend.Method({ + name: 'preimage', + call: 'debug_preimage', + inputFormatter: [null], + params: 1 + })) + } - if (!(web3.debug && web3.debug.traceTransaction)) { - methods.push(new web3.extend.Method({ - name: 'traceTransaction', - call: 'debug_traceTransaction', - inputFormatter: [null, null], - params: 2 - })) - } + if (!(web3.debug && web3.debug.traceTransaction)) { + methods.push(new web3.extend.Method({ + name: 'traceTransaction', + call: 'debug_traceTransaction', + inputFormatter: [null, null], + params: 2 + })) + } - if (!(web3.debug && web3.debug.storageRangeAt)) { - methods.push(new web3.extend.Method({ - name: 'storageRangeAt', - call: 'debug_storageRangeAt', - inputFormatter: [null, null, null, null, null], - params: 5 - })) - } - if (methods.length > 0) { - web3.extend({ - property: 'debug', - methods: methods, - properties: [] - }) - } + if (!(web3.debug && web3.debug.storageRangeAt)) { + methods.push(new web3.extend.Method({ + name: 'storageRangeAt', + call: 'debug_storageRangeAt', + inputFormatter: [null, null, null, null, null], + params: 5 + })) + } + if (methods.length > 0) { + web3.extend({ + property: 'debug', + methods: methods, + properties: [] + }) + } } diff --git a/libs/remix-lib/src/query-params.ts b/libs/remix-lib/src/query-params.ts index 7e89b226ff..8adefe84b3 100644 --- a/libs/remix-lib/src/query-params.ts +++ b/libs/remix-lib/src/query-params.ts @@ -2,37 +2,37 @@ export class QueryParams { - update (params) { - const currentParams = this.get() - const keys = Object.keys(params) - for (const x in keys) { - currentParams[keys[x]] = params[keys[x]] - } - let queryString = '#' - const updatedKeys = Object.keys(currentParams) - for (const y in updatedKeys) { - queryString += updatedKeys[y] + '=' + currentParams[updatedKeys[y]] + '&' - } - window.location.hash = queryString.slice(0, -1) + update (params) { + const currentParams = this.get() + const keys = Object.keys(params) + for (const x in keys) { + currentParams[keys[x]] = params[keys[x]] } + let queryString = '#' + const updatedKeys = Object.keys(currentParams) + for (const y in updatedKeys) { + queryString += updatedKeys[y] + '=' + currentParams[updatedKeys[y]] + '&' + } + window.location.hash = queryString.slice(0, -1) + } - get () { - const qs = window.location.hash.substr(1) + get () { + const qs = window.location.hash.substr(1) - if (window.location.search.length > 0) { - // use legacy query params instead of hash - window.location.hash = window.location.search.substr(1) - window.location.search = '' - } + if (window.location.search.length > 0) { + // use legacy query params instead of hash + window.location.hash = window.location.search.substr(1) + window.location.search = '' + } - const params = {} - const parts = qs.split('&') - for (const x in parts) { - const keyValue = parts[x].split('=') - if (keyValue[0] !== '') { - params[keyValue[0]] = keyValue[1] - } - } - return params + const params = {} + const parts = qs.split('&') + for (const x in parts) { + const keyValue = parts[x].split('=') + if (keyValue[0] !== '') { + params[keyValue[0]] = keyValue[1] + } } + return params + } } diff --git a/libs/remix-lib/src/storage.ts b/libs/remix-lib/src/storage.ts index 02278cc47f..14c279cf51 100644 --- a/libs/remix-lib/src/storage.ts +++ b/libs/remix-lib/src/storage.ts @@ -1,80 +1,80 @@ 'use strict' export class Storage { - prefix + prefix - constructor (prefix) { - this.prefix = prefix + constructor (prefix) { + this.prefix = prefix - // on startup, upgrade the old storage layout - if (typeof window !== 'undefined') { - this.safeKeys().forEach(function (name) { - if (name.indexOf('sol-cache-file-', 0) === 0) { - const content = window.localStorage.getItem(name) - window.localStorage.setItem(name.replace(/^sol-cache-file-/, 'sol:'), content) - window.localStorage.removeItem(name) - } - }) - } - - // remove obsolete key - if (typeof window !== 'undefined') { - window.localStorage.removeItem('editor-size-cache') + // on startup, upgrade the old storage layout + if (typeof window !== 'undefined') { + this.safeKeys().forEach(function (name) { + if (name.indexOf('sol-cache-file-', 0) === 0) { + const content = window.localStorage.getItem(name) + window.localStorage.setItem(name.replace(/^sol-cache-file-/, 'sol:'), content) + window.localStorage.removeItem(name) } + }) } - exists (name) { - if (typeof window !== 'undefined') { - return this.get(name) !== null - } + // remove obsolete key + if (typeof window !== 'undefined') { + window.localStorage.removeItem('editor-size-cache') } + } - get (name) { - if (typeof window !== 'undefined') { - return window.localStorage.getItem(this.prefix + name) - } + exists (name) { + if (typeof window !== 'undefined') { + return this.get(name) !== null } + } - set (name, content) { - try { - if (typeof window !== 'undefined') { - window.localStorage.setItem(this.prefix + name, content) - } - } catch (exception) { - return false - } - return true + get (name) { + if (typeof window !== 'undefined') { + return window.localStorage.getItem(this.prefix + name) } + } - remove (name) { - if (typeof window !== 'undefined') { - window.localStorage.removeItem(this.prefix + name) - } - return true + set (name, content) { + try { + if (typeof window !== 'undefined') { + window.localStorage.setItem(this.prefix + name, content) + } + } catch (exception) { + return false } + return true + } - rename (originalName, newName) { - const content = this.get(originalName) - if (!this.set(newName, content)) { - return false - } - this.remove(originalName) - return true + remove (name) { + if (typeof window !== 'undefined') { + window.localStorage.removeItem(this.prefix + name) } + return true + } - safeKeys () { - // NOTE: this is a workaround for some browsers - if (typeof window !== 'undefined') { - return Object.keys(window.localStorage).filter(function (item) { return item !== null && item !== undefined }) - } - return [] + rename (originalName, newName) { + const content = this.get(originalName) + if (!this.set(newName, content)) { + return false } + this.remove(originalName) + return true + } - keys () { - return this.safeKeys() - // filter any names not including the prefix - .filter(item => item.indexOf(this.prefix, 0) === 0) - // remove prefix from filename and add the 'browser' path - .map(item => item.substr(this.prefix.length)) + safeKeys () { + // NOTE: this is a workaround for some browsers + if (typeof window !== 'undefined') { + return Object.keys(window.localStorage).filter(function (item) { return item !== null && item !== undefined }) } + return [] + } + + keys () { + return this.safeKeys() + // filter any names not including the prefix + .filter(item => item.indexOf(this.prefix, 0) === 0) + // remove prefix from filename and add the 'browser' path + .map(item => item.substr(this.prefix.length)) + } } diff --git a/libs/remix-lib/src/util.ts b/libs/remix-lib/src/util.ts index 74317dee97..d1c9bde758 100644 --- a/libs/remix-lib/src/util.ts +++ b/libs/remix-lib/src/util.ts @@ -20,51 +20,51 @@ import stringSimilarity from 'string-similarity' * Converts a hex string to an array of integers. */ export function hexToIntArray (hexString) { - if (hexString.slice(0, 2) === '0x') { - hexString = hexString.slice(2) - } - const integers = [] - for (let i = 0; i < hexString.length; i += 2) { - integers.push(parseInt(hexString.slice(i, i + 2), 16)) - } - return integers + if (hexString.slice(0, 2) === '0x') { + hexString = hexString.slice(2) + } + const integers = [] + for (let i = 0; i < hexString.length; i += 2) { + integers.push(parseInt(hexString.slice(i, i + 2), 16)) + } + return integers } /* ints: list of BNs */ export function hexListFromBNs (bnList) { - const ret = [] - for (const k in bnList) { - const v = bnList[k].toString(16) - ret.push('0x' + v.padStart(64, '0')) - } - return ret + const ret = [] + for (const k in bnList) { + const v = bnList[k].toString(16) + ret.push('0x' + v.padStart(64, '0')) + } + return ret } export function toHexPaddedString(v: bigint | string): string { - if (v) { - if (typeof v === 'string') { - return v.startsWith('0x') ? v : '0x' + v - } else { - return '0x' + v.toString(16).padStart(64, '0') - } + if (v) { + if (typeof v === 'string') { + return v.startsWith('0x') ? v : '0x' + v + } else { + return '0x' + v.toString(16).padStart(64, '0') } - else - return '0x' + '0'.padStart(64, '0') + } + else + return '0x' + '0'.padStart(64, '0') } /* ints: ints: IntArray */ export function formatMemory (mem) { - const hexMem = bufferToHex(mem).substr(2) - const ret = [] - for (let k = 0; k < hexMem.length; k += 32) { - const row = hexMem.substr(k, 32) - ret.push(row) - } - return ret + const hexMem = bufferToHex(mem).substr(2) + const ret = [] + for (let k = 0; k < hexMem.length; k += 32) { + const row = hexMem.substr(k, 32) + ret.push(row) + } + return ret } /* @@ -73,19 +73,19 @@ export function formatMemory (mem) { return largest i such that array[i] <= target; return -1 if array[0] > target || array is empty */ export function findLowerBound (target, array) { - let start = 0 - let length = array.length - while (length > 0) { - const half = length >> 1 - const middle = start + half - if (array[middle] <= target) { - length = length - 1 - half - start = middle + 1 - } else { - length = half - } + let start = 0 + let length = array.length + while (length > 0) { + const half = length >> 1 + const middle = start + half + if (array[middle] <= target) { + length = length - 1 - half + start = middle + 1 + } else { + length = half } - return start - 1 + } + return start - 1 } /* @@ -94,8 +94,8 @@ export function findLowerBound (target, array) { return largest array[i] such that array[i] <= target; return null if array[0] > target || array is empty */ export function findLowerBoundValue (target, array) { - const index = findLowerBound(target, array) - return index >= 0 ? array[index] : null + const index = findLowerBound(target, array) + return index >= 0 ? array[index] : null } /* @@ -105,18 +105,18 @@ export function findLowerBoundValue (target, array) { Returns the smallest i for multiple candidates. */ export function findClosestIndex (target, array): number { - if (array.length === 0) { - return -1 - } - const index = findLowerBound(target, array) - if (index < 0) { - return 0 - } else if (index >= array.length - 1) { - return array.length - 1 - } else { - const middle = (array[index] + array[index + 1]) / 2 - return target <= middle ? index : index + 1 - } + if (array.length === 0) { + return -1 + } + const index = findLowerBound(target, array) + if (index < 0) { + return 0 + } else if (index >= array.length - 1) { + return array.length - 1 + } else { + const middle = (array[index] + array[index + 1]) / 2 + return target <= middle ? index : index + 1 + } } /** @@ -127,8 +127,8 @@ export function findClosestIndex (target, array): number { * @return {Object} - return the call which include the @args index */ export function findCall (index, rootCall) { - const ret = buildCallPath(index, rootCall) - return ret[ret.length - 1] + const ret = buildCallPath(index, rootCall) + return ret[ret.length - 1] } /** @@ -139,9 +139,9 @@ export function findCall (index, rootCall) { * @return {Array} - return the calls path to @args index */ export function buildCallPath (index, rootCall) { - const ret = [] - findCallInternal(index, rootCall, ret) - return ret + const ret = [] + findCallInternal(index, rootCall, ret) + return ret } /** @@ -152,9 +152,9 @@ export function buildCallPath (index, rootCall) { */ // eslint-disable-next-line camelcase export function sha3_256 (value) { - value = toBuffer(addHexPrefix(value)) - const retInBuffer: Buffer = hash.keccak(setLengthLeft(value, 32)) - return bufferToHex(retInBuffer) + value = toBuffer(addHexPrefix(value)) + const retInBuffer: Buffer = hash.keccak(setLengthLeft(value, 32)) + return bufferToHex(retInBuffer) } /** @@ -163,7 +163,7 @@ export function sha3_256 (value) { * @return {RegEx} */ export function swarmHashExtraction () { - return /a165627a7a72305820([0-9a-f]{64})0029$/ + return /a165627a7a72305820([0-9a-f]{64})0029$/ } /** @@ -172,7 +172,7 @@ export function swarmHashExtraction () { * @return {RegEx} */ export function swarmHashExtractionPOC31 () { - return /a265627a7a72315820([0-9a-f]{64})64736f6c6343([0-9a-f]{6})0032$/ + return /a265627a7a72315820([0-9a-f]{64})64736f6c6343([0-9a-f]{6})0032$/ } /** @@ -181,7 +181,7 @@ export function swarmHashExtractionPOC31 () { * @return {RegEx} */ export function swarmHashExtractionPOC32 () { - return /a265627a7a72305820([0-9a-f]{64})64736f6c6343([0-9a-f]{6})0032$/ + return /a265627a7a72305820([0-9a-f]{64})64736f6c6343([0-9a-f]{6})0032$/ } /** @@ -190,7 +190,7 @@ export function swarmHashExtractionPOC32 () { * @return {RegEx} */ export function cborEncodedValueExtraction () { - return /64697066735822([0-9a-f]{68})64736f6c6343([0-9a-f]{6})0033$/ + return /64697066735822([0-9a-f]{68})64736f6c6343([0-9a-f]{6})0033$/ } /** @@ -199,30 +199,30 @@ export function cborEncodedValueExtraction () { * @return {RegEx} */ export function inputParametersExtraction () { - return /64697066735822[0-9a-f]{68}64736f6c6343[0-9a-f]{6}0033(.*)$/ + return /64697066735822[0-9a-f]{68}64736f6c6343[0-9a-f]{6}0033(.*)$/ } export function extractcborMetadata (value) { - return value.replace(cborEncodedValueExtraction(), '') + return value.replace(cborEncodedValueExtraction(), '') } export function extractSwarmHash (value) { - value = value.replace(swarmHashExtraction(), '') - value = value.replace(swarmHashExtractionPOC31(), '') - value = value.replace(swarmHashExtractionPOC32(), '') - return value + value = value.replace(swarmHashExtraction(), '') + value = value.replace(swarmHashExtractionPOC31(), '') + value = value.replace(swarmHashExtractionPOC32(), '') + return value } export function extractinputParameters (value) { - return value.replace(inputParametersExtraction(), '') + return value.replace(inputParametersExtraction(), '') } export function getinputParameters (value) { - const regex = value.match(inputParametersExtraction()) - if (regex && regex[1]) { - return regex[1] - } else - return '' + const regex = value.match(inputParametersExtraction()) + if (regex && regex[1]) { + return regex[1] + } else + return '' } /** @@ -234,96 +234,96 @@ export function getinputParameters (value) { * @return {bool} */ export function compareByteCode (code1, code2) { - if (code1 === code2) return true - if (code2 === '0x') return false // abstract contract. see comment + if (code1 === code2) return true + if (code2 === '0x') return false // abstract contract. see comment - if (code2.substr(2, 46) === '7300000000000000000000000000000000000000003014') { - // testing the following signature: PUSH20 00..00 ADDRESS EQ - // in the context of a library, that slot contains the address of the library (pushed by the compiler to avoid calling library other than with a DELEGATECALL) - // if code2 is not a library, well we still suppose that the comparison remain relevant even if we remove some information from `code1` - code1 = replaceLibReference(code1, 4) - } - let pos = -1 - while ((pos = code2.search(/__(.*)__/)) !== -1) { - code2 = replaceLibReference(code2, pos) - code1 = replaceLibReference(code1, pos) - } + if (code2.substr(2, 46) === '7300000000000000000000000000000000000000003014') { + // testing the following signature: PUSH20 00..00 ADDRESS EQ + // in the context of a library, that slot contains the address of the library (pushed by the compiler to avoid calling library other than with a DELEGATECALL) + // if code2 is not a library, well we still suppose that the comparison remain relevant even if we remove some information from `code1` + code1 = replaceLibReference(code1, 4) + } + let pos = -1 + while ((pos = code2.search(/__(.*)__/)) !== -1) { + code2 = replaceLibReference(code2, pos) + code1 = replaceLibReference(code1, pos) + } - code1 = removeImmutableReference(code1, code2) - code1 = extractinputParameters(code1) - code1 = extractSwarmHash(code1) - code1 = extractcborMetadata(code1) - code2 = extractinputParameters(code2) - code2 = extractSwarmHash(code2) - code2 = extractcborMetadata(code2) + code1 = removeImmutableReference(code1, code2) + code1 = extractinputParameters(code1) + code1 = extractSwarmHash(code1) + code1 = extractcborMetadata(code1) + code2 = extractinputParameters(code2) + code2 = extractSwarmHash(code2) + code2 = extractcborMetadata(code2) - if (code1 && code2) { - if (code1.length !== code2.length) { - // if the length isn't the same, we have an issue with extracting the metadata hash. - const minLength = code1.length > code2.length ? code2.length: code1.length - code1 = code1.substr(0, minLength - 10) - code2 = code2.substr(0, minLength - 10) - } - const compare = stringSimilarity.compareTwoStrings(code1, code2) - return compare == 1 + if (code1 && code2) { + if (code1.length !== code2.length) { + // if the length isn't the same, we have an issue with extracting the metadata hash. + const minLength = code1.length > code2.length ? code2.length: code1.length + code1 = code1.substr(0, minLength - 10) + code2 = code2.substr(0, minLength - 10) } + const compare = stringSimilarity.compareTwoStrings(code1, code2) + return compare == 1 + } - return false + return false } /* util extracted out from remix-ide. @TODO split this file, cause it mix real util fn with solidity related stuff ... */ export function groupBy (arr, key) { - return arr.reduce((sum, item) => { - const groupByVal = item[key] - const groupedItems = sum[groupByVal] || [] - groupedItems.push(item) - sum[groupByVal] = groupedItems - return sum - }, {}) + return arr.reduce((sum, item) => { + const groupByVal = item[key] + const groupedItems = sum[groupByVal] || [] + groupedItems.push(item) + sum[groupByVal] = groupedItems + return sum + }, {}) } export function concatWithSeperator (list, seperator) { - return list.reduce((sum, item) => sum + item + seperator, '').slice(0, -seperator.length) + return list.reduce((sum, item) => sum + item + seperator, '').slice(0, -seperator.length) } export function escapeRegExp (str) { - return str.replace(/[-[\]/{}()+?.\\^$|]/g, '\\$&') + return str.replace(/[-[\]/{}()+?.\\^$|]/g, '\\$&') } function replaceLibReference (code, pos) { - return code.substring(0, pos) + '0000000000000000000000000000000000000000' + code.substring(pos + 40) + return code.substring(0, pos) + '0000000000000000000000000000000000000000' + code.substring(pos + 40) } function removeByIndex (code, index, length, emptyRef) { - if (!code) return code - return code.slice(0, index) + emptyRef + code.slice(index + length) + if (!code) return code + return code.slice(0, index) + emptyRef + code.slice(index + length) } function removeImmutableReference (code1, code2) { - try { - const refOccurence = code2.match(/7f0000000000000000000000000000000000000000000000000000000000000000/g) - if (!refOccurence) return code1 - let offset = 0 - refOccurence.map((value) => { - offset = code2.indexOf(value, offset) - code1 = removeByIndex(code1, offset, value.length, '7f0000000000000000000000000000000000000000000000000000000000000000') - offset = offset + 1 - }) - } catch (e) { - console.log('error removeImmutableReference', e) - } - return code1 + try { + const refOccurence = code2.match(/7f0000000000000000000000000000000000000000000000000000000000000000/g) + if (!refOccurence) return code1 + let offset = 0 + refOccurence.map((value) => { + offset = code2.indexOf(value, offset) + code1 = removeByIndex(code1, offset, value.length, '7f0000000000000000000000000000000000000000000000000000000000000000') + offset = offset + 1 + }) + } catch (e) { + console.log('error removeImmutableReference', e) + } + return code1 } function findCallInternal (index, rootCall, callsPath) { - const calls = Object.keys(rootCall.calls) - const ret = rootCall - callsPath.push(rootCall) - for (const k in calls) { - const subCall = rootCall.calls[calls[k]] - if (index >= subCall.start && index <= subCall.return) { - findCallInternal(index, subCall, callsPath) - break - } + const calls = Object.keys(rootCall.calls) + const ret = rootCall + callsPath.push(rootCall) + for (const k in calls) { + const subCall = rootCall.calls[calls[k]] + if (index >= subCall.start && index <= subCall.return) { + findCallInternal(index, subCall, callsPath) + break } - return ret + } + return ret } diff --git a/libs/remix-lib/test/eventManager.ts b/libs/remix-lib/test/eventManager.ts index 3caa7a2e0f..d5a2f8bd7f 100644 --- a/libs/remix-lib/test/eventManager.ts +++ b/libs/remix-lib/test/eventManager.ts @@ -2,34 +2,34 @@ import tape from 'tape' import { EventManager } from '../src/eventManager' tape('eventManager', function (t) { - t.test('eventManager', function (st) { - const events = new EventManager() - const listenner = {} + t.test('eventManager', function (st) { + const events = new EventManager() + const listenner = {} - let trace = '' - listenner['listen'] = function (data1) { - trace += data1 - } - const registeredFunction = function (data) { - trace += data - } - events.register('event1', listenner, listenner['listen']) - events.register('event2', registeredFunction, null) - events.trigger('event1', ['event1']) - events.trigger('event2', ['event2']) - st.equal(trace, 'event1event2') + let trace = '' + listenner['listen'] = function (data1) { + trace += data1 + } + const registeredFunction = function (data) { + trace += data + } + events.register('event1', listenner, listenner['listen']) + events.register('event2', registeredFunction, null) + events.trigger('event1', ['event1']) + events.trigger('event2', ['event2']) + st.equal(trace, 'event1event2') - events.unregister('event1', listenner['listen'], null) - st.equal(events.registered['event1'].length, 1) - st.equal(events.registered['event2'].length, 1) + events.unregister('event1', listenner['listen'], null) + st.equal(events.registered['event1'].length, 1) + st.equal(events.registered['event2'].length, 1) - events.unregister('event1', listenner, listenner['listen']) - st.equal(events.registered['event1'].length, 0) - st.equal(events.registered['event2'].length, 1) + events.unregister('event1', listenner, listenner['listen']) + st.equal(events.registered['event1'].length, 0) + st.equal(events.registered['event2'].length, 1) - events.unregister('event2', registeredFunction, null) - st.equal(events.registered['event1'].length, 0) - st.equal(events.registered['event2'].length, 0) - st.end() - }) + events.unregister('event2', registeredFunction, null) + st.equal(events.registered['event1'].length, 0) + st.equal(events.registered['event2'].length, 0) + st.end() + }) }) diff --git a/libs/remix-lib/test/init.ts b/libs/remix-lib/test/init.ts index 7f844e234f..4f5b8a3897 100644 --- a/libs/remix-lib/test/init.ts +++ b/libs/remix-lib/test/init.ts @@ -1,31 +1,31 @@ const init = { - overrideWeb3: function (web3, web3Override) { - web3.eth.getCode = web3Override.getCode - web3.debug.traceTransaction = web3Override.traceTransaction - web3.debug.storageRangeAt = web3Override.storageRangeAt - web3.eth.getTransaction = web3Override.getTransaction - web3.eth.getTransactionFromBlock = web3Override.getTransactionFromBlock - web3.eth.getBlockNumber = web3Override.getBlockNumber - }, + overrideWeb3: function (web3, web3Override) { + web3.eth.getCode = web3Override.getCode + web3.debug.traceTransaction = web3Override.traceTransaction + web3.debug.storageRangeAt = web3Override.storageRangeAt + web3.eth.getTransaction = web3Override.getTransaction + web3.eth.getTransactionFromBlock = web3Override.getTransactionFromBlock + web3.eth.getBlockNumber = web3Override.getBlockNumber + }, - readFile: function (filename, callback) { - const fs = require('fs') - try { - console.log('reading ' + filename) - if (callback) { - fs.readFile(filename, 'utf8', callback) - } else { - return fs.readFileSync(filename, 'utf8') - } - } catch (e) { - console.log(e) - if (callback) { - callback(e) - } else { - return e - } - } + readFile: function (filename, callback) { + const fs = require('fs') + try { + console.log('reading ' + filename) + if (callback) { + fs.readFile(filename, 'utf8', callback) + } else { + return fs.readFileSync(filename, 'utf8') + } + } catch (e) { + console.log(e) + if (callback) { + callback(e) + } else { + return e + } } + } } module.exports = init diff --git a/libs/remix-lib/test/txFormat.ts b/libs/remix-lib/test/txFormat.ts index 4203d452e5..8d3deb69b4 100644 --- a/libs/remix-lib/test/txFormat.ts +++ b/libs/remix-lib/test/txFormat.ts @@ -9,321 +9,321 @@ const solidityVersion = 'v0.6.0+commit.26b70077' /* tape *********************************************************** */ tape('load compiler ' + solidityVersion, function (t) { - compiler.loadRemoteVersion(solidityVersion, (error, solcSnapshot) => { - if (error) console.log(error) - console.warn('testing *txFormat* against', solidityVersion) - compiler = solcSnapshot - t.end() - }) + compiler.loadRemoteVersion(solidityVersion, (error, solcSnapshot) => { + if (error) console.log(error) + console.warn('testing *txFormat* against', solidityVersion) + compiler = solcSnapshot + t.end() + }) }) let context tape('ContractParameters - (TxFormat.buildData) - format input parameters', function (t) { - let output = compiler.compile(compilerInput(uintContract)) - output = JSON.parse(output) - const contract = output.contracts['test.sol']['uintContractTest'] - context = { output, contract } - - t.test('(TxFormat.buildData)', function (st) { - st.plan(3) - testWithInput(st, '123123, "0xf7a10e525d4b168f45f74db1b61f63d3e7619ea8", "34"', '000000000000000000000000000000000000000000000000000000000001e0f3000000000000000000000000f7a10e525d4b168f45f74db1b61f63d3e7619ea80000000000000000000000000000000000000000000000000000000000000022') - testWithInput(st, '"123123" , 0xf7a10e525d4b168f45f74db1b61f63d3e7619ea8, 654 ', '000000000000000000000000000000000000000000000000000000000001e0f3000000000000000000000000f7a10e525d4b168f45f74db1b61f63d3e7619ea8000000000000000000000000000000000000000000000000000000000000028e') - // parsing this as javascript number should overflow - testWithInput(st, '90071992547409910000, 0xf7a10e525d4b168f45f74db1b61f63d3e7619ea8, 0', '000000000000000000000000000000000000000000000004e1ffffffffffd8f0000000000000000000000000f7a10e525d4b168f45f74db1b61f63d3e7619ea80000000000000000000000000000000000000000000000000000000000000000') - }) + let output = compiler.compile(compilerInput(uintContract)) + output = JSON.parse(output) + const contract = output.contracts['test.sol']['uintContractTest'] + context = { output, contract } + + t.test('(TxFormat.buildData)', function (st) { + st.plan(3) + testWithInput(st, '123123, "0xf7a10e525d4b168f45f74db1b61f63d3e7619ea8", "34"', '000000000000000000000000000000000000000000000000000000000001e0f3000000000000000000000000f7a10e525d4b168f45f74db1b61f63d3e7619ea80000000000000000000000000000000000000000000000000000000000000022') + testWithInput(st, '"123123" , 0xf7a10e525d4b168f45f74db1b61f63d3e7619ea8, 654 ', '000000000000000000000000000000000000000000000000000000000001e0f3000000000000000000000000f7a10e525d4b168f45f74db1b61f63d3e7619ea8000000000000000000000000000000000000000000000000000000000000028e') + // parsing this as javascript number should overflow + testWithInput(st, '90071992547409910000, 0xf7a10e525d4b168f45f74db1b61f63d3e7619ea8, 0', '000000000000000000000000000000000000000000000004e1ffffffffffd8f0000000000000000000000000f7a10e525d4b168f45f74db1b61f63d3e7619ea80000000000000000000000000000000000000000000000000000000000000000') + }) }) function testWithInput (st, params, expected) { - txFormat.buildData('uintContractTest', context.contract, context.output.contracts, true, context.contract.abi[0], params, (error, data) => { - if (error) { return st.fail(error) } - console.log(data) - if (!data.dataHex.endsWith(expected)) { - st.fail(`result of buildData ${data.dataHex} should end with ${expected} . `) - } else { - st.pass(`testWithInput. result of buildData ${data.dataHex} ends with correct data`) - } - }, () => {}, () => {}) + txFormat.buildData('uintContractTest', context.contract, context.output.contracts, true, context.contract.abi[0], params, (error, data) => { + if (error) { return st.fail(error) } + console.log(data) + if (!data.dataHex.endsWith(expected)) { + st.fail(`result of buildData ${data.dataHex} should end with ${expected} . `) + } else { + st.pass(`testWithInput. result of buildData ${data.dataHex} ends with correct data`) + } + }, () => {}, () => {}) } tape('ContractStringParameters - (TxFormat.buildData) - format string input parameters', function (t) { - let output = compiler.compile(compilerInput(stringContract)) - output = JSON.parse(output) - const contract = output.contracts['test.sol']['stringContractTest'] - context = { output, contract } - t.test('(TxFormat.buildData)', function (st) { - st.plan(3) - testWithStringInput(st, '"1,2,3,4qwerty,5", 0xf7a10e525d4b168f45f74db1b61f63d3e7619ea8, "1,a,5,34"', '0000000000000000000000000000000000000000000000000000000000000060000000000000000000000000f7a10e525d4b168f45f74db1b61f63d3e7619ea800000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000000f312c322c332c347177657274792c3500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008312c612c352c3334000000000000000000000000000000000000000000000000') - testWithStringInput(st, '"1,2,3,4qwerty,5", "0xf7a10e525d4b168f45f74db1b61f63d3e7619ea8", "1,a,5,34"', '0000000000000000000000000000000000000000000000000000000000000060000000000000000000000000f7a10e525d4b168f45f74db1b61f63d3e7619ea800000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000000f312c322c332c347177657274792c3500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008312c612c352c3334000000000000000000000000000000000000000000000000') - // string with space - testWithStringInput(st, '"1,2,3,,4qw erty,5", "0xf7a10e525d4b168f45f74db1b61f63d3e7619ea8", "abcdefghijkl"', '0000000000000000000000000000000000000000000000000000000000000060000000000000000000000000f7a10e525d4b168f45f74db1b61f63d3e7619ea800000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000012312c322c332c2c3471772020657274792c350000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c6162636465666768696a6b6c0000000000000000000000000000000000000000') - }) + let output = compiler.compile(compilerInput(stringContract)) + output = JSON.parse(output) + const contract = output.contracts['test.sol']['stringContractTest'] + context = { output, contract } + t.test('(TxFormat.buildData)', function (st) { + st.plan(3) + testWithStringInput(st, '"1,2,3,4qwerty,5", 0xf7a10e525d4b168f45f74db1b61f63d3e7619ea8, "1,a,5,34"', '0000000000000000000000000000000000000000000000000000000000000060000000000000000000000000f7a10e525d4b168f45f74db1b61f63d3e7619ea800000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000000f312c322c332c347177657274792c3500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008312c612c352c3334000000000000000000000000000000000000000000000000') + testWithStringInput(st, '"1,2,3,4qwerty,5", "0xf7a10e525d4b168f45f74db1b61f63d3e7619ea8", "1,a,5,34"', '0000000000000000000000000000000000000000000000000000000000000060000000000000000000000000f7a10e525d4b168f45f74db1b61f63d3e7619ea800000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000000f312c322c332c347177657274792c3500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008312c612c352c3334000000000000000000000000000000000000000000000000') + // string with space + testWithStringInput(st, '"1,2,3,,4qw erty,5", "0xf7a10e525d4b168f45f74db1b61f63d3e7619ea8", "abcdefghijkl"', '0000000000000000000000000000000000000000000000000000000000000060000000000000000000000000f7a10e525d4b168f45f74db1b61f63d3e7619ea800000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000012312c322c332c2c3471772020657274792c350000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c6162636465666768696a6b6c0000000000000000000000000000000000000000') + }) }) function testWithStringInput (st, params, expected) { - txFormat.buildData('stringContractTest', context.contract, context.output.contracts, true, context.contract.abi[0], params, (error, data) => { - if (error) { return st.fail(error) } - console.log(data) - if (!data.dataHex.endsWith(expected)) { - st.fail(`result of buildData ${data.dataHex} should end with ${expected} . `) - } else { - st.pass(`testWithStringInput. result of buildData ${data.dataHex} ends with correct data`) - } - }, () => {}, () => {}) + txFormat.buildData('stringContractTest', context.contract, context.output.contracts, true, context.contract.abi[0], params, (error, data) => { + if (error) { return st.fail(error) } + console.log(data) + if (!data.dataHex.endsWith(expected)) { + st.fail(`result of buildData ${data.dataHex} should end with ${expected} . `) + } else { + st.pass(`testWithStringInput. result of buildData ${data.dataHex} ends with correct data`) + } + }, () => {}, () => {}) } tape('ContractArrayParameters - (TxFormat.buildData) - format array input parameters', function (t) { - let output = compiler.compile(compilerInput(arrayContract)) - output = JSON.parse(output) - const contract = output.contracts['test.sol']['arrayContractTest'] - context = { output, contract } - t.test('(TxFormat.buildData)', function (st) { - st.plan(3) - testWithArrayInput(st, '[true, false, true], ["0xf7a10e525d4b168f45f74db1b61f63d3e7619ea8", "0xf7a10e525d4b168f45f74db1b61f63d3e7619ea8"], ["0x0c5d9661b4fb92eb7472f28510ea68d4f369c8fe57b3ed4c2e8dfa4e79e549fd", "0x0c5d9661b4fb92eb7472f28510ea68d4f369c8fe57b3ed4c2e8dfa4e79e549fd"], [12, 34, 45], "itsremix"', '00000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000018000000000000000000000000000000000000000000000000000000000000001e0000000000000000000000000000000000000000000000000000000000000026000000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002000000000000000000000000f7a10e525d4b168f45f74db1b61f63d3e7619ea8000000000000000000000000f7a10e525d4b168f45f74db1b61f63d3e7619ea800000000000000000000000000000000000000000000000000000000000000020c5d9661b4fb92eb7472f28510ea68d4f369c8fe57b3ed4c2e8dfa4e79e549fd0c5d9661b4fb92eb7472f28510ea68d4f369c8fe57b3ed4c2e8dfa4e79e549fd0000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000022000000000000000000000000000000000000000000000000000000000000002d000000000000000000000000000000000000000000000000000000000000000869747372656d6978000000000000000000000000000000000000000000000000') - testWithArrayInput(st, '[true, false, true], ["0xf7a10e525d4b168f45f74db1b61f63d3e7619ea8", "0xf7a10e525d4b168f45f74db1b61f63d3e7619ea8"], ["0x0c5d9661b4fb92eb7472f28510ea68d4f369c8fe57b3ed4c2e8dfa4e79e549fd", "0x0c5d9661b4fb92eb7472f28510ea68d4f369c8fe57b3ed4c2e8dfa4e79e549fd"], ["12", "34", "45"], "itsremix"', '00000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000018000000000000000000000000000000000000000000000000000000000000001e0000000000000000000000000000000000000000000000000000000000000026000000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002000000000000000000000000f7a10e525d4b168f45f74db1b61f63d3e7619ea8000000000000000000000000f7a10e525d4b168f45f74db1b61f63d3e7619ea800000000000000000000000000000000000000000000000000000000000000020c5d9661b4fb92eb7472f28510ea68d4f369c8fe57b3ed4c2e8dfa4e79e549fd0c5d9661b4fb92eb7472f28510ea68d4f369c8fe57b3ed4c2e8dfa4e79e549fd0000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000022000000000000000000000000000000000000000000000000000000000000002d000000000000000000000000000000000000000000000000000000000000000869747372656d6978000000000000000000000000000000000000000000000000') - // with complex string containing comma, space and underscore - testWithArrayInput(st, '[true, false, true], ["0xf7a10e525d4b168f45f74db1b61f63d3e7619ea8", "0xf7a10e525d4b168f45f74db1b61f63d3e7619ea8"], ["0x0c5d9661b4fb92eb7472f28510ea68d4f369c8fe57b3ed4c2e8dfa4e79e549fd", "0x0c5d9661b4fb92eb7472f28510ea68d4f369c8fe57b3ed4c2e8dfa4e79e549fd"], ["12", "34", "45"], "its _ re, m,ix"', '00000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000018000000000000000000000000000000000000000000000000000000000000001e0000000000000000000000000000000000000000000000000000000000000026000000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002000000000000000000000000f7a10e525d4b168f45f74db1b61f63d3e7619ea8000000000000000000000000f7a10e525d4b168f45f74db1b61f63d3e7619ea800000000000000000000000000000000000000000000000000000000000000020c5d9661b4fb92eb7472f28510ea68d4f369c8fe57b3ed4c2e8dfa4e79e549fd0c5d9661b4fb92eb7472f28510ea68d4f369c8fe57b3ed4c2e8dfa4e79e549fd0000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000022000000000000000000000000000000000000000000000000000000000000002d000000000000000000000000000000000000000000000000000000000000001069747320205f202072652c206d2c697800000000000000000000000000000000') - }) + let output = compiler.compile(compilerInput(arrayContract)) + output = JSON.parse(output) + const contract = output.contracts['test.sol']['arrayContractTest'] + context = { output, contract } + t.test('(TxFormat.buildData)', function (st) { + st.plan(3) + testWithArrayInput(st, '[true, false, true], ["0xf7a10e525d4b168f45f74db1b61f63d3e7619ea8", "0xf7a10e525d4b168f45f74db1b61f63d3e7619ea8"], ["0x0c5d9661b4fb92eb7472f28510ea68d4f369c8fe57b3ed4c2e8dfa4e79e549fd", "0x0c5d9661b4fb92eb7472f28510ea68d4f369c8fe57b3ed4c2e8dfa4e79e549fd"], [12, 34, 45], "itsremix"', '00000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000018000000000000000000000000000000000000000000000000000000000000001e0000000000000000000000000000000000000000000000000000000000000026000000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002000000000000000000000000f7a10e525d4b168f45f74db1b61f63d3e7619ea8000000000000000000000000f7a10e525d4b168f45f74db1b61f63d3e7619ea800000000000000000000000000000000000000000000000000000000000000020c5d9661b4fb92eb7472f28510ea68d4f369c8fe57b3ed4c2e8dfa4e79e549fd0c5d9661b4fb92eb7472f28510ea68d4f369c8fe57b3ed4c2e8dfa4e79e549fd0000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000022000000000000000000000000000000000000000000000000000000000000002d000000000000000000000000000000000000000000000000000000000000000869747372656d6978000000000000000000000000000000000000000000000000') + testWithArrayInput(st, '[true, false, true], ["0xf7a10e525d4b168f45f74db1b61f63d3e7619ea8", "0xf7a10e525d4b168f45f74db1b61f63d3e7619ea8"], ["0x0c5d9661b4fb92eb7472f28510ea68d4f369c8fe57b3ed4c2e8dfa4e79e549fd", "0x0c5d9661b4fb92eb7472f28510ea68d4f369c8fe57b3ed4c2e8dfa4e79e549fd"], ["12", "34", "45"], "itsremix"', '00000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000018000000000000000000000000000000000000000000000000000000000000001e0000000000000000000000000000000000000000000000000000000000000026000000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002000000000000000000000000f7a10e525d4b168f45f74db1b61f63d3e7619ea8000000000000000000000000f7a10e525d4b168f45f74db1b61f63d3e7619ea800000000000000000000000000000000000000000000000000000000000000020c5d9661b4fb92eb7472f28510ea68d4f369c8fe57b3ed4c2e8dfa4e79e549fd0c5d9661b4fb92eb7472f28510ea68d4f369c8fe57b3ed4c2e8dfa4e79e549fd0000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000022000000000000000000000000000000000000000000000000000000000000002d000000000000000000000000000000000000000000000000000000000000000869747372656d6978000000000000000000000000000000000000000000000000') + // with complex string containing comma, space and underscore + testWithArrayInput(st, '[true, false, true], ["0xf7a10e525d4b168f45f74db1b61f63d3e7619ea8", "0xf7a10e525d4b168f45f74db1b61f63d3e7619ea8"], ["0x0c5d9661b4fb92eb7472f28510ea68d4f369c8fe57b3ed4c2e8dfa4e79e549fd", "0x0c5d9661b4fb92eb7472f28510ea68d4f369c8fe57b3ed4c2e8dfa4e79e549fd"], ["12", "34", "45"], "its _ re, m,ix"', '00000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000018000000000000000000000000000000000000000000000000000000000000001e0000000000000000000000000000000000000000000000000000000000000026000000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002000000000000000000000000f7a10e525d4b168f45f74db1b61f63d3e7619ea8000000000000000000000000f7a10e525d4b168f45f74db1b61f63d3e7619ea800000000000000000000000000000000000000000000000000000000000000020c5d9661b4fb92eb7472f28510ea68d4f369c8fe57b3ed4c2e8dfa4e79e549fd0c5d9661b4fb92eb7472f28510ea68d4f369c8fe57b3ed4c2e8dfa4e79e549fd0000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000022000000000000000000000000000000000000000000000000000000000000002d000000000000000000000000000000000000000000000000000000000000001069747320205f202072652c206d2c697800000000000000000000000000000000') + }) }) function testWithArrayInput (st, params, expected) { - txFormat.buildData('arrayContractTest', context.contract, context.output.contracts, true, context.contract.abi[0], params, (error, data) => { - if (error) { return st.fail(error) } - console.log(data) - if (!data.dataHex.endsWith(expected)) { - st.fail(`result of buildData ${data.dataHex} should end with ${expected} . `) - } else { - st.pass(`testWithArrayInput. result of buildData ${data.dataHex} ends with correct data`) - } - }, () => {}, () => {}) + txFormat.buildData('arrayContractTest', context.contract, context.output.contracts, true, context.contract.abi[0], params, (error, data) => { + if (error) { return st.fail(error) } + console.log(data) + if (!data.dataHex.endsWith(expected)) { + st.fail(`result of buildData ${data.dataHex} should end with ${expected} . `) + } else { + st.pass(`testWithArrayInput. result of buildData ${data.dataHex} ends with correct data`) + } + }, () => {}, () => {}) } tape('ContractNestedArrayParameters - (TxFormat.buildData) - format nested array input parameters', function (t) { - let output = compiler.compile(compilerInput(nestedArrayContract)) - output = JSON.parse(output) - const contract = output.contracts['test.sol']['nestedArrayContractTest'] - context = { output, contract } - t.test('(TxFormat.buildData)', function (st) { - st.plan(2) - testWithNestedArrayInput(st, '[[true],[false]] , [ [[1,2],[3,4],[5,6]], [[1,2],[3,4],[5,6]], [ [1,2],[3,4],[5,6]] ], "ab ab, a,b", 145', '0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000005000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000005000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000005000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000002c00000000000000000000000000000000000000000000000000000000000000091000000000000000000000000000000000000000000000000000000000000000a61622061622c20612c6200000000000000000000000000000000000000000000') - testWithNestedArrayInput(st, '[[true],[false]] , [ [["1","2"],["3","4"],["5","6"]], [ ["1","2"],["3","4"],["5","6"]], [ ["1","2"],["3","4"],["5","6"]] ], "ab ab, a,b", "145"', '0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000005000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000005000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000005000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000002c00000000000000000000000000000000000000000000000000000000000000091000000000000000000000000000000000000000000000000000000000000000a61622061622c20612c6200000000000000000000000000000000000000000000') - }) + let output = compiler.compile(compilerInput(nestedArrayContract)) + output = JSON.parse(output) + const contract = output.contracts['test.sol']['nestedArrayContractTest'] + context = { output, contract } + t.test('(TxFormat.buildData)', function (st) { + st.plan(2) + testWithNestedArrayInput(st, '[[true],[false]] , [ [[1,2],[3,4],[5,6]], [[1,2],[3,4],[5,6]], [ [1,2],[3,4],[5,6]] ], "ab ab, a,b", 145', '0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000005000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000005000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000005000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000002c00000000000000000000000000000000000000000000000000000000000000091000000000000000000000000000000000000000000000000000000000000000a61622061622c20612c6200000000000000000000000000000000000000000000') + testWithNestedArrayInput(st, '[[true],[false]] , [ [["1","2"],["3","4"],["5","6"]], [ ["1","2"],["3","4"],["5","6"]], [ ["1","2"],["3","4"],["5","6"]] ], "ab ab, a,b", "145"', '0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000005000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000005000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000005000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000002c00000000000000000000000000000000000000000000000000000000000000091000000000000000000000000000000000000000000000000000000000000000a61622061622c20612c6200000000000000000000000000000000000000000000') + }) }) function testWithNestedArrayInput (st, params, expected) { - txFormat.buildData('nestedArrayContractTest', context.contract, context.output.contracts, true, context.contract.abi[4], params, (error, data) => { - if (error) { - return st.fail(error) - } - console.log(data) - if (!data.dataHex.endsWith(expected)) { - st.fail(`result of buildData ${data.dataHex} should end with ${expected} . `) - } else { - st.pass(`testWithNestedArrayInput. result of buildData ${data.dataHex} ends with correct data`) - } - }, () => {}, () => {}) + txFormat.buildData('nestedArrayContractTest', context.contract, context.output.contracts, true, context.contract.abi[4], params, (error, data) => { + if (error) { + return st.fail(error) + } + console.log(data) + if (!data.dataHex.endsWith(expected)) { + st.fail(`result of buildData ${data.dataHex} should end with ${expected} . `) + } else { + st.pass(`testWithNestedArrayInput. result of buildData ${data.dataHex} ends with correct data`) + } + }, () => {}, () => {}) } tape('abiEncoderV2InvalidTuple - (TxFormat.buildData) - should throw error for invalid tuple value', function (t) { - let output = compiler.compile(compilerInput(abiEncoderV2InvalidTuple)) - output = JSON.parse(output) - const contract = output.contracts['test.sol']['test'] - context = { output, contract } - t.test('(TxFormat.buildData)', function (st) { - st.plan(4) - testInvalidTupleInput(st, '[11, 12, "13"') - testInvalidTupleInput(st, '[11, 12, 13') - testInvalidTupleInput(st, '[11, 12, "13') - testInvalidTupleInput(st, '[11, 12, 13"') - }) + let output = compiler.compile(compilerInput(abiEncoderV2InvalidTuple)) + output = JSON.parse(output) + const contract = output.contracts['test.sol']['test'] + context = { output, contract } + t.test('(TxFormat.buildData)', function (st) { + st.plan(4) + testInvalidTupleInput(st, '[11, 12, "13"') + testInvalidTupleInput(st, '[11, 12, 13') + testInvalidTupleInput(st, '[11, 12, "13') + testInvalidTupleInput(st, '[11, 12, 13"') + }) }) function testInvalidTupleInput (st, params) { - txFormat.buildData('abiEncoderV2InvalidTuple', context.contract, context.output.contracts, true, context.contract.abi[2], params, (error, data) => { - if (error) { - return st.ok(error.includes('Error encoding arguments: Error: invalid tuple params'), 'should fail because of invalid tuple input') - } - }, () => {}, () => {}) + txFormat.buildData('abiEncoderV2InvalidTuple', context.contract, context.output.contracts, true, context.contract.abi[2], params, (error, data) => { + if (error) { + return st.ok(error.includes('Error encoding arguments: Error: invalid tuple params'), 'should fail because of invalid tuple input') + } + }, () => {}, () => {}) } /* tape *********************************************************** */ tape('ContractParameters - (TxFormat.buildData) - link Libraries', function (t) { - const compileData = compiler.compile(compilerInput(deploySimpleLib)) + const compileData = compiler.compile(compilerInput(deploySimpleLib)) - const fakeDeployedContracts = { - lib1: '0xf7a10e525d4b168f45f74db1b61f63d3e7619e11', - lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2: '0xf7a10e525d4b168f45f74db1b61f63d3e7619e33', - testContractLinkLibrary: '0xf7a10e525d4b168f45f74db1b61f63d3e7619e22' - } - const callbackDeployLibraries = (param, callback) => { - callback(null, { - receipt: { - contractAddress: fakeDeployedContracts[param.data.contractName] - } - }) - } // fake - - t.test('(TxFormat.buildData and link library (standard way))', function (st) { - st.plan(6) - const output = JSON.parse(compileData) - const contract = output.contracts['test.sol']['testContractLinkLibrary'] - context = { output, contract } - testLinkLibrary(st, fakeDeployedContracts, callbackDeployLibraries) + const fakeDeployedContracts = { + lib1: '0xf7a10e525d4b168f45f74db1b61f63d3e7619e11', + lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2: '0xf7a10e525d4b168f45f74db1b61f63d3e7619e33', + testContractLinkLibrary: '0xf7a10e525d4b168f45f74db1b61f63d3e7619e22' + } + const callbackDeployLibraries = (param, callback) => { + callback(null, { + receipt: { + contractAddress: fakeDeployedContracts[param.data.contractName] + } }) + } // fake - t.test('(TxFormat.encodeConstructorCallAndLinkLibraries and link library (standard way))', function (st) { - st.plan(12) - const output = JSON.parse(compileData) - const contract = output.contracts['test.sol']['testContractLinkLibrary'] - context = { output, contract } - testLinkLibrary2(st, callbackDeployLibraries) - }) + t.test('(TxFormat.buildData and link library (standard way))', function (st) { + st.plan(6) + const output = JSON.parse(compileData) + const contract = output.contracts['test.sol']['testContractLinkLibrary'] + context = { output, contract } + testLinkLibrary(st, fakeDeployedContracts, callbackDeployLibraries) + }) + + t.test('(TxFormat.encodeConstructorCallAndLinkLibraries and link library (standard way))', function (st) { + st.plan(12) + const output = JSON.parse(compileData) + const contract = output.contracts['test.sol']['testContractLinkLibrary'] + context = { output, contract } + testLinkLibrary2(st, callbackDeployLibraries) + }) }) function testLinkLibrary (st, fakeDeployedContracts, callbackDeployLibraries) { - const deployMsg = ['creation of library test.sol:lib1 pending...', - 'creation of library test.sol:lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2 pending...'] - txFormat.buildData('testContractLinkLibrary', context.contract, context.output.contracts, true, context.contract.abi[0], '', (error, data) => { - if (error) { return st.fail(error) } - console.log(data) - const linkedbyteCode = data.dataHex - let libReference = context.contract.evm.bytecode.linkReferences['test.sol']['lib1'] - st.equal(linkedbyteCode.substr(2 * libReference[0].start, 40), fakeDeployedContracts['lib1'].replace('0x', '')) - st.equal(linkedbyteCode.substr(2 * libReference[1].start, 40), fakeDeployedContracts['lib1'].replace('0x', '')) - - libReference = context.contract.evm.bytecode.linkReferences['test.sol']['lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2'] - st.equal(linkedbyteCode.substr(2 * libReference[0].start, 40), fakeDeployedContracts['lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2'].replace('0x', '')) - st.equal(linkedbyteCode.substr(2 * libReference[1].start, 40), fakeDeployedContracts['lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2'].replace('0x', '')) - }, (msg) => { - st.equal(msg, deployMsg[0]) - deployMsg.shift() - }, callbackDeployLibraries) + const deployMsg = ['creation of library test.sol:lib1 pending...', + 'creation of library test.sol:lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2 pending...'] + txFormat.buildData('testContractLinkLibrary', context.contract, context.output.contracts, true, context.contract.abi[0], '', (error, data) => { + if (error) { return st.fail(error) } + console.log(data) + const linkedbyteCode = data.dataHex + let libReference = context.contract.evm.bytecode.linkReferences['test.sol']['lib1'] + st.equal(linkedbyteCode.substr(2 * libReference[0].start, 40), fakeDeployedContracts['lib1'].replace('0x', '')) + st.equal(linkedbyteCode.substr(2 * libReference[1].start, 40), fakeDeployedContracts['lib1'].replace('0x', '')) + + libReference = context.contract.evm.bytecode.linkReferences['test.sol']['lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2'] + st.equal(linkedbyteCode.substr(2 * libReference[0].start, 40), fakeDeployedContracts['lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2'].replace('0x', '')) + st.equal(linkedbyteCode.substr(2 * libReference[1].start, 40), fakeDeployedContracts['lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2'].replace('0x', '')) + }, (msg) => { + st.equal(msg, deployMsg[0]) + deployMsg.shift() + }, callbackDeployLibraries) } function testLinkLibrary2 (st, callbackDeployLibraries) { - const librariesReference = { - 'test.sol': { - 'lib1': '0xf7a10e525d4b168f45f74db1b61f63d3e7619e11', - 'lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2': '0xf7a10e525d4b168f45f74db1b61f63d3e7619e33' - } + const librariesReference = { + 'test.sol': { + 'lib1': '0xf7a10e525d4b168f45f74db1b61f63d3e7619e11', + 'lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2': '0xf7a10e525d4b168f45f74db1b61f63d3e7619e33' } + } - const data = '608060405234801561001057600080fd5b506101e2806100206000396000f3fe608060405234801561001057600080fd5b506004361061002b5760003560e01c80636d4ce63c14610030575b600080fd5b61003861003a565b005b73f7a10e525d4b168f45f74db1b61f63d3e7619e116344733ae16040518163ffffffff1660e01b815260040160006040518083038186803b15801561007e57600080fd5b505af4158015610092573d6000803e3d6000fd5b5050505073f7a10e525d4b168f45f74db1b61f63d3e7619e336344733ae16040518163ffffffff1660e01b815260040160006040518083038186803b1580156100da57600080fd5b505af41580156100ee573d6000803e3d6000fd5b5050505073f7a10e525d4b168f45f74db1b61f63d3e7619e336344733ae16040518163ffffffff1660e01b815260040160006040518083038186803b15801561013657600080fd5b505af415801561014a573d6000803e3d6000fd5b5050505073f7a10e525d4b168f45f74db1b61f63d3e7619e116344733ae16040518163ffffffff1660e01b815260040160006040518083038186803b15801561019257600080fd5b505af41580156101a6573d6000803e3d6000fd5b5050505056fea264697066735822122007784c53df7f324243100f6642d889a08a88831c3811dd13eebe3163b7eb2e5464736f6c63430006000033' - - const deployMsg = ['creation of library test.sol:lib1 pending...', - 'creation of library test.sol:lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2 pending...'] - txFormat.encodeConstructorCallAndLinkLibraries(context.contract, '', context.contract.abi[0], librariesReference, context.contract.evm.bytecode.linkReferences, (error, result) => { - console.log(error, result) - st.equal(data, result.dataHex) - const linkedbyteCode = result.dataHex - let libReference = context.contract.evm.bytecode.linkReferences['test.sol']['lib1'] - st.equal(linkedbyteCode.substr(2 * libReference[0].start, 40), librariesReference['test.sol']['lib1'].replace('0x', '')) - st.equal(linkedbyteCode.substr(2 * libReference[1].start, 40), librariesReference['test.sol']['lib1'].replace('0x', '')) - libReference = context.contract.evm.bytecode.linkReferences['test.sol']['lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2'] - st.equal(linkedbyteCode.substr(2 * libReference[0].start, 40), librariesReference['test.sol']['lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2'].replace('0x', '')) - st.equal(linkedbyteCode.substr(2 * libReference[1].start, 40), librariesReference['test.sol']['lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2'].replace('0x', '')) - }) - - txFormat.encodeConstructorCallAndDeployLibraries('testContractLinkLibrary', context.contract, context.output.contracts, '', context.contract.abi[0], (error, result) => { - console.log(error, result) - st.equal(data, result.dataHex) - const linkedbyteCode = result.dataHex - let libReference = context.contract.evm.bytecode.linkReferences['test.sol']['lib1'] - st.equal(linkedbyteCode.substr(2 * libReference[0].start, 40), librariesReference['test.sol']['lib1'].replace('0x', '')) - st.equal(linkedbyteCode.substr(2 * libReference[1].start, 40), librariesReference['test.sol']['lib1'].replace('0x', '')) - - libReference = context.contract.evm.bytecode.linkReferences['test.sol']['lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2'] - st.equal(linkedbyteCode.substr(2 * libReference[0].start, 40), librariesReference['test.sol']['lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2'].replace('0x', '')) - st.equal(linkedbyteCode.substr(2 * libReference[1].start, 40), librariesReference['test.sol']['lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2'].replace('0x', '')) - }, (msg) => { - st.equal(msg, deployMsg[0]) - deployMsg.shift() - }, callbackDeployLibraries) + const data = '608060405234801561001057600080fd5b506101e2806100206000396000f3fe608060405234801561001057600080fd5b506004361061002b5760003560e01c80636d4ce63c14610030575b600080fd5b61003861003a565b005b73f7a10e525d4b168f45f74db1b61f63d3e7619e116344733ae16040518163ffffffff1660e01b815260040160006040518083038186803b15801561007e57600080fd5b505af4158015610092573d6000803e3d6000fd5b5050505073f7a10e525d4b168f45f74db1b61f63d3e7619e336344733ae16040518163ffffffff1660e01b815260040160006040518083038186803b1580156100da57600080fd5b505af41580156100ee573d6000803e3d6000fd5b5050505073f7a10e525d4b168f45f74db1b61f63d3e7619e336344733ae16040518163ffffffff1660e01b815260040160006040518083038186803b15801561013657600080fd5b505af415801561014a573d6000803e3d6000fd5b5050505073f7a10e525d4b168f45f74db1b61f63d3e7619e116344733ae16040518163ffffffff1660e01b815260040160006040518083038186803b15801561019257600080fd5b505af41580156101a6573d6000803e3d6000fd5b5050505056fea264697066735822122007784c53df7f324243100f6642d889a08a88831c3811dd13eebe3163b7eb2e5464736f6c63430006000033' + + const deployMsg = ['creation of library test.sol:lib1 pending...', + 'creation of library test.sol:lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2 pending...'] + txFormat.encodeConstructorCallAndLinkLibraries(context.contract, '', context.contract.abi[0], librariesReference, context.contract.evm.bytecode.linkReferences, (error, result) => { + console.log(error, result) + st.equal(data, result.dataHex) + const linkedbyteCode = result.dataHex + let libReference = context.contract.evm.bytecode.linkReferences['test.sol']['lib1'] + st.equal(linkedbyteCode.substr(2 * libReference[0].start, 40), librariesReference['test.sol']['lib1'].replace('0x', '')) + st.equal(linkedbyteCode.substr(2 * libReference[1].start, 40), librariesReference['test.sol']['lib1'].replace('0x', '')) + libReference = context.contract.evm.bytecode.linkReferences['test.sol']['lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2'] + st.equal(linkedbyteCode.substr(2 * libReference[0].start, 40), librariesReference['test.sol']['lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2'].replace('0x', '')) + st.equal(linkedbyteCode.substr(2 * libReference[1].start, 40), librariesReference['test.sol']['lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2'].replace('0x', '')) + }) + + txFormat.encodeConstructorCallAndDeployLibraries('testContractLinkLibrary', context.contract, context.output.contracts, '', context.contract.abi[0], (error, result) => { + console.log(error, result) + st.equal(data, result.dataHex) + const linkedbyteCode = result.dataHex + let libReference = context.contract.evm.bytecode.linkReferences['test.sol']['lib1'] + st.equal(linkedbyteCode.substr(2 * libReference[0].start, 40), librariesReference['test.sol']['lib1'].replace('0x', '')) + st.equal(linkedbyteCode.substr(2 * libReference[1].start, 40), librariesReference['test.sol']['lib1'].replace('0x', '')) + + libReference = context.contract.evm.bytecode.linkReferences['test.sol']['lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2'] + st.equal(linkedbyteCode.substr(2 * libReference[0].start, 40), librariesReference['test.sol']['lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2'].replace('0x', '')) + st.equal(linkedbyteCode.substr(2 * libReference[1].start, 40), librariesReference['test.sol']['lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2'].replace('0x', '')) + }, (msg) => { + st.equal(msg, deployMsg[0]) + deployMsg.shift() + }, callbackDeployLibraries) } /* tape *********************************************************** */ tape('EncodeParameter', function (t) { - t.test('(TxFormat.encodeFunctionCall)', function (st) { - st.plan(1) - encodeFunctionCallTest(st) - }) + t.test('(TxFormat.encodeFunctionCall)', function (st) { + st.plan(1) + encodeFunctionCallTest(st) + }) }) function encodeFunctionCallTest (st) { - let output = compiler.compile(compilerInput(encodeFunctionCall)) - output = JSON.parse(output) - const contract = output.contracts['test.sol']['testContractLinkLibrary'] - txFormat.encodeFunctionCall('123, "test string"', contract.abi[0], (error, encoded) => { - console.log(error) - st.equal(encoded.dataHex, '0x805da4ad000000000000000000000000000000000000000000000000000000000000007b0000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000b7465737420737472696e67000000000000000000000000000000000000000000') - }) + let output = compiler.compile(compilerInput(encodeFunctionCall)) + output = JSON.parse(output) + const contract = output.contracts['test.sol']['testContractLinkLibrary'] + txFormat.encodeFunctionCall('123, "test string"', contract.abi[0], (error, encoded) => { + console.log(error) + st.equal(encoded.dataHex, '0x805da4ad000000000000000000000000000000000000000000000000000000000000007b0000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000b7465737420737472696e67000000000000000000000000000000000000000000') + }) } /* *********************************************************** */ tape('test fallback & receive function', function (t) { - t.test('(fallback)', function (st) { - st.plan(3) - let output = compiler.compile(compilerInput(fallbackAndReceiveFunction)) - output = JSON.parse(output) - const contract = output.contracts['test.sol']['fallbackAndReceiveFunctionContract'] - st.equal(txHelper.encodeFunctionId(contract.abi[2]), '0x') // for receive function - st.equal(txHelper.encodeFunctionId(contract.abi[1]), '0x805da4ad') - st.equal(txHelper.encodeFunctionId(contract.abi[0]), '0x') // for fallback function - }) + t.test('(fallback)', function (st) { + st.plan(3) + let output = compiler.compile(compilerInput(fallbackAndReceiveFunction)) + output = JSON.parse(output) + const contract = output.contracts['test.sol']['fallbackAndReceiveFunctionContract'] + st.equal(txHelper.encodeFunctionId(contract.abi[2]), '0x') // for receive function + st.equal(txHelper.encodeFunctionId(contract.abi[1]), '0x805da4ad') + st.equal(txHelper.encodeFunctionId(contract.abi[0]), '0x') // for fallback function + }) }) tape('test abiEncoderV2', function (t) { - const functionId = '0x56d89238' - const encodedData = '0x000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000170000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000042ed123b0bd8203c2700000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000090746573745f737472696e675f746573745f737472696e675f746573745f737472696e675f746573745f737472696e675f746573745f737472696e675f746573745f737472696e675f746573745f737472696e675f746573745f737472696e675f746573745f737472696e675f746573745f737472696e675f746573745f737472696e675f746573745f737472696e675f00000000000000000000000000000000' - const value1 = '1' - const value2 = '1234567890123456789543' - const value3 = 'test_string_test_string_test_string_test_string_test_string_test_string_test_string_test_string_test_string_test_string_test_string_test_string_' - const decodedData = `[${value1}, ${value2}, "${value3}"], 23` - t.test('(abiEncoderV2)', function (st) { - st.plan(2) - let output = compiler.compile(compilerInput(abiEncoderV2)) - output = JSON.parse(output) - const contract = output.contracts['test.sol']['test'] - txFormat.encodeFunctionCall(decodedData, contract.abi[0], (error, encoded) => { - console.log(error) - st.equal(encoded.dataHex, functionId + encodedData.replace('0x', '')) - }) - const decoded = txFormat.decodeResponse(hexToIntArray(encodedData), contract.abi[0]) - console.log(decoded) - st.equal(decoded[0], `tuple(uint256,uint256,string): ${value1},${value2},${value3}`) + const functionId = '0x56d89238' + const encodedData = '0x000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000170000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000042ed123b0bd8203c2700000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000090746573745f737472696e675f746573745f737472696e675f746573745f737472696e675f746573745f737472696e675f746573745f737472696e675f746573745f737472696e675f746573745f737472696e675f746573745f737472696e675f746573745f737472696e675f746573745f737472696e675f746573745f737472696e675f746573745f737472696e675f00000000000000000000000000000000' + const value1 = '1' + const value2 = '1234567890123456789543' + const value3 = 'test_string_test_string_test_string_test_string_test_string_test_string_test_string_test_string_test_string_test_string_test_string_test_string_' + const decodedData = `[${value1}, ${value2}, "${value3}"], 23` + t.test('(abiEncoderV2)', function (st) { + st.plan(2) + let output = compiler.compile(compilerInput(abiEncoderV2)) + output = JSON.parse(output) + const contract = output.contracts['test.sol']['test'] + txFormat.encodeFunctionCall(decodedData, contract.abi[0], (error, encoded) => { + console.log(error) + st.equal(encoded.dataHex, functionId + encodedData.replace('0x', '')) }) + const decoded = txFormat.decodeResponse(hexToIntArray(encodedData), contract.abi[0]) + console.log(decoded) + st.equal(decoded[0], `tuple(uint256,uint256,string): ${value1},${value2},${value3}`) + }) }) tape('test abiEncoderV2 array of tuple', function (t) { - t.test('(abiEncoderV2)', function (st) { - /* + t.test('(abiEncoderV2)', function (st) { + /* { "685e37ad": "addStructs((uint256,string))", "e5cb65f9": "addStructs((uint256,string)[])" } */ - st.plan(2) - - let output = compiler.compile(compilerInput(abiEncoderV2ArrayOfTuple)) - output = JSON.parse(output) - const contract = output.contracts['test.sol']['test'] - txFormat.encodeParams('[34, "test"]', contract.abi[1], (error, encoded) => { - console.log(error) - const decoded = txFormat.decodeResponse(hexToIntArray(encoded.dataHex), contract.abi[1]) - console.log(decoded) - st.equal(decoded[0], 'tuple(uint256,string): _strucmts 34,test') - }) - - txFormat.encodeParams('[[34, "test"], [123, "test2"]]', contract.abi[2], (error, encoded) => { - console.log(error) - const decoded = txFormat.decodeResponse(hexToIntArray(encoded.dataHex), contract.abi[2]) - console.log(decoded) - st.equal(decoded[0], 'tuple(uint256,string)[]: strucmts 34,test,123,test2') - }) + st.plan(2) + + let output = compiler.compile(compilerInput(abiEncoderV2ArrayOfTuple)) + output = JSON.parse(output) + const contract = output.contracts['test.sol']['test'] + txFormat.encodeParams('[34, "test"]', contract.abi[1], (error, encoded) => { + console.log(error) + const decoded = txFormat.decodeResponse(hexToIntArray(encoded.dataHex), contract.abi[1]) + console.log(decoded) + st.equal(decoded[0], 'tuple(uint256,string): _strucmts 34,test') + }) + + txFormat.encodeParams('[[34, "test"], [123, "test2"]]', contract.abi[2], (error, encoded) => { + console.log(error) + const decoded = txFormat.decodeResponse(hexToIntArray(encoded.dataHex), contract.abi[2]) + console.log(decoded) + st.equal(decoded[0], 'tuple(uint256,string)[]: strucmts 34,test,123,test2') }) + }) }) const uintContract = `contract uintContractTest { diff --git a/libs/remix-lib/test/txHelper.ts b/libs/remix-lib/test/txHelper.ts index 4d9a46426f..90146f1cea 100644 --- a/libs/remix-lib/test/txHelper.ts +++ b/libs/remix-lib/test/txHelper.ts @@ -3,35 +3,35 @@ import tape from 'tape' import * as txHelper from '../src/execution/txHelper' tape('getFunction', function (st) { - st.plan(11) - let fn = txHelper.getFunction(JSON.parse(abi), 'o((address,uint256))') - st.equal(fn.name, 'o') + st.plan(11) + let fn = txHelper.getFunction(JSON.parse(abi), 'o((address,uint256))') + st.equal(fn.name, 'o') - fn = txHelper.getFunction(JSON.parse(abi), 'i(bytes32)') - st.equal(fn.name, 'i') + fn = txHelper.getFunction(JSON.parse(abi), 'i(bytes32)') + st.equal(fn.name, 'i') - fn = txHelper.getFunction(JSON.parse(abi), 'o1(string,(address,uint256),int256,int256[][3],(address,uint256)[3][])') - st.equal(fn.name, 'o1') + fn = txHelper.getFunction(JSON.parse(abi), 'o1(string,(address,uint256),int256,int256[][3],(address,uint256)[3][])') + st.equal(fn.name, 'o1') - fn = txHelper.getConstructorInterface(JSON.parse(abi)) - st.equal(fn.type, 'constructor') + fn = txHelper.getConstructorInterface(JSON.parse(abi)) + st.equal(fn.type, 'constructor') - fn = txHelper.getFallbackInterface(JSON.parse(abi)) - st.equal(fn.type, 'fallback') + fn = txHelper.getFallbackInterface(JSON.parse(abi)) + st.equal(fn.type, 'fallback') - fn = txHelper.getReceiveInterface(JSON.parse(abi)) - st.equal(fn.type, 'receive') + fn = txHelper.getReceiveInterface(JSON.parse(abi)) + st.equal(fn.type, 'receive') - fn = txHelper.getFunction(testTupleAbi, 'setUser(tuple)') // some compiler version might resolve to tuple. - st.equal(fn.name, 'setUser') - st.equal(fn.inputs[0].type, 'tuple') - st.equal(fn.inputs[0].name, 'user') + fn = txHelper.getFunction(testTupleAbi, 'setUser(tuple)') // some compiler version might resolve to tuple. + st.equal(fn.name, 'setUser') + st.equal(fn.inputs[0].type, 'tuple') + st.equal(fn.inputs[0].name, 'user') - fn = txHelper.getFunctionLiner(testTupleAbi[0], true) - st.equal(fn, 'setUser((string,uint256))') + fn = txHelper.getFunctionLiner(testTupleAbi[0], true) + st.equal(fn, 'setUser((string,uint256))') - fn = txHelper.getFunctionLiner(testTupleAbi[0], false) - st.equal(fn, 'setUser(tuple)') + fn = txHelper.getFunctionLiner(testTupleAbi[0], false) + st.equal(fn, 'setUser(tuple)') }) const abi = `[ diff --git a/libs/remix-lib/test/txResultHelper.ts b/libs/remix-lib/test/txResultHelper.ts index cb972b68b7..11833494d2 100644 --- a/libs/remix-lib/test/txResultHelper.ts +++ b/libs/remix-lib/test/txResultHelper.ts @@ -8,108 +8,108 @@ import { resultToRemixTx } from '../src/helpers/txResultHelper' const TRANSACTION_HASH = '0x538ad944d09c2df403f064c1e4556fae877fe3f1b600c567622e330c2bdbbe2e' const CONTRACT_ADDRESS_HEX = '0x692a70d2e424a56d2c6c27aa97d1a86395877b3a' const CONTRACT_ADDRESS_BUFFER = toBuffer( - [105, 42, 112, 210, 228, 36, 165, 109, 44, 108, 39, 170, 151, 209, 168, - 99, 149, 135, 123, 58]) + [105, 42, 112, 210, 228, 36, 165, 109, 44, 108, 39, 170, 151, 209, 168, + 99, 149, 135, 123, 58]) const RETURN_VALUE_HEX = '0x0000000000000000000000000000000000000000000000000000000000000001' const RETURN_VALUE_BUFFER = toBuffer( - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 1]) + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 1]) const STATUS_OK = '0x1' const GAS_USED_INT = 75427 const GAS_USED_HEX = '0x126a3' const NODE_CALL_RESULT = { - receipt: {}, - result: RETURN_VALUE_HEX, - transactionHash: undefined + receipt: {}, + result: RETURN_VALUE_HEX, + transactionHash: undefined } const NODE_TX_RESULT = { - receipt: { - blockHash: '0x380485a4e6372a42e36489783c7f7cb66257612133cd245859c206fd476e9c44', - blockNumber: 5994, - contractAddress: CONTRACT_ADDRESS_HEX, - cumulativeGasUsed: GAS_USED_INT, - from: '0xed9d02e382b34818e88b88a309c7fe71e65f419d', - gasUsed: GAS_USED_INT, - status: STATUS_OK, - to: null, - transactionHash: TRANSACTION_HASH, - transactionIndex: 0 - }, - transactionHash: TRANSACTION_HASH + receipt: { + blockHash: '0x380485a4e6372a42e36489783c7f7cb66257612133cd245859c206fd476e9c44', + blockNumber: 5994, + contractAddress: CONTRACT_ADDRESS_HEX, + cumulativeGasUsed: GAS_USED_INT, + from: '0xed9d02e382b34818e88b88a309c7fe71e65f419d', + gasUsed: GAS_USED_INT, + status: STATUS_OK, + to: null, + transactionHash: TRANSACTION_HASH, + transactionIndex: 0 + }, + transactionHash: TRANSACTION_HASH } const VM_RESULT = { - receipt: { - amountSpent: new BN(1), - contractAddress: CONTRACT_ADDRESS_BUFFER, - gasRefund: new BN(0), - gasUsed: new BN(GAS_USED_INT), - status: STATUS_OK, - }, - transactionHash: TRANSACTION_HASH + receipt: { + amountSpent: new BN(1), + contractAddress: CONTRACT_ADDRESS_BUFFER, + gasRefund: new BN(0), + gasUsed: new BN(GAS_USED_INT), + status: STATUS_OK, + }, + transactionHash: TRANSACTION_HASH } const EXEC_RESULT = { - exceptionError: null, - gasRefund: new BN(0), - gasUsed: new BN(GAS_USED_INT), - returnValue: RETURN_VALUE_BUFFER + exceptionError: null, + gasRefund: new BN(0), + gasUsed: new BN(GAS_USED_INT), + returnValue: RETURN_VALUE_BUFFER } const EXEC_RESULT_ERROR = { - exceptionError: 'this is an error' + exceptionError: 'this is an error' } tape('converts node transaction result to RemixTx', function (t) { - // contract creation - const txResult = { ...NODE_TX_RESULT } - let remixTx = resultToRemixTx(txResult, {}) - - t.equal(remixTx.transactionHash, TRANSACTION_HASH) - t.equal(remixTx.createdAddress, CONTRACT_ADDRESS_HEX) - t.equal(remixTx.status, STATUS_OK) - t.equal(remixTx.gasUsed, GAS_USED_HEX) - t.equal(remixTx.return, undefined) - t.equal(remixTx.error, undefined) - - // contract method tx - txResult.receipt.contractAddress = null - remixTx = resultToRemixTx(txResult, {}) - t.equal(remixTx.createdAddress, null) - - t.end() + // contract creation + const txResult = { ...NODE_TX_RESULT } + let remixTx = resultToRemixTx(txResult, {}) + + t.equal(remixTx.transactionHash, TRANSACTION_HASH) + t.equal(remixTx.createdAddress, CONTRACT_ADDRESS_HEX) + t.equal(remixTx.status, STATUS_OK) + t.equal(remixTx.gasUsed, GAS_USED_HEX) + t.equal(remixTx.return, undefined) + t.equal(remixTx.error, undefined) + + // contract method tx + txResult.receipt.contractAddress = null + remixTx = resultToRemixTx(txResult, {}) + t.equal(remixTx.createdAddress, null) + + t.end() }) tape('converts node call result to RemixTx', function (t) { - const txResult = { ...NODE_CALL_RESULT } - const remixTx = resultToRemixTx(txResult, {}) + const txResult = { ...NODE_CALL_RESULT } + const remixTx = resultToRemixTx(txResult, {}) - t.equal(remixTx.transactionHash, undefined) - t.equal(remixTx.createdAddress, undefined) - t.equal(remixTx.status, undefined) - t.equal(remixTx.gasUsed, undefined) - t.equal(remixTx.return, RETURN_VALUE_HEX) - t.equal(remixTx.error, undefined) + t.equal(remixTx.transactionHash, undefined) + t.equal(remixTx.createdAddress, undefined) + t.equal(remixTx.status, undefined) + t.equal(remixTx.gasUsed, undefined) + t.equal(remixTx.return, RETURN_VALUE_HEX) + t.equal(remixTx.error, undefined) - t.end() + t.end() }) tape('converts VM result to RemixTx', function (t) { - const txResult = { ...VM_RESULT } - let remixTx = resultToRemixTx(txResult, EXEC_RESULT) + const txResult = { ...VM_RESULT } + let remixTx = resultToRemixTx(txResult, EXEC_RESULT) - t.equal(remixTx.transactionHash, - TRANSACTION_HASH) - t.equal(remixTx.createdAddress, CONTRACT_ADDRESS_HEX) - t.equal(remixTx.status, STATUS_OK) - t.equal(remixTx.gasUsed, GAS_USED_HEX) - t.equal(remixTx.return, RETURN_VALUE_HEX) - t.equal(remixTx.error, null) + t.equal(remixTx.transactionHash, + TRANSACTION_HASH) + t.equal(remixTx.createdAddress, CONTRACT_ADDRESS_HEX) + t.equal(remixTx.status, STATUS_OK) + t.equal(remixTx.gasUsed, GAS_USED_HEX) + t.equal(remixTx.return, RETURN_VALUE_HEX) + t.equal(remixTx.error, null) - remixTx = resultToRemixTx(VM_RESULT, EXEC_RESULT_ERROR) - t.equal(remixTx.error, 'this is an error') + remixTx = resultToRemixTx(VM_RESULT, EXEC_RESULT_ERROR) + t.equal(remixTx.error, 'this is an error') - t.end() + t.end() }) diff --git a/libs/remix-lib/test/util.ts b/libs/remix-lib/test/util.ts index 2f4b63926a..9ef5da1ca1 100644 --- a/libs/remix-lib/test/util.ts +++ b/libs/remix-lib/test/util.ts @@ -5,108 +5,108 @@ import { ERC721 } from './data/ERC721' import { sampleERC721 } from './data/sampleERC721' tape('Util', function (t) { - t.test('lowerbound', function (st) { - st.plan(7) - let array = [2, 5, 8, 9, 45, 56, 78] - let lowerBound = util.findLowerBound(10, array) - st.equal(lowerBound, 3) + t.test('lowerbound', function (st) { + st.plan(7) + let array = [2, 5, 8, 9, 45, 56, 78] + let lowerBound = util.findLowerBound(10, array) + st.equal(lowerBound, 3) - lowerBound = util.findLowerBound(3, array) - st.equal(lowerBound, 0) + lowerBound = util.findLowerBound(3, array) + st.equal(lowerBound, 0) - lowerBound = util.findLowerBound(100, array) - st.equal(lowerBound, 6) + lowerBound = util.findLowerBound(100, array) + st.equal(lowerBound, 6) - lowerBound = util.findLowerBound(1, array) - st.equal(lowerBound, -1) + lowerBound = util.findLowerBound(1, array) + st.equal(lowerBound, -1) - lowerBound = util.findLowerBound(45, array) - st.equal(lowerBound, 4) + lowerBound = util.findLowerBound(45, array) + st.equal(lowerBound, 4) - array = [2, 5, 8, 9, 9, 45, 56, 78] - lowerBound = util.findLowerBound(9, array) - st.equal(lowerBound, 4) + array = [2, 5, 8, 9, 9, 45, 56, 78] + lowerBound = util.findLowerBound(9, array) + st.equal(lowerBound, 4) - lowerBound = util.findLowerBound(9, []) - st.equal(lowerBound, -1) - }) + lowerBound = util.findLowerBound(9, []) + st.equal(lowerBound, -1) + }) }) tape('util.groupBy on valid input', function (t) { - t.plan(1) - - const result = util.groupBy([ - {category: 'GAS', name: 'a'}, - {category: 'SEC', name: 'b'}, - {category: 'GAS', name: 'c'} - - ], 'category') - - const expectedResult = { - 'GAS': [ - {category: 'GAS', name: 'a'}, - {category: 'GAS', name: 'c'} - ], - 'SEC': [ - {category: 'SEC', name: 'b'} - ] - } - - t.deepEqual(result, expectedResult) + t.plan(1) + + const result = util.groupBy([ + {category: 'GAS', name: 'a'}, + {category: 'SEC', name: 'b'}, + {category: 'GAS', name: 'c'} + + ], 'category') + + const expectedResult = { + 'GAS': [ + {category: 'GAS', name: 'a'}, + {category: 'GAS', name: 'c'} + ], + 'SEC': [ + {category: 'SEC', name: 'b'} + ] + } + + t.deepEqual(result, expectedResult) }) tape('util.concatWithSeperator valid output', function (t) { - t.plan(4) - t.notEqual(util.concatWithSeperator(['a', 'b', 'c'], ','), 'a, b, c', 'Concat with comma should not produce spaces') - t.equal(util.concatWithSeperator(['a', 'b', 'c'], ','), 'a,b,c', 'Concat with comma should not produce spaces') - t.equal(util.concatWithSeperator(['a', 'b', 'c'], ', '), 'a, b, c', 'Concat with comma space should not produce trailing comma') - t.equal(util.concatWithSeperator(['a', 'b', 'c'], '+'), 'a+b+c', 'Concat with plus') + t.plan(4) + t.notEqual(util.concatWithSeperator(['a', 'b', 'c'], ','), 'a, b, c', 'Concat with comma should not produce spaces') + t.equal(util.concatWithSeperator(['a', 'b', 'c'], ','), 'a,b,c', 'Concat with comma should not produce spaces') + t.equal(util.concatWithSeperator(['a', 'b', 'c'], ', '), 'a, b, c', 'Concat with comma space should not produce trailing comma') + t.equal(util.concatWithSeperator(['a', 'b', 'c'], '+'), 'a+b+c', 'Concat with plus') }) tape('util.escapeRegExp', function (t) { - t.plan(3) - const original = 'function (uint256) returns (bool)' - t.equal(util.escapeRegExp('abcd'), 'abcd', 'String with no regex') - t.equal(util.escapeRegExp(original), 'function \\(uint256\\) returns \\(bool\\)', 'function string with regex') - t.ok(new RegExp(util.escapeRegExp(original)).test(original), 'should still test for original string') + t.plan(3) + const original = 'function (uint256) returns (bool)' + t.equal(util.escapeRegExp('abcd'), 'abcd', 'String with no regex') + t.equal(util.escapeRegExp(original), 'function \\(uint256\\) returns \\(bool\\)', 'function string with regex') + t.ok(new RegExp(util.escapeRegExp(original)).test(original), 'should still test for original string') }) tape('util.compareByteCode', function (t) { - t.plan(5) + t.plan(5) - const address = 'c2a9cef5420203c2672f0e4325cca774893cca98' - const nullAddress = '0000000000000000000000000000000000000000' - const deployedLibraryByteCode = '0x73c2a9cef5420203c2672f0e4325cca774893cca983014608060405260043610610058576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff168063f26ea02c1461005d575b600080fd5b81801561006957600080fd5b506100886004803603810190808035906020019092919050505061008a565b005b600081600101600060648110151561009e57fe5b600502016002018190555060008160010160006064811015156100bd57fe5b600502016004018190555060008160010160006064811015156100dc57fe5b6005020160030181905550600081600001819055506001816101f501819055816101f601819055506064816101f70181905550505600a165627a7a723058203a6f106db7413fd9cad962bc12ba2327799d6b1334335f7bb854eab04200b3bf0029' - t.ok(util.compareByteCode(deployedLibraryByteCode, deployedLibraryByteCode.replace(address, nullAddress)), 'library bytecode should be the same') + const address = 'c2a9cef5420203c2672f0e4325cca774893cca98' + const nullAddress = '0000000000000000000000000000000000000000' + const deployedLibraryByteCode = '0x73c2a9cef5420203c2672f0e4325cca774893cca983014608060405260043610610058576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff168063f26ea02c1461005d575b600080fd5b81801561006957600080fd5b506100886004803603810190808035906020019092919050505061008a565b005b600081600101600060648110151561009e57fe5b600502016002018190555060008160010160006064811015156100bd57fe5b600502016004018190555060008160010160006064811015156100dc57fe5b6005020160030181905550600081600001819055506001816101f501819055816101f601819055506064816101f70181905550505600a165627a7a723058203a6f106db7413fd9cad962bc12ba2327799d6b1334335f7bb854eab04200b3bf0029' + t.ok(util.compareByteCode(deployedLibraryByteCode, deployedLibraryByteCode.replace(address, nullAddress)), 'library bytecode should be the same') - // ballot on ropsten - 0x96d87AB604AEC7FB26C2E046CA5e6eBBB9D8b74D - const code1 = '0x608060405234801561001057600080fd5b50600436106100885760003560e01c8063609ff1bd1161005b578063609ff1bd146101925780639e7b8d61146101b0578063a3ec138d146101f4578063e2ba53f01461029157610088565b80630121b93f1461008d578063013cf08b146100bb5780632e4176cf146101045780635c19a95c1461014e575b600080fd5b6100b9600480360360208110156100a357600080fd5b81019080803590602001909291905050506102af565b005b6100e7600480360360208110156100d157600080fd5b810190808035906020019092919050505061044c565b604051808381526020018281526020019250505060405180910390f35b61010c61047d565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b6101906004803603602081101561016457600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291905050506104a2565b005b61019a6108be565b6040518082815260200191505060405180910390f35b6101f2600480360360208110156101c657600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610935565b005b6102366004803603602081101561020a57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610b36565b60405180858152602001841515151581526020018373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200182815260200194505050505060405180910390f35b610299610b93565b6040518082815260200191505060405180910390f35b6000600160003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020905060008160000154141561036d576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260148152602001807f486173206e6f20726967687420746f20766f746500000000000000000000000081525060200191505060405180910390fd5b8060010160009054906101000a900460ff16156103f2576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252600e8152602001807f416c726561647920766f7465642e00000000000000000000000000000000000081525060200191505060405180910390fd5b60018160010160006101000a81548160ff02191690831515021790555081816002018190555080600001546002838154811061042a57fe5b9060005260206000209060020201600101600082825401925050819055505050565b6002818154811061045957fe5b90600052602060002090600202016000915090508060000154908060010154905082565b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b6000600160003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002090508060010160009054906101000a900460ff161561056a576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260128152602001807f596f7520616c726561647920766f7465642e000000000000000000000000000081525060200191505060405180910390fd5b3373ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff16141561060c576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601e8152602001807f53656c662d64656c65676174696f6e20697320646973616c6c6f7765642e000081525060200191505060405180910390fd5b5b600073ffffffffffffffffffffffffffffffffffffffff16600160008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060010160019054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16146107af57600160008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060010160019054906101000a900473ffffffffffffffffffffffffffffffffffffffff1691503373ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff1614156107aa576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260198152602001807f466f756e64206c6f6f7020696e2064656c65676174696f6e2e0000000000000081525060200191505060405180910390fd5b61060d565b60018160010160006101000a81548160ff021916908315150217905550818160010160016101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055506000600160008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002090508060010160009054906101000a900460ff16156108a2578160000154600282600201548154811061087f57fe5b9060005260206000209060020201600101600082825401925050819055506108b9565b816000015481600001600082825401925050819055505b505050565b6000806000905060008090505b6002805490508110156109305781600282815481106108e657fe5b9060005260206000209060020201600101541115610923576002818154811061090b57fe5b90600052602060002090600202016001015491508092505b80806001019150506108cb565b505090565b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16146109da576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526028815260200180610bc16028913960400191505060405180910390fd5b600160008273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060010160009054906101000a900460ff1615610a9d576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260188152602001807f54686520766f74657220616c726561647920766f7465642e000000000000000081525060200191505060405180910390fd5b6000600160008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000015414610aec57600080fd5b60018060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000018190555050565b60016020528060005260406000206000915090508060000154908060010160009054906101000a900460ff16908060010160019054906101000a900473ffffffffffffffffffffffffffffffffffffffff16908060020154905084565b60006002610b9f6108be565b81548110610ba957fe5b90600052602060002090600202016000015490509056fe4f6e6c79206368616972706572736f6e2063616e206769766520726967687420746f20766f74652ea26469706673582212205c306dddac7a80542891873341cf580f3b5920b0374bd5e32fe35fcabb116aaa64736f6c63430006010033' - const code2 = '0x608060405234801561001057600080fd5b50600436106100885760003560e01c8063609ff1bd1161005b578063609ff1bd146101925780639e7b8d61146101b0578063a3ec138d146101f4578063e2ba53f01461029157610088565b80630121b93f1461008d578063013cf08b146100bb5780632e4176cf146101045780635c19a95c1461014e575b600080fd5b6100b9600480360360208110156100a357600080fd5b81019080803590602001909291905050506102af565b005b6100e7600480360360208110156100d157600080fd5b810190808035906020019092919050505061044c565b604051808381526020018281526020019250505060405180910390f35b61010c61047d565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b6101906004803603602081101561016457600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291905050506104a2565b005b61019a6108be565b6040518082815260200191505060405180910390f35b6101f2600480360360208110156101c657600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610935565b005b6102366004803603602081101561020a57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610b36565b60405180858152602001841515151581526020018373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200182815260200194505050505060405180910390f35b610299610b93565b6040518082815260200191505060405180910390f35b6000600160003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020905060008160000154141561036d576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260148152602001807f486173206e6f20726967687420746f20766f746500000000000000000000000081525060200191505060405180910390fd5b8060010160009054906101000a900460ff16156103f2576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252600e8152602001807f416c726561647920766f7465642e00000000000000000000000000000000000081525060200191505060405180910390fd5b60018160010160006101000a81548160ff02191690831515021790555081816002018190555080600001546002838154811061042a57fe5b9060005260206000209060020201600101600082825401925050819055505050565b6002818154811061045957fe5b90600052602060002090600202016000915090508060000154908060010154905082565b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b6000600160003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002090508060010160009054906101000a900460ff161561056a576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260128152602001807f596f7520616c726561647920766f7465642e000000000000000000000000000081525060200191505060405180910390fd5b3373ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff16141561060c576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601e8152602001807f53656c662d64656c65676174696f6e20697320646973616c6c6f7765642e000081525060200191505060405180910390fd5b5b600073ffffffffffffffffffffffffffffffffffffffff16600160008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060010160019054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16146107af57600160008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060010160019054906101000a900473ffffffffffffffffffffffffffffffffffffffff1691503373ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff1614156107aa576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260198152602001807f466f756e64206c6f6f7020696e2064656c65676174696f6e2e0000000000000081525060200191505060405180910390fd5b61060d565b60018160010160006101000a81548160ff021916908315150217905550818160010160016101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055506000600160008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002090508060010160009054906101000a900460ff16156108a2578160000154600282600201548154811061087f57fe5b9060005260206000209060020201600101600082825401925050819055506108b9565b816000015481600001600082825401925050819055505b505050565b6000806000905060008090505b6002805490508110156109305781600282815481106108e657fe5b9060005260206000209060020201600101541115610923576002818154811061090b57fe5b90600052602060002090600202016001015491508092505b80806001019150506108cb565b505090565b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16146109da576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526028815260200180610bc16028913960400191505060405180910390fd5b600160008273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060010160009054906101000a900460ff1615610a9d576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260188152602001807f54686520766f74657220616c726561647920766f7465642e000000000000000081525060200191505060405180910390fd5b6000600160008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000015414610aec57600080fd5b60018060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000018190555050565b60016020528060005260406000206000915090508060000154908060010160009054906101000a900460ff16908060010160019054906101000a900473ffffffffffffffffffffffffffffffffffffffff16908060020154905084565b60006002610b9f6108be565b81548110610ba957fe5b90600052602060002090600202016000015490509056fe4f6e6c79206368616972706572736f6e2063616e206769766520726967687420746f20766f74652ea26469706673582212207f175eb02901e2002e7a57d5736c3bea18bac88a3602e809c170e4a0da9dbf8d64736f6c63430006010033' - t.ok(util.compareByteCode(code1, code2), 'should match even though cbor encoded metadatas are different') + // ballot on ropsten - 0x96d87AB604AEC7FB26C2E046CA5e6eBBB9D8b74D + const code1 = '0x608060405234801561001057600080fd5b50600436106100885760003560e01c8063609ff1bd1161005b578063609ff1bd146101925780639e7b8d61146101b0578063a3ec138d146101f4578063e2ba53f01461029157610088565b80630121b93f1461008d578063013cf08b146100bb5780632e4176cf146101045780635c19a95c1461014e575b600080fd5b6100b9600480360360208110156100a357600080fd5b81019080803590602001909291905050506102af565b005b6100e7600480360360208110156100d157600080fd5b810190808035906020019092919050505061044c565b604051808381526020018281526020019250505060405180910390f35b61010c61047d565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b6101906004803603602081101561016457600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291905050506104a2565b005b61019a6108be565b6040518082815260200191505060405180910390f35b6101f2600480360360208110156101c657600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610935565b005b6102366004803603602081101561020a57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610b36565b60405180858152602001841515151581526020018373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200182815260200194505050505060405180910390f35b610299610b93565b6040518082815260200191505060405180910390f35b6000600160003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020905060008160000154141561036d576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260148152602001807f486173206e6f20726967687420746f20766f746500000000000000000000000081525060200191505060405180910390fd5b8060010160009054906101000a900460ff16156103f2576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252600e8152602001807f416c726561647920766f7465642e00000000000000000000000000000000000081525060200191505060405180910390fd5b60018160010160006101000a81548160ff02191690831515021790555081816002018190555080600001546002838154811061042a57fe5b9060005260206000209060020201600101600082825401925050819055505050565b6002818154811061045957fe5b90600052602060002090600202016000915090508060000154908060010154905082565b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b6000600160003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002090508060010160009054906101000a900460ff161561056a576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260128152602001807f596f7520616c726561647920766f7465642e000000000000000000000000000081525060200191505060405180910390fd5b3373ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff16141561060c576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601e8152602001807f53656c662d64656c65676174696f6e20697320646973616c6c6f7765642e000081525060200191505060405180910390fd5b5b600073ffffffffffffffffffffffffffffffffffffffff16600160008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060010160019054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16146107af57600160008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060010160019054906101000a900473ffffffffffffffffffffffffffffffffffffffff1691503373ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff1614156107aa576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260198152602001807f466f756e64206c6f6f7020696e2064656c65676174696f6e2e0000000000000081525060200191505060405180910390fd5b61060d565b60018160010160006101000a81548160ff021916908315150217905550818160010160016101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055506000600160008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002090508060010160009054906101000a900460ff16156108a2578160000154600282600201548154811061087f57fe5b9060005260206000209060020201600101600082825401925050819055506108b9565b816000015481600001600082825401925050819055505b505050565b6000806000905060008090505b6002805490508110156109305781600282815481106108e657fe5b9060005260206000209060020201600101541115610923576002818154811061090b57fe5b90600052602060002090600202016001015491508092505b80806001019150506108cb565b505090565b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16146109da576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526028815260200180610bc16028913960400191505060405180910390fd5b600160008273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060010160009054906101000a900460ff1615610a9d576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260188152602001807f54686520766f74657220616c726561647920766f7465642e000000000000000081525060200191505060405180910390fd5b6000600160008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000015414610aec57600080fd5b60018060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000018190555050565b60016020528060005260406000206000915090508060000154908060010160009054906101000a900460ff16908060010160019054906101000a900473ffffffffffffffffffffffffffffffffffffffff16908060020154905084565b60006002610b9f6108be565b81548110610ba957fe5b90600052602060002090600202016000015490509056fe4f6e6c79206368616972706572736f6e2063616e206769766520726967687420746f20766f74652ea26469706673582212205c306dddac7a80542891873341cf580f3b5920b0374bd5e32fe35fcabb116aaa64736f6c63430006010033' + const code2 = '0x608060405234801561001057600080fd5b50600436106100885760003560e01c8063609ff1bd1161005b578063609ff1bd146101925780639e7b8d61146101b0578063a3ec138d146101f4578063e2ba53f01461029157610088565b80630121b93f1461008d578063013cf08b146100bb5780632e4176cf146101045780635c19a95c1461014e575b600080fd5b6100b9600480360360208110156100a357600080fd5b81019080803590602001909291905050506102af565b005b6100e7600480360360208110156100d157600080fd5b810190808035906020019092919050505061044c565b604051808381526020018281526020019250505060405180910390f35b61010c61047d565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b6101906004803603602081101561016457600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291905050506104a2565b005b61019a6108be565b6040518082815260200191505060405180910390f35b6101f2600480360360208110156101c657600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610935565b005b6102366004803603602081101561020a57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610b36565b60405180858152602001841515151581526020018373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200182815260200194505050505060405180910390f35b610299610b93565b6040518082815260200191505060405180910390f35b6000600160003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020905060008160000154141561036d576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260148152602001807f486173206e6f20726967687420746f20766f746500000000000000000000000081525060200191505060405180910390fd5b8060010160009054906101000a900460ff16156103f2576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252600e8152602001807f416c726561647920766f7465642e00000000000000000000000000000000000081525060200191505060405180910390fd5b60018160010160006101000a81548160ff02191690831515021790555081816002018190555080600001546002838154811061042a57fe5b9060005260206000209060020201600101600082825401925050819055505050565b6002818154811061045957fe5b90600052602060002090600202016000915090508060000154908060010154905082565b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b6000600160003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002090508060010160009054906101000a900460ff161561056a576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260128152602001807f596f7520616c726561647920766f7465642e000000000000000000000000000081525060200191505060405180910390fd5b3373ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff16141561060c576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601e8152602001807f53656c662d64656c65676174696f6e20697320646973616c6c6f7765642e000081525060200191505060405180910390fd5b5b600073ffffffffffffffffffffffffffffffffffffffff16600160008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060010160019054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16146107af57600160008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060010160019054906101000a900473ffffffffffffffffffffffffffffffffffffffff1691503373ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff1614156107aa576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260198152602001807f466f756e64206c6f6f7020696e2064656c65676174696f6e2e0000000000000081525060200191505060405180910390fd5b61060d565b60018160010160006101000a81548160ff021916908315150217905550818160010160016101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055506000600160008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002090508060010160009054906101000a900460ff16156108a2578160000154600282600201548154811061087f57fe5b9060005260206000209060020201600101600082825401925050819055506108b9565b816000015481600001600082825401925050819055505b505050565b6000806000905060008090505b6002805490508110156109305781600282815481106108e657fe5b9060005260206000209060020201600101541115610923576002818154811061090b57fe5b90600052602060002090600202016001015491508092505b80806001019150506108cb565b505090565b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16146109da576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526028815260200180610bc16028913960400191505060405180910390fd5b600160008273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060010160009054906101000a900460ff1615610a9d576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260188152602001807f54686520766f74657220616c726561647920766f7465642e000000000000000081525060200191505060405180910390fd5b6000600160008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000015414610aec57600080fd5b60018060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000018190555050565b60016020528060005260406000206000915090508060000154908060010160009054906101000a900460ff16908060010160019054906101000a900473ffffffffffffffffffffffffffffffffffffffff16908060020154905084565b60006002610b9f6108be565b81548110610ba957fe5b90600052602060002090600202016000015490509056fe4f6e6c79206368616972706572736f6e2063616e206769766520726967687420746f20766f74652ea26469706673582212207f175eb02901e2002e7a57d5736c3bea18bac88a3602e809c170e4a0da9dbf8d64736f6c63430006010033' + t.ok(util.compareByteCode(code1, code2), 'should match even though cbor encoded metadatas are different') - const storageCode1 = '0x608060405234801561001057600080fd5b50610150806100206000396000f3fe608060405234801561001057600080fd5b50600436106100365760003560e01c80632e64cec11461003b5780636057361d14610059575b600080fd5b610043610075565b60405161005091906100d9565b60405180910390f35b610073600480360381019061006e919061009d565b61007e565b005b60008054905090565b8060008190555050565b60008135905061009781610103565b92915050565b6000602082840312156100b3576100b26100fe565b5b60006100c184828501610088565b91505092915050565b6100d3816100f4565b82525050565b60006020820190506100ee60008301846100ca565b92915050565b6000819050919050565b600080fd5b61010c816100f4565b811461011757600080fd5b5056fea2646970667358221220c29dfcfdfd91e29bbf21ece97752e17fd3a0c07db30320f5b60b087866d2b7b164736f6c63430008070033' - const storageCode2 = '0x608060405234801561001057600080fd5b506101e8806100206000396000f3fe608060405234801561001057600080fd5b50600436106100365760003560e01c80632e64cec11461003b5780636057361d14610059575b600080fd5b610043610075565b60405161005091906100ec565b60405180910390f35b610073600480360381019061006e91906100b0565b61007e565b005b60008054905090565b6000602a90506001826100919190610107565b6000819055505050565b6000813590506100aa8161019b565b92915050565b6000602082840312156100c6576100c5610196565b5b60006100d48482850161009b565b91505092915050565b6100e68161015d565b82525050565b600060208201905061010160008301846100dd565b92915050565b60006101128261015d565b915061011d8361015d565b9250827fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0382111561015257610151610167565b5b828201905092915050565b6000819050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b600080fd5b6101a48161015d565b81146101af57600080fd5b5056fea264697066735822122096fdfc54e6f9b12cf7a0e5d49289e40f3e9ed7554a1e5485cff008aebc78f27264736f6c63430008070033' - t.ok(!util.compareByteCode(storageCode1, storageCode2), 'should not match') + const storageCode1 = '0x608060405234801561001057600080fd5b50610150806100206000396000f3fe608060405234801561001057600080fd5b50600436106100365760003560e01c80632e64cec11461003b5780636057361d14610059575b600080fd5b610043610075565b60405161005091906100d9565b60405180910390f35b610073600480360381019061006e919061009d565b61007e565b005b60008054905090565b8060008190555050565b60008135905061009781610103565b92915050565b6000602082840312156100b3576100b26100fe565b5b60006100c184828501610088565b91505092915050565b6100d3816100f4565b82525050565b60006020820190506100ee60008301846100ca565b92915050565b6000819050919050565b600080fd5b61010c816100f4565b811461011757600080fd5b5056fea2646970667358221220c29dfcfdfd91e29bbf21ece97752e17fd3a0c07db30320f5b60b087866d2b7b164736f6c63430008070033' + const storageCode2 = '0x608060405234801561001057600080fd5b506101e8806100206000396000f3fe608060405234801561001057600080fd5b50600436106100365760003560e01c80632e64cec11461003b5780636057361d14610059575b600080fd5b610043610075565b60405161005091906100ec565b60405180910390f35b610073600480360381019061006e91906100b0565b61007e565b005b60008054905090565b6000602a90506001826100919190610107565b6000819055505050565b6000813590506100aa8161019b565b92915050565b6000602082840312156100c6576100c5610196565b5b60006100d48482850161009b565b91505092915050565b6100e68161015d565b82525050565b600060208201905061010160008301846100dd565b92915050565b60006101128261015d565b915061011d8361015d565b9250827fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0382111561015257610151610167565b5b828201905092915050565b6000819050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b600080fd5b6101a48161015d565b81146101af57600080fd5b5056fea264697066735822122096fdfc54e6f9b12cf7a0e5d49289e40f3e9ed7554a1e5485cff008aebc78f27264736f6c63430008070033' + t.ok(!util.compareByteCode(storageCode1, storageCode2), 'should not match') - t.ok(!util.compareByteCode(sampleERC721, ERC721), 'should not match') + t.ok(!util.compareByteCode(sampleERC721, ERC721), 'should not match') - const immutableRef1 = '0x6080604052600436106101145760003560e01c806352d1902d116100a057806395d89b411161006457806395d89b4114610380578063a457c2d7146103ab578063a9059cbb146103e8578063dd62ed3e14610425578063f2fde38b1461046257610114565b806352d1902d146102bf57806370a08231146102ea578063715018a6146103275780638129fc1c1461033e5780638da5cb5b1461035557610114565b8063313ce567116100e7578063313ce567146101e95780633659cfe614610214578063395093511461023d57806340c10f191461027a5780634f1ef286146102a357610114565b806306fdde0314610119578063095ea7b31461014457806318160ddd1461018157806323b872dd146101ac575b600080fd5b34801561012557600080fd5b5061012e61048b565b60405161013b919061228a565b60405180910390f35b34801561015057600080fd5b5061016b60048036038101906101669190611e3d565b61051d565b6040516101789190612239565b60405180910390f35b34801561018d57600080fd5b50610196610540565b6040516101a3919061250c565b60405180910390f35b3480156101b857600080fd5b506101d360048036038101906101ce9190611d8e565b61054a565b6040516101e09190612239565b60405180910390f35b3480156101f557600080fd5b506101fe610579565b60405161020b9190612527565b60405180910390f35b34801561022057600080fd5b5061023b60048036038101906102369190611d21565b610582565b005b34801561024957600080fd5b50610264600480360381019061025f9190611e3d565b61070b565b6040516102719190612239565b60405180910390f35b34801561028657600080fd5b506102a1600480360381019061029c9190611e3d565b610742565b005b6102bd60048036038101906102b89190611de1565b610758565b005b3480156102cb57600080fd5b506102d4610895565b6040516102e19190612254565b60405180910390f35b3480156102f657600080fd5b50610311600480360381019061030c9190611d21565b61094e565b60405161031e919061250c565b60405180910390f35b34801561033357600080fd5b5061033c610997565b005b34801561034a57600080fd5b506103536109ab565b005b34801561036157600080fd5b5061036a610b65565b604051610377919061221e565b60405180910390f35b34801561038c57600080fd5b50610395610b8f565b6040516103a2919061228a565b60405180910390f35b3480156103b757600080fd5b506103d260048036038101906103cd9190611e3d565b610c21565b6040516103df9190612239565b60405180910390f35b3480156103f457600080fd5b5061040f600480360381019061040a9190611e3d565b610c98565b60405161041c9190612239565b60405180910390f35b34801561043157600080fd5b5061044c60048036038101906104479190611d4e565b610cbb565b604051610459919061250c565b60405180910390f35b34801561046e57600080fd5b5061048960048036038101906104849190611d21565b610d42565b005b60606036805461049a906126d3565b80601f01602080910402602001604051908101604052809291908181526020018280546104c6906126d3565b80156105135780601f106104e857610100808354040283529160200191610513565b820191906000526020600020905b8154815290600101906020018083116104f657829003601f168201915b5050505050905090565b600080610528610dc6565b9050610535818585610dce565b600191505092915050565b6000603554905090565b600080610555610dc6565b9050610562858285610f99565b61056d858585611025565b60019150509392505050565b60006012905090565b7f000000000000000000000000d9145cce52d386f254917e481eb44e9943f3913873ffffffffffffffffffffffffffffffffffffffff163073ffffffffffffffffffffffffffffffffffffffff161415610611576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016106089061230c565b60405180910390fd5b7f000000000000000000000000d9145cce52d386f254917e481eb44e9943f3913873ffffffffffffffffffffffffffffffffffffffff166106506112a0565b73ffffffffffffffffffffffffffffffffffffffff16146106a6576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161069d9061236c565b60405180910390fd5b6106af816112f7565b61070881600067ffffffffffffffff8111156106ce576106cd612794565b5b6040519080825280601f01601f1916602001820160405280156107005781602001600182028036833780820191505090505b506000611302565b50565b600080610716610dc6565b90506107378185856107288589610cbb565b61073291906125ca565b610dce565b600191505092915050565b61074a61147f565b61075482826114fd565b5050565b7f000000000000000000000000d9145cce52d386f254917e481eb44e9943f3913873ffffffffffffffffffffffffffffffffffffffff163073ffffffffffffffffffffffffffffffffffffffff1614156107e7576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016107de9061230c565b60405180910390fd5b7f000000000000000000000000d9145cce52d386f254917e481eb44e9943f3913873ffffffffffffffffffffffffffffffffffffffff166108266112a0565b73ffffffffffffffffffffffffffffffffffffffff161461087c576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016108739061236c565b60405180910390fd5b610885826112f7565b61089182826001611302565b5050565b60007f000000000000000000000000d9145cce52d386f254917e481eb44e9943f3913873ffffffffffffffffffffffffffffffffffffffff163073ffffffffffffffffffffffffffffffffffffffff1614610925576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161091c9061238c565b60405180910390fd5b7f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc60001b905090565b6000603360008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020549050919050565b61099f61147f565b6109a96000611655565b565b60008060019054906101000a900460ff161590508080156109dc5750600160008054906101000a900460ff1660ff16105b80610a0957506109eb3061171b565b158015610a085750600160008054906101000a900460ff1660ff16145b5b610a48576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610a3f906123cc565b60405180910390fd5b60016000806101000a81548160ff021916908360ff1602179055508015610a85576001600060016101000a81548160ff0219169083151502179055505b610af96040518060400160405280600781526020017f4d79546f6b656e000000000000000000000000000000000000000000000000008152506040518060400160405280600381526020017f4d544b000000000000000000000000000000000000000000000000000000000081525061173e565b610b0161179b565b610b096117f4565b8015610b625760008060016101000a81548160ff0219169083151502179055507f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb38474024986001604051610b59919061226f565b60405180910390a15b50565b6000606560009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905090565b606060378054610b9e906126d3565b80601f0160208091040260200160405190810160405280929190818152602001828054610bca906126d3565b8015610c175780601f10610bec57610100808354040283529160200191610c17565b820191906000526020600020905b815481529060010190602001808311610bfa57829003601f168201915b5050505050905090565b600080610c2c610dc6565b90506000610c3a8286610cbb565b905083811015610c7f576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610c76906124cc565b60405180910390fd5b610c8c8286868403610dce565b60019250505092915050565b600080610ca3610dc6565b9050610cb0818585611025565b600191505092915050565b6000603460008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054905092915050565b610d4a61147f565b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff161415610dba576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610db1906122cc565b60405180910390fd5b610dc381611655565b50565b600033905090565b600073ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff161415610e3e576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610e359061248c565b60405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff161415610eae576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610ea5906122ec565b60405180910390fd5b80603460008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055508173ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92583604051610f8c919061250c565b60405180910390a3505050565b6000610fa58484610cbb565b90507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff811461101f5781811015611011576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016110089061232c565b60405180910390fd5b61101e8484848403610dce565b5b50505050565b600073ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff161415611095576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161108c9061246c565b60405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff161415611105576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016110fc906122ac565b60405180910390fd5b611110838383611845565b6000603360008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054905081811015611197576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161118e9061234c565b60405180910390fd5b818103603360008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000208190555081603360008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825401925050819055508273ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef84604051611287919061250c565b60405180910390a361129a84848461184a565b50505050565b60006112ce7f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc60001b61184f565b60000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905090565b6112ff61147f565b50565b61132e7f4910fdfa16fed3260ed0e7147f7cc6da11a60208b5b9406d12a635614ffd914360001b611859565b60000160009054906101000a900460ff16156113525761134d83611863565b61147a565b8273ffffffffffffffffffffffffffffffffffffffff166352d1902d6040518163ffffffff1660e01b815260040160206040518083038186803b15801561139857600080fd5b505afa9250505080156113c957506040513d601f19601f820116820180604052508101906113c69190611e7d565b60015b611408576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016113ff906123ec565b60405180910390fd5b7f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc60001b811461146d576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401611464906123ac565b60405180910390fd5b5061147983838361191c565b5b505050565b611487610dc6565b73ffffffffffffffffffffffffffffffffffffffff166114a5610b65565b73ffffffffffffffffffffffffffffffffffffffff16146114fb576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016114f29061242c565b60405180910390fd5b565b600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff16141561156d576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401611564906124ec565b60405180910390fd5b61157960008383611845565b806035600082825461158b91906125ca565b9250508190555080603360008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825401925050819055508173ffffffffffffffffffffffffffffffffffffffff16600073ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef8360405161163d919061250c565b60405180910390a36116516000838361184a565b5050565b6000606560009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905081606560006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055508173ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a35050565b6000808273ffffffffffffffffffffffffffffffffffffffff163b119050919050565b600060019054906101000a900460ff1661178d576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401611784906124ac565b60405180910390fd5b6117978282611948565b5050565b600060019054906101000a900460ff166117ea576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016117e1906124ac565b60405180910390fd5b6117f26119c9565b565b600060019054906101000a900460ff16611843576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161183a906124ac565b60405180910390fd5b565b505050565b505050565b6000819050919050565b6000819050919050565b61186c8161171b565b6118ab576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016118a29061240c565b60405180910390fd5b806118d87f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc60001b61184f565b60000160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b61192583611a2a565b6000825111806119325750805b15611943576119418383611a79565b505b505050565b600060019054906101000a900460ff16611997576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161198e906124ac565b60405180910390fd5b81603690805190602001906119ad929190611bcf565b5080603790805190602001906119c4929190611bcf565b505050565b600060019054906101000a900460ff16611a18576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401611a0f906124ac565b60405180910390fd5b611a28611a23610dc6565b611655565b565b611a3381611863565b8073ffffffffffffffffffffffffffffffffffffffff167fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b60405160405180910390a250565b6060611a848361171b565b611ac3576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401611aba9061244c565b60405180910390fd5b6000808473ffffffffffffffffffffffffffffffffffffffff1684604051611aeb9190612207565b600060405180830381855af49150503d8060008114611b26576040519150601f19603f3d011682016040523d82523d6000602084013e611b2b565b606091505b5091509150611b538282604051806060016040528060278152602001612d9960279139611b5d565b9250505092915050565b60608315611b6d57829050611b78565b611b778383611b7f565b5b9392505050565b600082511115611b925781518083602001fd5b806040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401611bc6919061228a565b60405180910390fd5b828054611bdb906126d3565b90600052602060002090601f016020900481019282611bfd5760008555611c44565b82601f10611c1657805160ff1916838001178555611c44565b82800160010185558215611c44579182015b82811115611c43578251825591602001919060010190611c28565b5b509050611c519190611c55565b5090565b5b80821115611c6e576000816000905550600101611c56565b5090565b6000611c85611c8084612567565b612542565b905082815260208101848484011115611ca157611ca06127c8565b5b611cac848285612691565b509392505050565b600081359050611cc381612d53565b92915050565b600081519050611cd881612d6a565b92915050565b600082601f830112611cf357611cf26127c3565b5b8135611d03848260208601611c72565b91505092915050565b600081359050611d1b81612d81565b92915050565b600060208284031215611d3757611d366127d2565b5b6000611d4584828501611cb4565b91505092915050565b60008060408385031215611d6557611d646127d2565b5b6000611d7385828601611cb4565b9250506020611d8485828601611cb4565b9150509250929050565b600080600060608486031215611da757611da66127d2565b5b6000611db586828701611cb4565b9350506020611dc686828701611cb4565b9250506040611dd786828701611d0c565b9150509250925092565b60008060408385031215611df857611df76127d2565b5b6000611e0685828601611cb4565b925050602083013567ffffffffffffffff811115611e2757611e266127cd565b5b611e3385828601611cde565b9150509250929050565b60008060408385031215611e5457611e536127d2565b5b6000611e6285828601611cb4565b9250506020611e7385828601611d0c565b9150509250929050565b600060208284031215611e9357611e926127d2565b5b6000611ea184828501611cc9565b91505092915050565b611eb381612620565b82525050565b611ec281612632565b82525050565b611ed18161263e565b82525050565b6000611ee282612598565b611eec81856125ae565b9350611efc8185602086016126a0565b80840191505092915050565b611f118161267f565b82525050565b6000611f22826125a3565b611f2c81856125b9565b9350611f3c8185602086016126a0565b611f45816127d7565b840191505092915050565b6000611f5d6023836125b9565b9150611f68826127e8565b604082019050919050565b6000611f806026836125b9565b9150611f8b82612837565b604082019050919050565b6000611fa36022836125b9565b9150611fae82612886565b604082019050919050565b6000611fc6602c836125b9565b9150611fd1826128d5565b604082019050919050565b6000611fe9601d836125b9565b9150611ff482612924565b602082019050919050565b600061200c6026836125b9565b91506120178261294d565b604082019050919050565b600061202f602c836125b9565b915061203a8261299c565b604082019050919050565b60006120526038836125b9565b915061205d826129eb565b604082019050919050565b60006120756029836125b9565b915061208082612a3a565b604082019050919050565b6000612098602e836125b9565b91506120a382612a89565b604082019050919050565b60006120bb602e836125b9565b91506120c682612ad8565b604082019050919050565b60006120de602d836125b9565b91506120e982612b27565b604082019050919050565b60006121016020836125b9565b915061210c82612b76565b602082019050919050565b60006121246026836125b9565b915061212f82612b9f565b604082019050919050565b60006121476025836125b9565b915061215282612bee565b604082019050919050565b600061216a6024836125b9565b915061217582612c3d565b604082019050919050565b600061218d602b836125b9565b915061219882612c8c565b604082019050919050565b60006121b06025836125b9565b91506121bb82612cdb565b604082019050919050565b60006121d3601f836125b9565b91506121de82612d2a565b602082019050919050565b6121f281612668565b82525050565b61220181612672565b82525050565b60006122138284611ed7565b915081905092915050565b60006020820190506122336000830184611eaa565b92915050565b600060208201905061224e6000830184611eb9565b92915050565b60006020820190506122696000830184611ec8565b92915050565b60006020820190506122846000830184611f08565b92915050565b600060208201905081810360008301526122a48184611f17565b905092915050565b600060208201905081810360008301526122c581611f50565b9050919050565b600060208201905081810360008301526122e581611f73565b9050919050565b6000602082019050818103600083015261230581611f96565b9050919050565b6000602082019050818103600083015261232581611fb9565b9050919050565b6000602082019050818103600083015261234581611fdc565b9050919050565b6000602082019050818103600083015261236581611fff565b9050919050565b6000602082019050818103600083015261238581612022565b9050919050565b600060208201905081810360008301526123a581612045565b9050919050565b600060208201905081810360008301526123c581612068565b9050919050565b600060208201905081810360008301526123e58161208b565b9050919050565b60006020820190508181036000830152612405816120ae565b9050919050565b60006020820190508181036000830152612425816120d1565b9050919050565b60006020820190508181036000830152612445816120f4565b9050919050565b6000602082019050818103600083015261246581612117565b9050919050565b600060208201905081810360008301526124858161213a565b9050919050565b600060208201905081810360008301526124a58161215d565b9050919050565b600060208201905081810360008301526124c581612180565b9050919050565b600060208201905081810360008301526124e5816121a3565b9050919050565b60006020820190508181036000830152612505816121c6565b9050919050565b600060208201905061252160008301846121e9565b92915050565b600060208201905061253c60008301846121f8565b92915050565b600061254c61255d565b90506125588282612705565b919050565b6000604051905090565b600067ffffffffffffffff82111561258257612581612794565b5b61258b826127d7565b9050602081019050919050565b600081519050919050565b600081519050919050565b600081905092915050565b600082825260208201905092915050565b60006125d582612668565b91506125e083612668565b9250827fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0382111561261557612614612736565b5b828201905092915050565b600061262b82612648565b9050919050565b60008115159050919050565b6000819050919050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b6000819050919050565b600060ff82169050919050565b600061268a82612672565b9050919050565b82818337600083830152505050565b60005b838110156126be5780820151818401526020810190506126a3565b838111156126cd576000848401525b50505050565b600060028204905060018216806126eb57607f821691505b602082108114156126ff576126fe612765565b5b50919050565b61270e826127d7565b810181811067ffffffffffffffff8211171561272d5761272c612794565b5b80604052505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b600080fd5b600080fd5b600080fd5b600080fd5b6000601f19601f8301169050919050565b7f45524332303a207472616e7366657220746f20746865207a65726f206164647260008201527f6573730000000000000000000000000000000000000000000000000000000000602082015250565b7f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160008201527f6464726573730000000000000000000000000000000000000000000000000000602082015250565b7f45524332303a20617070726f766520746f20746865207a65726f20616464726560008201527f7373000000000000000000000000000000000000000000000000000000000000602082015250565b7f46756e6374696f6e206d7573742062652063616c6c6564207468726f7567682060008201527f64656c656761746563616c6c0000000000000000000000000000000000000000602082015250565b7f45524332303a20696e73756666696369656e7420616c6c6f77616e6365000000600082015250565b7f45524332303a207472616e7366657220616d6f756e742065786365656473206260008201527f616c616e63650000000000000000000000000000000000000000000000000000602082015250565b7f46756e6374696f6e206d7573742062652063616c6c6564207468726f7567682060008201527f6163746976652070726f78790000000000000000000000000000000000000000602082015250565b7f555550535570677261646561626c653a206d757374206e6f742062652063616c60008201527f6c6564207468726f7567682064656c656761746563616c6c0000000000000000602082015250565b7f45524331393637557067726164653a20756e737570706f727465642070726f7860008201527f6961626c65555549440000000000000000000000000000000000000000000000602082015250565b7f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160008201527f647920696e697469616c697a6564000000000000000000000000000000000000602082015250565b7f45524331393637557067726164653a206e657720696d706c656d656e7461746960008201527f6f6e206973206e6f742055555053000000000000000000000000000000000000602082015250565b7f455243313936373a206e657720696d706c656d656e746174696f6e206973206e60008201527f6f74206120636f6e747261637400000000000000000000000000000000000000602082015250565b7f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572600082015250565b7f416464726573733a2064656c65676174652063616c6c20746f206e6f6e2d636f60008201527f6e74726163740000000000000000000000000000000000000000000000000000602082015250565b7f45524332303a207472616e736665722066726f6d20746865207a65726f20616460008201527f6472657373000000000000000000000000000000000000000000000000000000602082015250565b7f45524332303a20617070726f76652066726f6d20746865207a65726f2061646460008201527f7265737300000000000000000000000000000000000000000000000000000000602082015250565b7f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960008201527f6e697469616c697a696e67000000000000000000000000000000000000000000602082015250565b7f45524332303a2064656372656173656420616c6c6f77616e63652062656c6f7760008201527f207a65726f000000000000000000000000000000000000000000000000000000602082015250565b7f45524332303a206d696e7420746f20746865207a65726f206164647265737300600082015250565b612d5c81612620565b8114612d6757600080fd5b50565b612d738161263e565b8114612d7e57600080fd5b50565b612d8a81612668565b8114612d9557600080fd5b5056fe416464726573733a206c6f772d6c6576656c2064656c65676174652063616c6c206661696c6564a264697066735822122066b4ca7b81ce7dbc6af77b0a062c617f3c53c6ec81d80662160ff3cdcd358a8064736f6c63430008070033' - const immutableRef2 = '0x6080604052600436106101145760003560e01c806352d1902d116100a057806395d89b411161006457806395d89b4114610380578063a457c2d7146103ab578063a9059cbb146103e8578063dd62ed3e14610425578063f2fde38b1461046257610114565b806352d1902d146102bf57806370a08231146102ea578063715018a6146103275780638129fc1c1461033e5780638da5cb5b1461035557610114565b8063313ce567116100e7578063313ce567146101e95780633659cfe614610214578063395093511461023d57806340c10f191461027a5780634f1ef286146102a357610114565b806306fdde0314610119578063095ea7b31461014457806318160ddd1461018157806323b872dd146101ac575b600080fd5b34801561012557600080fd5b5061012e61048b565b60405161013b919061228a565b60405180910390f35b34801561015057600080fd5b5061016b60048036038101906101669190611e3d565b61051d565b6040516101789190612239565b60405180910390f35b34801561018d57600080fd5b50610196610540565b6040516101a3919061250c565b60405180910390f35b3480156101b857600080fd5b506101d360048036038101906101ce9190611d8e565b61054a565b6040516101e09190612239565b60405180910390f35b3480156101f557600080fd5b506101fe610579565b60405161020b9190612527565b60405180910390f35b34801561022057600080fd5b5061023b60048036038101906102369190611d21565b610582565b005b34801561024957600080fd5b50610264600480360381019061025f9190611e3d565b61070b565b6040516102719190612239565b60405180910390f35b34801561028657600080fd5b506102a1600480360381019061029c9190611e3d565b610742565b005b6102bd60048036038101906102b89190611de1565b610758565b005b3480156102cb57600080fd5b506102d4610895565b6040516102e19190612254565b60405180910390f35b3480156102f657600080fd5b50610311600480360381019061030c9190611d21565b61094e565b60405161031e919061250c565b60405180910390f35b34801561033357600080fd5b5061033c610997565b005b34801561034a57600080fd5b506103536109ab565b005b34801561036157600080fd5b5061036a610b65565b604051610377919061221e565b60405180910390f35b34801561038c57600080fd5b50610395610b8f565b6040516103a2919061228a565b60405180910390f35b3480156103b757600080fd5b506103d260048036038101906103cd9190611e3d565b610c21565b6040516103df9190612239565b60405180910390f35b3480156103f457600080fd5b5061040f600480360381019061040a9190611e3d565b610c98565b60405161041c9190612239565b60405180910390f35b34801561043157600080fd5b5061044c60048036038101906104479190611d4e565b610cbb565b604051610459919061250c565b60405180910390f35b34801561046e57600080fd5b5061048960048036038101906104849190611d21565b610d42565b005b60606036805461049a906126d3565b80601f01602080910402602001604051908101604052809291908181526020018280546104c6906126d3565b80156105135780601f106104e857610100808354040283529160200191610513565b820191906000526020600020905b8154815290600101906020018083116104f657829003601f168201915b5050505050905090565b600080610528610dc6565b9050610535818585610dce565b600191505092915050565b6000603554905090565b600080610555610dc6565b9050610562858285610f99565b61056d858585611025565b60019150509392505050565b60006012905090565b7f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff163073ffffffffffffffffffffffffffffffffffffffff161415610611576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016106089061230c565b60405180910390fd5b7f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff166106506112a0565b73ffffffffffffffffffffffffffffffffffffffff16146106a6576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161069d9061236c565b60405180910390fd5b6106af816112f7565b61070881600067ffffffffffffffff8111156106ce576106cd612794565b5b6040519080825280601f01601f1916602001820160405280156107005781602001600182028036833780820191505090505b506000611302565b50565b600080610716610dc6565b90506107378185856107288589610cbb565b61073291906125ca565b610dce565b600191505092915050565b61074a61147f565b61075482826114fd565b5050565b7f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff163073ffffffffffffffffffffffffffffffffffffffff1614156107e7576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016107de9061230c565b60405180910390fd5b7f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff166108266112a0565b73ffffffffffffffffffffffffffffffffffffffff161461087c576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016108739061236c565b60405180910390fd5b610885826112f7565b61089182826001611302565b5050565b60007f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff163073ffffffffffffffffffffffffffffffffffffffff1614610925576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161091c9061238c565b60405180910390fd5b7f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc60001b905090565b6000603360008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020549050919050565b61099f61147f565b6109a96000611655565b565b60008060019054906101000a900460ff161590508080156109dc5750600160008054906101000a900460ff1660ff16105b80610a0957506109eb3061171b565b158015610a085750600160008054906101000a900460ff1660ff16145b5b610a48576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610a3f906123cc565b60405180910390fd5b60016000806101000a81548160ff021916908360ff1602179055508015610a85576001600060016101000a81548160ff0219169083151502179055505b610af96040518060400160405280600781526020017f4d79546f6b656e000000000000000000000000000000000000000000000000008152506040518060400160405280600381526020017f4d544b000000000000000000000000000000000000000000000000000000000081525061173e565b610b0161179b565b610b096117f4565b8015610b625760008060016101000a81548160ff0219169083151502179055507f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb38474024986001604051610b59919061226f565b60405180910390a15b50565b6000606560009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905090565b606060378054610b9e906126d3565b80601f0160208091040260200160405190810160405280929190818152602001828054610bca906126d3565b8015610c175780601f10610bec57610100808354040283529160200191610c17565b820191906000526020600020905b815481529060010190602001808311610bfa57829003601f168201915b5050505050905090565b600080610c2c610dc6565b90506000610c3a8286610cbb565b905083811015610c7f576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610c76906124cc565b60405180910390fd5b610c8c8286868403610dce565b60019250505092915050565b600080610ca3610dc6565b9050610cb0818585611025565b600191505092915050565b6000603460008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054905092915050565b610d4a61147f565b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff161415610dba576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610db1906122cc565b60405180910390fd5b610dc381611655565b50565b600033905090565b600073ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff161415610e3e576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610e359061248c565b60405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff161415610eae576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610ea5906122ec565b60405180910390fd5b80603460008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055508173ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92583604051610f8c919061250c565b60405180910390a3505050565b6000610fa58484610cbb565b90507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff811461101f5781811015611011576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016110089061232c565b60405180910390fd5b61101e8484848403610dce565b5b50505050565b600073ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff161415611095576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161108c9061246c565b60405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff161415611105576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016110fc906122ac565b60405180910390fd5b611110838383611845565b6000603360008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054905081811015611197576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161118e9061234c565b60405180910390fd5b818103603360008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000208190555081603360008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825401925050819055508273ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef84604051611287919061250c565b60405180910390a361129a84848461184a565b50505050565b60006112ce7f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc60001b61184f565b60000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905090565b6112ff61147f565b50565b61132e7f4910fdfa16fed3260ed0e7147f7cc6da11a60208b5b9406d12a635614ffd914360001b611859565b60000160009054906101000a900460ff16156113525761134d83611863565b61147a565b8273ffffffffffffffffffffffffffffffffffffffff166352d1902d6040518163ffffffff1660e01b815260040160206040518083038186803b15801561139857600080fd5b505afa9250505080156113c957506040513d601f19601f820116820180604052508101906113c69190611e7d565b60015b611408576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016113ff906123ec565b60405180910390fd5b7f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc60001b811461146d576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401611464906123ac565b60405180910390fd5b5061147983838361191c565b5b505050565b611487610dc6565b73ffffffffffffffffffffffffffffffffffffffff166114a5610b65565b73ffffffffffffffffffffffffffffffffffffffff16146114fb576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016114f29061242c565b60405180910390fd5b565b600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff16141561156d576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401611564906124ec565b60405180910390fd5b61157960008383611845565b806035600082825461158b91906125ca565b9250508190555080603360008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825401925050819055508173ffffffffffffffffffffffffffffffffffffffff16600073ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef8360405161163d919061250c565b60405180910390a36116516000838361184a565b5050565b6000606560009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905081606560006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055508173ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a35050565b6000808273ffffffffffffffffffffffffffffffffffffffff163b119050919050565b600060019054906101000a900460ff1661178d576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401611784906124ac565b60405180910390fd5b6117978282611948565b5050565b600060019054906101000a900460ff166117ea576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016117e1906124ac565b60405180910390fd5b6117f26119c9565b565b600060019054906101000a900460ff16611843576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161183a906124ac565b60405180910390fd5b565b505050565b505050565b6000819050919050565b6000819050919050565b61186c8161171b565b6118ab576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016118a29061240c565b60405180910390fd5b806118d87f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc60001b61184f565b60000160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b61192583611a2a565b6000825111806119325750805b15611943576119418383611a79565b505b505050565b600060019054906101000a900460ff16611997576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161198e906124ac565b60405180910390fd5b81603690805190602001906119ad929190611bcf565b5080603790805190602001906119c4929190611bcf565b505050565b600060019054906101000a900460ff16611a18576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401611a0f906124ac565b60405180910390fd5b611a28611a23610dc6565b611655565b565b611a3381611863565b8073ffffffffffffffffffffffffffffffffffffffff167fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b60405160405180910390a250565b6060611a848361171b565b611ac3576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401611aba9061244c565b60405180910390fd5b6000808473ffffffffffffffffffffffffffffffffffffffff1684604051611aeb9190612207565b600060405180830381855af49150503d8060008114611b26576040519150601f19603f3d011682016040523d82523d6000602084013e611b2b565b606091505b5091509150611b538282604051806060016040528060278152602001612d9960279139611b5d565b9250505092915050565b60608315611b6d57829050611b78565b611b778383611b7f565b5b9392505050565b600082511115611b925781518083602001fd5b806040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401611bc6919061228a565b60405180910390fd5b828054611bdb906126d3565b90600052602060002090601f016020900481019282611bfd5760008555611c44565b82601f10611c1657805160ff1916838001178555611c44565b82800160010185558215611c44579182015b82811115611c43578251825591602001919060010190611c28565b5b509050611c519190611c55565b5090565b5b80821115611c6e576000816000905550600101611c56565b5090565b6000611c85611c8084612567565b612542565b905082815260208101848484011115611ca157611ca06127c8565b5b611cac848285612691565b509392505050565b600081359050611cc381612d53565b92915050565b600081519050611cd881612d6a565b92915050565b600082601f830112611cf357611cf26127c3565b5b8135611d03848260208601611c72565b91505092915050565b600081359050611d1b81612d81565b92915050565b600060208284031215611d3757611d366127d2565b5b6000611d4584828501611cb4565b91505092915050565b60008060408385031215611d6557611d646127d2565b5b6000611d7385828601611cb4565b9250506020611d8485828601611cb4565b9150509250929050565b600080600060608486031215611da757611da66127d2565b5b6000611db586828701611cb4565b9350506020611dc686828701611cb4565b9250506040611dd786828701611d0c565b9150509250925092565b60008060408385031215611df857611df76127d2565b5b6000611e0685828601611cb4565b925050602083013567ffffffffffffffff811115611e2757611e266127cd565b5b611e3385828601611cde565b9150509250929050565b60008060408385031215611e5457611e536127d2565b5b6000611e6285828601611cb4565b9250506020611e7385828601611d0c565b9150509250929050565b600060208284031215611e9357611e926127d2565b5b6000611ea184828501611cc9565b91505092915050565b611eb381612620565b82525050565b611ec281612632565b82525050565b611ed18161263e565b82525050565b6000611ee282612598565b611eec81856125ae565b9350611efc8185602086016126a0565b80840191505092915050565b611f118161267f565b82525050565b6000611f22826125a3565b611f2c81856125b9565b9350611f3c8185602086016126a0565b611f45816127d7565b840191505092915050565b6000611f5d6023836125b9565b9150611f68826127e8565b604082019050919050565b6000611f806026836125b9565b9150611f8b82612837565b604082019050919050565b6000611fa36022836125b9565b9150611fae82612886565b604082019050919050565b6000611fc6602c836125b9565b9150611fd1826128d5565b604082019050919050565b6000611fe9601d836125b9565b9150611ff482612924565b602082019050919050565b600061200c6026836125b9565b91506120178261294d565b604082019050919050565b600061202f602c836125b9565b915061203a8261299c565b604082019050919050565b60006120526038836125b9565b915061205d826129eb565b604082019050919050565b60006120756029836125b9565b915061208082612a3a565b604082019050919050565b6000612098602e836125b9565b91506120a382612a89565b604082019050919050565b60006120bb602e836125b9565b91506120c682612ad8565b604082019050919050565b60006120de602d836125b9565b91506120e982612b27565b604082019050919050565b60006121016020836125b9565b915061210c82612b76565b602082019050919050565b60006121246026836125b9565b915061212f82612b9f565b604082019050919050565b60006121476025836125b9565b915061215282612bee565b604082019050919050565b600061216a6024836125b9565b915061217582612c3d565b604082019050919050565b600061218d602b836125b9565b915061219882612c8c565b604082019050919050565b60006121b06025836125b9565b91506121bb82612cdb565b604082019050919050565b60006121d3601f836125b9565b91506121de82612d2a565b602082019050919050565b6121f281612668565b82525050565b61220181612672565b82525050565b60006122138284611ed7565b915081905092915050565b60006020820190506122336000830184611eaa565b92915050565b600060208201905061224e6000830184611eb9565b92915050565b60006020820190506122696000830184611ec8565b92915050565b60006020820190506122846000830184611f08565b92915050565b600060208201905081810360008301526122a48184611f17565b905092915050565b600060208201905081810360008301526122c581611f50565b9050919050565b600060208201905081810360008301526122e581611f73565b9050919050565b6000602082019050818103600083015261230581611f96565b9050919050565b6000602082019050818103600083015261232581611fb9565b9050919050565b6000602082019050818103600083015261234581611fdc565b9050919050565b6000602082019050818103600083015261236581611fff565b9050919050565b6000602082019050818103600083015261238581612022565b9050919050565b600060208201905081810360008301526123a581612045565b9050919050565b600060208201905081810360008301526123c581612068565b9050919050565b600060208201905081810360008301526123e58161208b565b9050919050565b60006020820190508181036000830152612405816120ae565b9050919050565b60006020820190508181036000830152612425816120d1565b9050919050565b60006020820190508181036000830152612445816120f4565b9050919050565b6000602082019050818103600083015261246581612117565b9050919050565b600060208201905081810360008301526124858161213a565b9050919050565b600060208201905081810360008301526124a58161215d565b9050919050565b600060208201905081810360008301526124c581612180565b9050919050565b600060208201905081810360008301526124e5816121a3565b9050919050565b60006020820190508181036000830152612505816121c6565b9050919050565b600060208201905061252160008301846121e9565b92915050565b600060208201905061253c60008301846121f8565b92915050565b600061254c61255d565b90506125588282612705565b919050565b6000604051905090565b600067ffffffffffffffff82111561258257612581612794565b5b61258b826127d7565b9050602081019050919050565b600081519050919050565b600081519050919050565b600081905092915050565b600082825260208201905092915050565b60006125d582612668565b91506125e083612668565b9250827fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0382111561261557612614612736565b5b828201905092915050565b600061262b82612648565b9050919050565b60008115159050919050565b6000819050919050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b6000819050919050565b600060ff82169050919050565b600061268a82612672565b9050919050565b82818337600083830152505050565b60005b838110156126be5780820151818401526020810190506126a3565b838111156126cd576000848401525b50505050565b600060028204905060018216806126eb57607f821691505b602082108114156126ff576126fe612765565b5b50919050565b61270e826127d7565b810181811067ffffffffffffffff8211171561272d5761272c612794565b5b80604052505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b600080fd5b600080fd5b600080fd5b600080fd5b6000601f19601f8301169050919050565b7f45524332303a207472616e7366657220746f20746865207a65726f206164647260008201527f6573730000000000000000000000000000000000000000000000000000000000602082015250565b7f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160008201527f6464726573730000000000000000000000000000000000000000000000000000602082015250565b7f45524332303a20617070726f766520746f20746865207a65726f20616464726560008201527f7373000000000000000000000000000000000000000000000000000000000000602082015250565b7f46756e6374696f6e206d7573742062652063616c6c6564207468726f7567682060008201527f64656c656761746563616c6c0000000000000000000000000000000000000000602082015250565b7f45524332303a20696e73756666696369656e7420616c6c6f77616e6365000000600082015250565b7f45524332303a207472616e7366657220616d6f756e742065786365656473206260008201527f616c616e63650000000000000000000000000000000000000000000000000000602082015250565b7f46756e6374696f6e206d7573742062652063616c6c6564207468726f7567682060008201527f6163746976652070726f78790000000000000000000000000000000000000000602082015250565b7f555550535570677261646561626c653a206d757374206e6f742062652063616c60008201527f6c6564207468726f7567682064656c656761746563616c6c0000000000000000602082015250565b7f45524331393637557067726164653a20756e737570706f727465642070726f7860008201527f6961626c65555549440000000000000000000000000000000000000000000000602082015250565b7f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160008201527f647920696e697469616c697a6564000000000000000000000000000000000000602082015250565b7f45524331393637557067726164653a206e657720696d706c656d656e7461746960008201527f6f6e206973206e6f742055555053000000000000000000000000000000000000602082015250565b7f455243313936373a206e657720696d706c656d656e746174696f6e206973206e60008201527f6f74206120636f6e747261637400000000000000000000000000000000000000602082015250565b7f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572600082015250565b7f416464726573733a2064656c65676174652063616c6c20746f206e6f6e2d636f60008201527f6e74726163740000000000000000000000000000000000000000000000000000602082015250565b7f45524332303a207472616e736665722066726f6d20746865207a65726f20616460008201527f6472657373000000000000000000000000000000000000000000000000000000602082015250565b7f45524332303a20617070726f76652066726f6d20746865207a65726f2061646460008201527f7265737300000000000000000000000000000000000000000000000000000000602082015250565b7f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960008201527f6e697469616c697a696e67000000000000000000000000000000000000000000602082015250565b7f45524332303a2064656372656173656420616c6c6f77616e63652062656c6f7760008201527f207a65726f000000000000000000000000000000000000000000000000000000602082015250565b7f45524332303a206d696e7420746f20746865207a65726f206164647265737300600082015250565b612d5c81612620565b8114612d6757600080fd5b50565b612d738161263e565b8114612d7e57600080fd5b50565b612d8a81612668565b8114612d9557600080fd5b5056fe416464726573733a206c6f772d6c6576656c2064656c65676174652063616c6c206661696c6564a264697066735822122066b4ca7b81ce7dbc6af77b0a062c617f3c53c6ec81d80662160ff3cdcd358a8064736f6c63430008070033' - t.ok(util.compareByteCode(immutableRef1, immutableRef2), 'code contains immutable refs and should match') + const immutableRef1 = '0x6080604052600436106101145760003560e01c806352d1902d116100a057806395d89b411161006457806395d89b4114610380578063a457c2d7146103ab578063a9059cbb146103e8578063dd62ed3e14610425578063f2fde38b1461046257610114565b806352d1902d146102bf57806370a08231146102ea578063715018a6146103275780638129fc1c1461033e5780638da5cb5b1461035557610114565b8063313ce567116100e7578063313ce567146101e95780633659cfe614610214578063395093511461023d57806340c10f191461027a5780634f1ef286146102a357610114565b806306fdde0314610119578063095ea7b31461014457806318160ddd1461018157806323b872dd146101ac575b600080fd5b34801561012557600080fd5b5061012e61048b565b60405161013b919061228a565b60405180910390f35b34801561015057600080fd5b5061016b60048036038101906101669190611e3d565b61051d565b6040516101789190612239565b60405180910390f35b34801561018d57600080fd5b50610196610540565b6040516101a3919061250c565b60405180910390f35b3480156101b857600080fd5b506101d360048036038101906101ce9190611d8e565b61054a565b6040516101e09190612239565b60405180910390f35b3480156101f557600080fd5b506101fe610579565b60405161020b9190612527565b60405180910390f35b34801561022057600080fd5b5061023b60048036038101906102369190611d21565b610582565b005b34801561024957600080fd5b50610264600480360381019061025f9190611e3d565b61070b565b6040516102719190612239565b60405180910390f35b34801561028657600080fd5b506102a1600480360381019061029c9190611e3d565b610742565b005b6102bd60048036038101906102b89190611de1565b610758565b005b3480156102cb57600080fd5b506102d4610895565b6040516102e19190612254565b60405180910390f35b3480156102f657600080fd5b50610311600480360381019061030c9190611d21565b61094e565b60405161031e919061250c565b60405180910390f35b34801561033357600080fd5b5061033c610997565b005b34801561034a57600080fd5b506103536109ab565b005b34801561036157600080fd5b5061036a610b65565b604051610377919061221e565b60405180910390f35b34801561038c57600080fd5b50610395610b8f565b6040516103a2919061228a565b60405180910390f35b3480156103b757600080fd5b506103d260048036038101906103cd9190611e3d565b610c21565b6040516103df9190612239565b60405180910390f35b3480156103f457600080fd5b5061040f600480360381019061040a9190611e3d565b610c98565b60405161041c9190612239565b60405180910390f35b34801561043157600080fd5b5061044c60048036038101906104479190611d4e565b610cbb565b604051610459919061250c565b60405180910390f35b34801561046e57600080fd5b5061048960048036038101906104849190611d21565b610d42565b005b60606036805461049a906126d3565b80601f01602080910402602001604051908101604052809291908181526020018280546104c6906126d3565b80156105135780601f106104e857610100808354040283529160200191610513565b820191906000526020600020905b8154815290600101906020018083116104f657829003601f168201915b5050505050905090565b600080610528610dc6565b9050610535818585610dce565b600191505092915050565b6000603554905090565b600080610555610dc6565b9050610562858285610f99565b61056d858585611025565b60019150509392505050565b60006012905090565b7f000000000000000000000000d9145cce52d386f254917e481eb44e9943f3913873ffffffffffffffffffffffffffffffffffffffff163073ffffffffffffffffffffffffffffffffffffffff161415610611576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016106089061230c565b60405180910390fd5b7f000000000000000000000000d9145cce52d386f254917e481eb44e9943f3913873ffffffffffffffffffffffffffffffffffffffff166106506112a0565b73ffffffffffffffffffffffffffffffffffffffff16146106a6576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161069d9061236c565b60405180910390fd5b6106af816112f7565b61070881600067ffffffffffffffff8111156106ce576106cd612794565b5b6040519080825280601f01601f1916602001820160405280156107005781602001600182028036833780820191505090505b506000611302565b50565b600080610716610dc6565b90506107378185856107288589610cbb565b61073291906125ca565b610dce565b600191505092915050565b61074a61147f565b61075482826114fd565b5050565b7f000000000000000000000000d9145cce52d386f254917e481eb44e9943f3913873ffffffffffffffffffffffffffffffffffffffff163073ffffffffffffffffffffffffffffffffffffffff1614156107e7576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016107de9061230c565b60405180910390fd5b7f000000000000000000000000d9145cce52d386f254917e481eb44e9943f3913873ffffffffffffffffffffffffffffffffffffffff166108266112a0565b73ffffffffffffffffffffffffffffffffffffffff161461087c576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016108739061236c565b60405180910390fd5b610885826112f7565b61089182826001611302565b5050565b60007f000000000000000000000000d9145cce52d386f254917e481eb44e9943f3913873ffffffffffffffffffffffffffffffffffffffff163073ffffffffffffffffffffffffffffffffffffffff1614610925576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161091c9061238c565b60405180910390fd5b7f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc60001b905090565b6000603360008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020549050919050565b61099f61147f565b6109a96000611655565b565b60008060019054906101000a900460ff161590508080156109dc5750600160008054906101000a900460ff1660ff16105b80610a0957506109eb3061171b565b158015610a085750600160008054906101000a900460ff1660ff16145b5b610a48576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610a3f906123cc565b60405180910390fd5b60016000806101000a81548160ff021916908360ff1602179055508015610a85576001600060016101000a81548160ff0219169083151502179055505b610af96040518060400160405280600781526020017f4d79546f6b656e000000000000000000000000000000000000000000000000008152506040518060400160405280600381526020017f4d544b000000000000000000000000000000000000000000000000000000000081525061173e565b610b0161179b565b610b096117f4565b8015610b625760008060016101000a81548160ff0219169083151502179055507f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb38474024986001604051610b59919061226f565b60405180910390a15b50565b6000606560009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905090565b606060378054610b9e906126d3565b80601f0160208091040260200160405190810160405280929190818152602001828054610bca906126d3565b8015610c175780601f10610bec57610100808354040283529160200191610c17565b820191906000526020600020905b815481529060010190602001808311610bfa57829003601f168201915b5050505050905090565b600080610c2c610dc6565b90506000610c3a8286610cbb565b905083811015610c7f576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610c76906124cc565b60405180910390fd5b610c8c8286868403610dce565b60019250505092915050565b600080610ca3610dc6565b9050610cb0818585611025565b600191505092915050565b6000603460008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054905092915050565b610d4a61147f565b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff161415610dba576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610db1906122cc565b60405180910390fd5b610dc381611655565b50565b600033905090565b600073ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff161415610e3e576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610e359061248c565b60405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff161415610eae576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610ea5906122ec565b60405180910390fd5b80603460008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055508173ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92583604051610f8c919061250c565b60405180910390a3505050565b6000610fa58484610cbb565b90507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff811461101f5781811015611011576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016110089061232c565b60405180910390fd5b61101e8484848403610dce565b5b50505050565b600073ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff161415611095576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161108c9061246c565b60405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff161415611105576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016110fc906122ac565b60405180910390fd5b611110838383611845565b6000603360008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054905081811015611197576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161118e9061234c565b60405180910390fd5b818103603360008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000208190555081603360008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825401925050819055508273ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef84604051611287919061250c565b60405180910390a361129a84848461184a565b50505050565b60006112ce7f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc60001b61184f565b60000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905090565b6112ff61147f565b50565b61132e7f4910fdfa16fed3260ed0e7147f7cc6da11a60208b5b9406d12a635614ffd914360001b611859565b60000160009054906101000a900460ff16156113525761134d83611863565b61147a565b8273ffffffffffffffffffffffffffffffffffffffff166352d1902d6040518163ffffffff1660e01b815260040160206040518083038186803b15801561139857600080fd5b505afa9250505080156113c957506040513d601f19601f820116820180604052508101906113c69190611e7d565b60015b611408576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016113ff906123ec565b60405180910390fd5b7f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc60001b811461146d576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401611464906123ac565b60405180910390fd5b5061147983838361191c565b5b505050565b611487610dc6565b73ffffffffffffffffffffffffffffffffffffffff166114a5610b65565b73ffffffffffffffffffffffffffffffffffffffff16146114fb576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016114f29061242c565b60405180910390fd5b565b600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff16141561156d576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401611564906124ec565b60405180910390fd5b61157960008383611845565b806035600082825461158b91906125ca565b9250508190555080603360008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825401925050819055508173ffffffffffffffffffffffffffffffffffffffff16600073ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef8360405161163d919061250c565b60405180910390a36116516000838361184a565b5050565b6000606560009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905081606560006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055508173ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a35050565b6000808273ffffffffffffffffffffffffffffffffffffffff163b119050919050565b600060019054906101000a900460ff1661178d576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401611784906124ac565b60405180910390fd5b6117978282611948565b5050565b600060019054906101000a900460ff166117ea576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016117e1906124ac565b60405180910390fd5b6117f26119c9565b565b600060019054906101000a900460ff16611843576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161183a906124ac565b60405180910390fd5b565b505050565b505050565b6000819050919050565b6000819050919050565b61186c8161171b565b6118ab576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016118a29061240c565b60405180910390fd5b806118d87f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc60001b61184f565b60000160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b61192583611a2a565b6000825111806119325750805b15611943576119418383611a79565b505b505050565b600060019054906101000a900460ff16611997576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161198e906124ac565b60405180910390fd5b81603690805190602001906119ad929190611bcf565b5080603790805190602001906119c4929190611bcf565b505050565b600060019054906101000a900460ff16611a18576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401611a0f906124ac565b60405180910390fd5b611a28611a23610dc6565b611655565b565b611a3381611863565b8073ffffffffffffffffffffffffffffffffffffffff167fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b60405160405180910390a250565b6060611a848361171b565b611ac3576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401611aba9061244c565b60405180910390fd5b6000808473ffffffffffffffffffffffffffffffffffffffff1684604051611aeb9190612207565b600060405180830381855af49150503d8060008114611b26576040519150601f19603f3d011682016040523d82523d6000602084013e611b2b565b606091505b5091509150611b538282604051806060016040528060278152602001612d9960279139611b5d565b9250505092915050565b60608315611b6d57829050611b78565b611b778383611b7f565b5b9392505050565b600082511115611b925781518083602001fd5b806040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401611bc6919061228a565b60405180910390fd5b828054611bdb906126d3565b90600052602060002090601f016020900481019282611bfd5760008555611c44565b82601f10611c1657805160ff1916838001178555611c44565b82800160010185558215611c44579182015b82811115611c43578251825591602001919060010190611c28565b5b509050611c519190611c55565b5090565b5b80821115611c6e576000816000905550600101611c56565b5090565b6000611c85611c8084612567565b612542565b905082815260208101848484011115611ca157611ca06127c8565b5b611cac848285612691565b509392505050565b600081359050611cc381612d53565b92915050565b600081519050611cd881612d6a565b92915050565b600082601f830112611cf357611cf26127c3565b5b8135611d03848260208601611c72565b91505092915050565b600081359050611d1b81612d81565b92915050565b600060208284031215611d3757611d366127d2565b5b6000611d4584828501611cb4565b91505092915050565b60008060408385031215611d6557611d646127d2565b5b6000611d7385828601611cb4565b9250506020611d8485828601611cb4565b9150509250929050565b600080600060608486031215611da757611da66127d2565b5b6000611db586828701611cb4565b9350506020611dc686828701611cb4565b9250506040611dd786828701611d0c565b9150509250925092565b60008060408385031215611df857611df76127d2565b5b6000611e0685828601611cb4565b925050602083013567ffffffffffffffff811115611e2757611e266127cd565b5b611e3385828601611cde565b9150509250929050565b60008060408385031215611e5457611e536127d2565b5b6000611e6285828601611cb4565b9250506020611e7385828601611d0c565b9150509250929050565b600060208284031215611e9357611e926127d2565b5b6000611ea184828501611cc9565b91505092915050565b611eb381612620565b82525050565b611ec281612632565b82525050565b611ed18161263e565b82525050565b6000611ee282612598565b611eec81856125ae565b9350611efc8185602086016126a0565b80840191505092915050565b611f118161267f565b82525050565b6000611f22826125a3565b611f2c81856125b9565b9350611f3c8185602086016126a0565b611f45816127d7565b840191505092915050565b6000611f5d6023836125b9565b9150611f68826127e8565b604082019050919050565b6000611f806026836125b9565b9150611f8b82612837565b604082019050919050565b6000611fa36022836125b9565b9150611fae82612886565b604082019050919050565b6000611fc6602c836125b9565b9150611fd1826128d5565b604082019050919050565b6000611fe9601d836125b9565b9150611ff482612924565b602082019050919050565b600061200c6026836125b9565b91506120178261294d565b604082019050919050565b600061202f602c836125b9565b915061203a8261299c565b604082019050919050565b60006120526038836125b9565b915061205d826129eb565b604082019050919050565b60006120756029836125b9565b915061208082612a3a565b604082019050919050565b6000612098602e836125b9565b91506120a382612a89565b604082019050919050565b60006120bb602e836125b9565b91506120c682612ad8565b604082019050919050565b60006120de602d836125b9565b91506120e982612b27565b604082019050919050565b60006121016020836125b9565b915061210c82612b76565b602082019050919050565b60006121246026836125b9565b915061212f82612b9f565b604082019050919050565b60006121476025836125b9565b915061215282612bee565b604082019050919050565b600061216a6024836125b9565b915061217582612c3d565b604082019050919050565b600061218d602b836125b9565b915061219882612c8c565b604082019050919050565b60006121b06025836125b9565b91506121bb82612cdb565b604082019050919050565b60006121d3601f836125b9565b91506121de82612d2a565b602082019050919050565b6121f281612668565b82525050565b61220181612672565b82525050565b60006122138284611ed7565b915081905092915050565b60006020820190506122336000830184611eaa565b92915050565b600060208201905061224e6000830184611eb9565b92915050565b60006020820190506122696000830184611ec8565b92915050565b60006020820190506122846000830184611f08565b92915050565b600060208201905081810360008301526122a48184611f17565b905092915050565b600060208201905081810360008301526122c581611f50565b9050919050565b600060208201905081810360008301526122e581611f73565b9050919050565b6000602082019050818103600083015261230581611f96565b9050919050565b6000602082019050818103600083015261232581611fb9565b9050919050565b6000602082019050818103600083015261234581611fdc565b9050919050565b6000602082019050818103600083015261236581611fff565b9050919050565b6000602082019050818103600083015261238581612022565b9050919050565b600060208201905081810360008301526123a581612045565b9050919050565b600060208201905081810360008301526123c581612068565b9050919050565b600060208201905081810360008301526123e58161208b565b9050919050565b60006020820190508181036000830152612405816120ae565b9050919050565b60006020820190508181036000830152612425816120d1565b9050919050565b60006020820190508181036000830152612445816120f4565b9050919050565b6000602082019050818103600083015261246581612117565b9050919050565b600060208201905081810360008301526124858161213a565b9050919050565b600060208201905081810360008301526124a58161215d565b9050919050565b600060208201905081810360008301526124c581612180565b9050919050565b600060208201905081810360008301526124e5816121a3565b9050919050565b60006020820190508181036000830152612505816121c6565b9050919050565b600060208201905061252160008301846121e9565b92915050565b600060208201905061253c60008301846121f8565b92915050565b600061254c61255d565b90506125588282612705565b919050565b6000604051905090565b600067ffffffffffffffff82111561258257612581612794565b5b61258b826127d7565b9050602081019050919050565b600081519050919050565b600081519050919050565b600081905092915050565b600082825260208201905092915050565b60006125d582612668565b91506125e083612668565b9250827fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0382111561261557612614612736565b5b828201905092915050565b600061262b82612648565b9050919050565b60008115159050919050565b6000819050919050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b6000819050919050565b600060ff82169050919050565b600061268a82612672565b9050919050565b82818337600083830152505050565b60005b838110156126be5780820151818401526020810190506126a3565b838111156126cd576000848401525b50505050565b600060028204905060018216806126eb57607f821691505b602082108114156126ff576126fe612765565b5b50919050565b61270e826127d7565b810181811067ffffffffffffffff8211171561272d5761272c612794565b5b80604052505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b600080fd5b600080fd5b600080fd5b600080fd5b6000601f19601f8301169050919050565b7f45524332303a207472616e7366657220746f20746865207a65726f206164647260008201527f6573730000000000000000000000000000000000000000000000000000000000602082015250565b7f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160008201527f6464726573730000000000000000000000000000000000000000000000000000602082015250565b7f45524332303a20617070726f766520746f20746865207a65726f20616464726560008201527f7373000000000000000000000000000000000000000000000000000000000000602082015250565b7f46756e6374696f6e206d7573742062652063616c6c6564207468726f7567682060008201527f64656c656761746563616c6c0000000000000000000000000000000000000000602082015250565b7f45524332303a20696e73756666696369656e7420616c6c6f77616e6365000000600082015250565b7f45524332303a207472616e7366657220616d6f756e742065786365656473206260008201527f616c616e63650000000000000000000000000000000000000000000000000000602082015250565b7f46756e6374696f6e206d7573742062652063616c6c6564207468726f7567682060008201527f6163746976652070726f78790000000000000000000000000000000000000000602082015250565b7f555550535570677261646561626c653a206d757374206e6f742062652063616c60008201527f6c6564207468726f7567682064656c656761746563616c6c0000000000000000602082015250565b7f45524331393637557067726164653a20756e737570706f727465642070726f7860008201527f6961626c65555549440000000000000000000000000000000000000000000000602082015250565b7f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160008201527f647920696e697469616c697a6564000000000000000000000000000000000000602082015250565b7f45524331393637557067726164653a206e657720696d706c656d656e7461746960008201527f6f6e206973206e6f742055555053000000000000000000000000000000000000602082015250565b7f455243313936373a206e657720696d706c656d656e746174696f6e206973206e60008201527f6f74206120636f6e747261637400000000000000000000000000000000000000602082015250565b7f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572600082015250565b7f416464726573733a2064656c65676174652063616c6c20746f206e6f6e2d636f60008201527f6e74726163740000000000000000000000000000000000000000000000000000602082015250565b7f45524332303a207472616e736665722066726f6d20746865207a65726f20616460008201527f6472657373000000000000000000000000000000000000000000000000000000602082015250565b7f45524332303a20617070726f76652066726f6d20746865207a65726f2061646460008201527f7265737300000000000000000000000000000000000000000000000000000000602082015250565b7f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960008201527f6e697469616c697a696e67000000000000000000000000000000000000000000602082015250565b7f45524332303a2064656372656173656420616c6c6f77616e63652062656c6f7760008201527f207a65726f000000000000000000000000000000000000000000000000000000602082015250565b7f45524332303a206d696e7420746f20746865207a65726f206164647265737300600082015250565b612d5c81612620565b8114612d6757600080fd5b50565b612d738161263e565b8114612d7e57600080fd5b50565b612d8a81612668565b8114612d9557600080fd5b5056fe416464726573733a206c6f772d6c6576656c2064656c65676174652063616c6c206661696c6564a264697066735822122066b4ca7b81ce7dbc6af77b0a062c617f3c53c6ec81d80662160ff3cdcd358a8064736f6c63430008070033' + const immutableRef2 = '0x6080604052600436106101145760003560e01c806352d1902d116100a057806395d89b411161006457806395d89b4114610380578063a457c2d7146103ab578063a9059cbb146103e8578063dd62ed3e14610425578063f2fde38b1461046257610114565b806352d1902d146102bf57806370a08231146102ea578063715018a6146103275780638129fc1c1461033e5780638da5cb5b1461035557610114565b8063313ce567116100e7578063313ce567146101e95780633659cfe614610214578063395093511461023d57806340c10f191461027a5780634f1ef286146102a357610114565b806306fdde0314610119578063095ea7b31461014457806318160ddd1461018157806323b872dd146101ac575b600080fd5b34801561012557600080fd5b5061012e61048b565b60405161013b919061228a565b60405180910390f35b34801561015057600080fd5b5061016b60048036038101906101669190611e3d565b61051d565b6040516101789190612239565b60405180910390f35b34801561018d57600080fd5b50610196610540565b6040516101a3919061250c565b60405180910390f35b3480156101b857600080fd5b506101d360048036038101906101ce9190611d8e565b61054a565b6040516101e09190612239565b60405180910390f35b3480156101f557600080fd5b506101fe610579565b60405161020b9190612527565b60405180910390f35b34801561022057600080fd5b5061023b60048036038101906102369190611d21565b610582565b005b34801561024957600080fd5b50610264600480360381019061025f9190611e3d565b61070b565b6040516102719190612239565b60405180910390f35b34801561028657600080fd5b506102a1600480360381019061029c9190611e3d565b610742565b005b6102bd60048036038101906102b89190611de1565b610758565b005b3480156102cb57600080fd5b506102d4610895565b6040516102e19190612254565b60405180910390f35b3480156102f657600080fd5b50610311600480360381019061030c9190611d21565b61094e565b60405161031e919061250c565b60405180910390f35b34801561033357600080fd5b5061033c610997565b005b34801561034a57600080fd5b506103536109ab565b005b34801561036157600080fd5b5061036a610b65565b604051610377919061221e565b60405180910390f35b34801561038c57600080fd5b50610395610b8f565b6040516103a2919061228a565b60405180910390f35b3480156103b757600080fd5b506103d260048036038101906103cd9190611e3d565b610c21565b6040516103df9190612239565b60405180910390f35b3480156103f457600080fd5b5061040f600480360381019061040a9190611e3d565b610c98565b60405161041c9190612239565b60405180910390f35b34801561043157600080fd5b5061044c60048036038101906104479190611d4e565b610cbb565b604051610459919061250c565b60405180910390f35b34801561046e57600080fd5b5061048960048036038101906104849190611d21565b610d42565b005b60606036805461049a906126d3565b80601f01602080910402602001604051908101604052809291908181526020018280546104c6906126d3565b80156105135780601f106104e857610100808354040283529160200191610513565b820191906000526020600020905b8154815290600101906020018083116104f657829003601f168201915b5050505050905090565b600080610528610dc6565b9050610535818585610dce565b600191505092915050565b6000603554905090565b600080610555610dc6565b9050610562858285610f99565b61056d858585611025565b60019150509392505050565b60006012905090565b7f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff163073ffffffffffffffffffffffffffffffffffffffff161415610611576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016106089061230c565b60405180910390fd5b7f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff166106506112a0565b73ffffffffffffffffffffffffffffffffffffffff16146106a6576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161069d9061236c565b60405180910390fd5b6106af816112f7565b61070881600067ffffffffffffffff8111156106ce576106cd612794565b5b6040519080825280601f01601f1916602001820160405280156107005781602001600182028036833780820191505090505b506000611302565b50565b600080610716610dc6565b90506107378185856107288589610cbb565b61073291906125ca565b610dce565b600191505092915050565b61074a61147f565b61075482826114fd565b5050565b7f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff163073ffffffffffffffffffffffffffffffffffffffff1614156107e7576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016107de9061230c565b60405180910390fd5b7f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff166108266112a0565b73ffffffffffffffffffffffffffffffffffffffff161461087c576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016108739061236c565b60405180910390fd5b610885826112f7565b61089182826001611302565b5050565b60007f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff163073ffffffffffffffffffffffffffffffffffffffff1614610925576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161091c9061238c565b60405180910390fd5b7f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc60001b905090565b6000603360008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020549050919050565b61099f61147f565b6109a96000611655565b565b60008060019054906101000a900460ff161590508080156109dc5750600160008054906101000a900460ff1660ff16105b80610a0957506109eb3061171b565b158015610a085750600160008054906101000a900460ff1660ff16145b5b610a48576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610a3f906123cc565b60405180910390fd5b60016000806101000a81548160ff021916908360ff1602179055508015610a85576001600060016101000a81548160ff0219169083151502179055505b610af96040518060400160405280600781526020017f4d79546f6b656e000000000000000000000000000000000000000000000000008152506040518060400160405280600381526020017f4d544b000000000000000000000000000000000000000000000000000000000081525061173e565b610b0161179b565b610b096117f4565b8015610b625760008060016101000a81548160ff0219169083151502179055507f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb38474024986001604051610b59919061226f565b60405180910390a15b50565b6000606560009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905090565b606060378054610b9e906126d3565b80601f0160208091040260200160405190810160405280929190818152602001828054610bca906126d3565b8015610c175780601f10610bec57610100808354040283529160200191610c17565b820191906000526020600020905b815481529060010190602001808311610bfa57829003601f168201915b5050505050905090565b600080610c2c610dc6565b90506000610c3a8286610cbb565b905083811015610c7f576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610c76906124cc565b60405180910390fd5b610c8c8286868403610dce565b60019250505092915050565b600080610ca3610dc6565b9050610cb0818585611025565b600191505092915050565b6000603460008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054905092915050565b610d4a61147f565b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff161415610dba576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610db1906122cc565b60405180910390fd5b610dc381611655565b50565b600033905090565b600073ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff161415610e3e576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610e359061248c565b60405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff161415610eae576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610ea5906122ec565b60405180910390fd5b80603460008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055508173ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92583604051610f8c919061250c565b60405180910390a3505050565b6000610fa58484610cbb565b90507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff811461101f5781811015611011576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016110089061232c565b60405180910390fd5b61101e8484848403610dce565b5b50505050565b600073ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff161415611095576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161108c9061246c565b60405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff161415611105576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016110fc906122ac565b60405180910390fd5b611110838383611845565b6000603360008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054905081811015611197576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161118e9061234c565b60405180910390fd5b818103603360008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000208190555081603360008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825401925050819055508273ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef84604051611287919061250c565b60405180910390a361129a84848461184a565b50505050565b60006112ce7f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc60001b61184f565b60000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905090565b6112ff61147f565b50565b61132e7f4910fdfa16fed3260ed0e7147f7cc6da11a60208b5b9406d12a635614ffd914360001b611859565b60000160009054906101000a900460ff16156113525761134d83611863565b61147a565b8273ffffffffffffffffffffffffffffffffffffffff166352d1902d6040518163ffffffff1660e01b815260040160206040518083038186803b15801561139857600080fd5b505afa9250505080156113c957506040513d601f19601f820116820180604052508101906113c69190611e7d565b60015b611408576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016113ff906123ec565b60405180910390fd5b7f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc60001b811461146d576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401611464906123ac565b60405180910390fd5b5061147983838361191c565b5b505050565b611487610dc6565b73ffffffffffffffffffffffffffffffffffffffff166114a5610b65565b73ffffffffffffffffffffffffffffffffffffffff16146114fb576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016114f29061242c565b60405180910390fd5b565b600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff16141561156d576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401611564906124ec565b60405180910390fd5b61157960008383611845565b806035600082825461158b91906125ca565b9250508190555080603360008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825401925050819055508173ffffffffffffffffffffffffffffffffffffffff16600073ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef8360405161163d919061250c565b60405180910390a36116516000838361184a565b5050565b6000606560009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905081606560006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055508173ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a35050565b6000808273ffffffffffffffffffffffffffffffffffffffff163b119050919050565b600060019054906101000a900460ff1661178d576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401611784906124ac565b60405180910390fd5b6117978282611948565b5050565b600060019054906101000a900460ff166117ea576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016117e1906124ac565b60405180910390fd5b6117f26119c9565b565b600060019054906101000a900460ff16611843576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161183a906124ac565b60405180910390fd5b565b505050565b505050565b6000819050919050565b6000819050919050565b61186c8161171b565b6118ab576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016118a29061240c565b60405180910390fd5b806118d87f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc60001b61184f565b60000160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b61192583611a2a565b6000825111806119325750805b15611943576119418383611a79565b505b505050565b600060019054906101000a900460ff16611997576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161198e906124ac565b60405180910390fd5b81603690805190602001906119ad929190611bcf565b5080603790805190602001906119c4929190611bcf565b505050565b600060019054906101000a900460ff16611a18576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401611a0f906124ac565b60405180910390fd5b611a28611a23610dc6565b611655565b565b611a3381611863565b8073ffffffffffffffffffffffffffffffffffffffff167fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b60405160405180910390a250565b6060611a848361171b565b611ac3576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401611aba9061244c565b60405180910390fd5b6000808473ffffffffffffffffffffffffffffffffffffffff1684604051611aeb9190612207565b600060405180830381855af49150503d8060008114611b26576040519150601f19603f3d011682016040523d82523d6000602084013e611b2b565b606091505b5091509150611b538282604051806060016040528060278152602001612d9960279139611b5d565b9250505092915050565b60608315611b6d57829050611b78565b611b778383611b7f565b5b9392505050565b600082511115611b925781518083602001fd5b806040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401611bc6919061228a565b60405180910390fd5b828054611bdb906126d3565b90600052602060002090601f016020900481019282611bfd5760008555611c44565b82601f10611c1657805160ff1916838001178555611c44565b82800160010185558215611c44579182015b82811115611c43578251825591602001919060010190611c28565b5b509050611c519190611c55565b5090565b5b80821115611c6e576000816000905550600101611c56565b5090565b6000611c85611c8084612567565b612542565b905082815260208101848484011115611ca157611ca06127c8565b5b611cac848285612691565b509392505050565b600081359050611cc381612d53565b92915050565b600081519050611cd881612d6a565b92915050565b600082601f830112611cf357611cf26127c3565b5b8135611d03848260208601611c72565b91505092915050565b600081359050611d1b81612d81565b92915050565b600060208284031215611d3757611d366127d2565b5b6000611d4584828501611cb4565b91505092915050565b60008060408385031215611d6557611d646127d2565b5b6000611d7385828601611cb4565b9250506020611d8485828601611cb4565b9150509250929050565b600080600060608486031215611da757611da66127d2565b5b6000611db586828701611cb4565b9350506020611dc686828701611cb4565b9250506040611dd786828701611d0c565b9150509250925092565b60008060408385031215611df857611df76127d2565b5b6000611e0685828601611cb4565b925050602083013567ffffffffffffffff811115611e2757611e266127cd565b5b611e3385828601611cde565b9150509250929050565b60008060408385031215611e5457611e536127d2565b5b6000611e6285828601611cb4565b9250506020611e7385828601611d0c565b9150509250929050565b600060208284031215611e9357611e926127d2565b5b6000611ea184828501611cc9565b91505092915050565b611eb381612620565b82525050565b611ec281612632565b82525050565b611ed18161263e565b82525050565b6000611ee282612598565b611eec81856125ae565b9350611efc8185602086016126a0565b80840191505092915050565b611f118161267f565b82525050565b6000611f22826125a3565b611f2c81856125b9565b9350611f3c8185602086016126a0565b611f45816127d7565b840191505092915050565b6000611f5d6023836125b9565b9150611f68826127e8565b604082019050919050565b6000611f806026836125b9565b9150611f8b82612837565b604082019050919050565b6000611fa36022836125b9565b9150611fae82612886565b604082019050919050565b6000611fc6602c836125b9565b9150611fd1826128d5565b604082019050919050565b6000611fe9601d836125b9565b9150611ff482612924565b602082019050919050565b600061200c6026836125b9565b91506120178261294d565b604082019050919050565b600061202f602c836125b9565b915061203a8261299c565b604082019050919050565b60006120526038836125b9565b915061205d826129eb565b604082019050919050565b60006120756029836125b9565b915061208082612a3a565b604082019050919050565b6000612098602e836125b9565b91506120a382612a89565b604082019050919050565b60006120bb602e836125b9565b91506120c682612ad8565b604082019050919050565b60006120de602d836125b9565b91506120e982612b27565b604082019050919050565b60006121016020836125b9565b915061210c82612b76565b602082019050919050565b60006121246026836125b9565b915061212f82612b9f565b604082019050919050565b60006121476025836125b9565b915061215282612bee565b604082019050919050565b600061216a6024836125b9565b915061217582612c3d565b604082019050919050565b600061218d602b836125b9565b915061219882612c8c565b604082019050919050565b60006121b06025836125b9565b91506121bb82612cdb565b604082019050919050565b60006121d3601f836125b9565b91506121de82612d2a565b602082019050919050565b6121f281612668565b82525050565b61220181612672565b82525050565b60006122138284611ed7565b915081905092915050565b60006020820190506122336000830184611eaa565b92915050565b600060208201905061224e6000830184611eb9565b92915050565b60006020820190506122696000830184611ec8565b92915050565b60006020820190506122846000830184611f08565b92915050565b600060208201905081810360008301526122a48184611f17565b905092915050565b600060208201905081810360008301526122c581611f50565b9050919050565b600060208201905081810360008301526122e581611f73565b9050919050565b6000602082019050818103600083015261230581611f96565b9050919050565b6000602082019050818103600083015261232581611fb9565b9050919050565b6000602082019050818103600083015261234581611fdc565b9050919050565b6000602082019050818103600083015261236581611fff565b9050919050565b6000602082019050818103600083015261238581612022565b9050919050565b600060208201905081810360008301526123a581612045565b9050919050565b600060208201905081810360008301526123c581612068565b9050919050565b600060208201905081810360008301526123e58161208b565b9050919050565b60006020820190508181036000830152612405816120ae565b9050919050565b60006020820190508181036000830152612425816120d1565b9050919050565b60006020820190508181036000830152612445816120f4565b9050919050565b6000602082019050818103600083015261246581612117565b9050919050565b600060208201905081810360008301526124858161213a565b9050919050565b600060208201905081810360008301526124a58161215d565b9050919050565b600060208201905081810360008301526124c581612180565b9050919050565b600060208201905081810360008301526124e5816121a3565b9050919050565b60006020820190508181036000830152612505816121c6565b9050919050565b600060208201905061252160008301846121e9565b92915050565b600060208201905061253c60008301846121f8565b92915050565b600061254c61255d565b90506125588282612705565b919050565b6000604051905090565b600067ffffffffffffffff82111561258257612581612794565b5b61258b826127d7565b9050602081019050919050565b600081519050919050565b600081519050919050565b600081905092915050565b600082825260208201905092915050565b60006125d582612668565b91506125e083612668565b9250827fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0382111561261557612614612736565b5b828201905092915050565b600061262b82612648565b9050919050565b60008115159050919050565b6000819050919050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b6000819050919050565b600060ff82169050919050565b600061268a82612672565b9050919050565b82818337600083830152505050565b60005b838110156126be5780820151818401526020810190506126a3565b838111156126cd576000848401525b50505050565b600060028204905060018216806126eb57607f821691505b602082108114156126ff576126fe612765565b5b50919050565b61270e826127d7565b810181811067ffffffffffffffff8211171561272d5761272c612794565b5b80604052505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b600080fd5b600080fd5b600080fd5b600080fd5b6000601f19601f8301169050919050565b7f45524332303a207472616e7366657220746f20746865207a65726f206164647260008201527f6573730000000000000000000000000000000000000000000000000000000000602082015250565b7f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160008201527f6464726573730000000000000000000000000000000000000000000000000000602082015250565b7f45524332303a20617070726f766520746f20746865207a65726f20616464726560008201527f7373000000000000000000000000000000000000000000000000000000000000602082015250565b7f46756e6374696f6e206d7573742062652063616c6c6564207468726f7567682060008201527f64656c656761746563616c6c0000000000000000000000000000000000000000602082015250565b7f45524332303a20696e73756666696369656e7420616c6c6f77616e6365000000600082015250565b7f45524332303a207472616e7366657220616d6f756e742065786365656473206260008201527f616c616e63650000000000000000000000000000000000000000000000000000602082015250565b7f46756e6374696f6e206d7573742062652063616c6c6564207468726f7567682060008201527f6163746976652070726f78790000000000000000000000000000000000000000602082015250565b7f555550535570677261646561626c653a206d757374206e6f742062652063616c60008201527f6c6564207468726f7567682064656c656761746563616c6c0000000000000000602082015250565b7f45524331393637557067726164653a20756e737570706f727465642070726f7860008201527f6961626c65555549440000000000000000000000000000000000000000000000602082015250565b7f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160008201527f647920696e697469616c697a6564000000000000000000000000000000000000602082015250565b7f45524331393637557067726164653a206e657720696d706c656d656e7461746960008201527f6f6e206973206e6f742055555053000000000000000000000000000000000000602082015250565b7f455243313936373a206e657720696d706c656d656e746174696f6e206973206e60008201527f6f74206120636f6e747261637400000000000000000000000000000000000000602082015250565b7f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572600082015250565b7f416464726573733a2064656c65676174652063616c6c20746f206e6f6e2d636f60008201527f6e74726163740000000000000000000000000000000000000000000000000000602082015250565b7f45524332303a207472616e736665722066726f6d20746865207a65726f20616460008201527f6472657373000000000000000000000000000000000000000000000000000000602082015250565b7f45524332303a20617070726f76652066726f6d20746865207a65726f2061646460008201527f7265737300000000000000000000000000000000000000000000000000000000602082015250565b7f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960008201527f6e697469616c697a696e67000000000000000000000000000000000000000000602082015250565b7f45524332303a2064656372656173656420616c6c6f77616e63652062656c6f7760008201527f207a65726f000000000000000000000000000000000000000000000000000000602082015250565b7f45524332303a206d696e7420746f20746865207a65726f206164647265737300600082015250565b612d5c81612620565b8114612d6757600080fd5b50565b612d738161263e565b8114612d7e57600080fd5b50565b612d8a81612668565b8114612d9557600080fd5b5056fe416464726573733a206c6f772d6c6576656c2064656c65676174652063616c6c206661696c6564a264697066735822122066b4ca7b81ce7dbc6af77b0a062c617f3c53c6ec81d80662160ff3cdcd358a8064736f6c63430008070033' + t.ok(util.compareByteCode(immutableRef1, immutableRef2), 'code contains immutable refs and should match') }) tape('util.getInputParameters', function (t) { - t.plan(2) + t.plan(2) - t.equal(util.getinputParameters(sampleERC721), '000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000001650000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000016500000000000000000000000000000000000000000000000000000000000000') - t.equal(util.getinputParameters(ERC721), '') + t.equal(util.getinputParameters(sampleERC721), '000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000001650000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000016500000000000000000000000000000000000000000000000000000000000000') + t.equal(util.getinputParameters(ERC721), '') }) tape('util.compareByteCode uniswap onchain vs solidity local compilation result', function (t) { - t.plan(1) - t.ok(util.compareByteCode(uniswapQuote1, uniswapQuote2), 'uniswap bytecode should be the same') + t.plan(1) + t.ok(util.compareByteCode(uniswapQuote1, uniswapQuote2), 'uniswap bytecode should be the same') }) const uniswapQuote2 = `0x608060405234801561001057600080fd5b506004361061007d5760003560e01c8063c45a01551161005b578063c45a0155146100d3578063cdca1753146100db578063f7729d43146100ee578063fa461e33146101015761007d565b80632f80bb1d1461008257806330d07f21146100ab5780634aa4a4fc146100be575b600080fd5b610095610090366004610e9e565b610116565b6040516100a29190611148565b60405180910390f35b6100956100b9366004610e30565b61017b565b6100c6610340565b6040516100a29190611084565b6100c6610364565b6100956100e9366004610e9e565b610388565b6100956100fc366004610e30565b6103d6565b61011461010f366004610f04565b610555565b005b60005b600061012484610660565b9050600080600061013487610668565b92509250925061014882848389600061017b565b955083156101605761015987610699565b965061016c565b85945050505050610175565b50505050610119565b92915050565b600073ffffffffffffffffffffffffffffffffffffffff808616878216109083166101a65760008490555b6101b18787876106ce565b73ffffffffffffffffffffffffffffffffffffffff1663128acb0830836101d78861070c565b60000373ffffffffffffffffffffffffffffffffffffffff8816156101fc5787610222565b8561021b5773fffd8963efd1fc6a506488495d951d5263988d25610222565b6401000276a45b8b8b8e6040516020016102379392919061101e565b6040516020818303038152906040526040518663ffffffff1660e01b81526004016102669594939291906110a5565b6040805180830381600087803b15801561027f57600080fd5b505af19250505080156102cd575060408051601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01682019092526102ca91810190610ee1565b60015b610333573d8080156102fb576040519150601f19603f3d011682016040523d82523d6000602084013e610300565b606091505b5073ffffffffffffffffffffffffffffffffffffffff841661032157600080555b61032a8161073e565b92505050610337565b5050505b95945050505050565b7f000000000000000000000000000000000000000000000000000000000000000081565b7f000000000000000000000000000000000000000000000000000000000000000081565b60005b600061039684610660565b905060008060006103a687610668565b9250925092506103ba8383838960006103d6565b95508315610160576103cb87610699565b96505050505061038b565b600073ffffffffffffffffffffffffffffffffffffffff808616908716106103ff8787876106ce565b73ffffffffffffffffffffffffffffffffffffffff1663128acb0830836104258861070c565b73ffffffffffffffffffffffffffffffffffffffff881615610447578761046d565b856104665773fffd8963efd1fc6a506488495d951d5263988d2561046d565b6401000276a45b8c8b8d6040516020016104829392919061101e565b6040516020818303038152906040526040518663ffffffff1660e01b81526004016104b19594939291906110a5565b6040805180830381600087803b1580156104ca57600080fd5b505af1925050508015610518575060408051601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016820190925261051591810190610ee1565b60015b610333573d808015610546576040519150601f19603f3d011682016040523d82523d6000602084013e61054b565b606091505b5061032a8161073e565b60008313806105645750600082135b61056d57600080fd5b600080600061057b84610668565b9250925092506105ad7f00000000000000000000000000000000000000000000000000000000000000008484846107ef565b5060008060008089136105f3578573ffffffffffffffffffffffffffffffffffffffff168573ffffffffffffffffffffffffffffffffffffffff1610888a600003610628565b8473ffffffffffffffffffffffffffffffffffffffff168673ffffffffffffffffffffffffffffffffffffffff161089896000035b925092509250821561063f57604051818152602081fd5b6000541561065557600054811461065557600080fd5b604051828152602081fd5b516042111590565b600080806106768482610805565b9250610683846014610905565b9050610690846017610805565b91509193909250565b80516060906101759083906017907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe9016109f5565b60006107047f00000000000000000000000000000000000000000000000000000000000000006106ff868686610bdc565b610c59565b949350505050565b60007f8000000000000000000000000000000000000000000000000000000000000000821061073a57600080fd5b5090565b600081516020146107db5760448251101561078e576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161078590611111565b60405180910390fd5b600482019150818060200190518101906107a89190610f52565b6040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161078591906110f7565b818060200190518101906101759190610fbc565b600061033785610800868686610bdc565b610d8f565b60008182601401101561087957604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601260248201527f746f416464726573735f6f766572666c6f770000000000000000000000000000604482015290519081900360640190fd5b81601401835110156108ec57604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601560248201527f746f416464726573735f6f75744f66426f756e64730000000000000000000000604482015290519081900360640190fd5b5001602001516c01000000000000000000000000900490565b60008182600301101561097957604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601160248201527f746f55696e7432345f6f766572666c6f77000000000000000000000000000000604482015290519081900360640190fd5b81600301835110156109ec57604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601460248201527f746f55696e7432345f6f75744f66426f756e6473000000000000000000000000604482015290519081900360640190fd5b50016003015190565b60608182601f011015610a6957604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600e60248201527f736c6963655f6f766572666c6f77000000000000000000000000000000000000604482015290519081900360640190fd5b828284011015610ada57604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600e60248201527f736c6963655f6f766572666c6f77000000000000000000000000000000000000604482015290519081900360640190fd5b81830184511015610b4c57604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601160248201527f736c6963655f6f75744f66426f756e6473000000000000000000000000000000604482015290519081900360640190fd5b606082158015610b6b5760405191506000825260208201604052610bd3565b6040519150601f8416801560200281840101858101878315602002848b0101015b81831015610ba4578051835260209283019201610b8c565b5050858452601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016604052505b50949350505050565b610be4610dbf565b8273ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff161115610c1c579192915b506040805160608101825273ffffffffffffffffffffffffffffffffffffffff948516815292909316602083015262ffffff169181019190915290565b6000816020015173ffffffffffffffffffffffffffffffffffffffff16826000015173ffffffffffffffffffffffffffffffffffffffff1610610c9b57600080fd5b508051602080830151604093840151845173ffffffffffffffffffffffffffffffffffffffff94851681850152939091168385015262ffffff166060808401919091528351808403820181526080840185528051908301207fff0000000000000000000000000000000000000000000000000000000000000060a085015294901b7fffffffffffffffffffffffffffffffffffffffff0000000000000000000000001660a183015260b58201939093527fe34f199b19b2b4f47f68442619d555527d244f78a3297ea89325f843f87b8b5460d5808301919091528251808303909101815260f5909101909152805191012090565b6000610d9b8383610c59565b90503373ffffffffffffffffffffffffffffffffffffffff82161461017557600080fd5b604080516060810182526000808252602082018190529181019190915290565b600082601f830112610def578081fd5b8135610e02610dfd82611175565b611151565b818152846020838601011115610e16578283fd5b816020850160208301379081016020019190915292915050565b600080600080600060a08688031215610e47578081fd5b8535610e52816111e5565b94506020860135610e62816111e5565b9350604086013562ffffff81168114610e79578182fd5b9250606086013591506080860135610e90816111e5565b809150509295509295909350565b60008060408385031215610eb0578182fd5b823567ffffffffffffffff811115610ec6578283fd5b610ed285828601610ddf565b95602094909401359450505050565b60008060408385031215610ef3578182fd5b505080516020909101519092909150565b600080600060608486031215610f18578283fd5b8335925060208401359150604084013567ffffffffffffffff811115610f3c578182fd5b610f4886828701610ddf565b9150509250925092565b600060208284031215610f63578081fd5b815167ffffffffffffffff811115610f79578182fd5b8201601f81018413610f89578182fd5b8051610f97610dfd82611175565b818152856020838501011115610fab578384fd5b6103378260208301602086016111b5565b600060208284031215610fcd578081fd5b5051919050565b60008151808452610fec8160208601602086016111b5565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160200192915050565b606093841b7fffffffffffffffffffffffffffffffffffffffff000000000000000000000000908116825260e89390931b7fffffff0000000000000000000000000000000000000000000000000000000000166014820152921b166017820152602b0190565b73ffffffffffffffffffffffffffffffffffffffff91909116815260200190565b600073ffffffffffffffffffffffffffffffffffffffff8088168352861515602084015285604084015280851660608401525060a060808301526110ec60a0830184610fd4565b979650505050505050565b60006020825261110a6020830184610fd4565b9392505050565b60208082526010908201527f556e6578706563746564206572726f7200000000000000000000000000000000604082015260600190565b90815260200190565b60405181810167ffffffffffffffff8111828210171561116d57fe5b604052919050565b600067ffffffffffffffff82111561118957fe5b50601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01660200190565b60005b838110156111d05781810151838201526020016111b8565b838111156111df576000848401525b50505050565b73ffffffffffffffffffffffffffffffffffffffff8116811461120757600080fd5b5056fea26469706673582212204ba294eca0a366d4d8601eaa80323cb403aa01c4f4d2454757678b73f2acd97c64736f6c63430007060033` diff --git a/libs/remix-simulator/src/VmProxy.ts b/libs/remix-simulator/src/VmProxy.ts index a0005f8fa2..f2c39395ae 100644 --- a/libs/remix-simulator/src/VmProxy.ts +++ b/libs/remix-simulator/src/VmProxy.ts @@ -15,440 +15,440 @@ import type { AfterTxEvent, VM } from '@ethereumjs/vm' import type { TypedTransaction } from '@ethereumjs/tx' export class VmProxy { - vmContext: VMContext - vm: VM - vmTraces - txs - txsReceipt - hhLogs - processingHash - processingAddress - processingIndex - previousDepth - incr - eth - debug - providers - currentProvider - storageCache - sha3Preimages - sha3 - toHex - toAscii - fromAscii - fromDecimal - fromWei - toWei - toBigNumber - isAddress - utils - txsMapBlock - blocks - stateCopy: StateManager - flagDoNotRecordEVMSteps: boolean - lastMemoryUpdate: Array + vmContext: VMContext + vm: VM + vmTraces + txs + txsReceipt + hhLogs + processingHash + processingAddress + processingIndex + previousDepth + incr + eth + debug + providers + currentProvider + storageCache + sha3Preimages + sha3 + toHex + toAscii + fromAscii + fromDecimal + fromWei + toWei + toBigNumber + isAddress + utils + txsMapBlock + blocks + stateCopy: StateManager + flagDoNotRecordEVMSteps: boolean + lastMemoryUpdate: Array - constructor (vmContext: VMContext) { - this.vmContext = vmContext - this.stateCopy - this.vm = null - this.vmTraces = {} - this.txs = {} - this.txsReceipt = {} - this.hhLogs = {} - this.processingHash = null - this.processingAddress = null - this.processingIndex = null - this.previousDepth = 0 - this.incr = 0 - this.eth = {} - this.debug = {} - this.eth.getCode = (address, cb) => this.getCode(address, cb) - this.eth.getTransaction = (txHash, cb) => this.getTransaction(txHash, cb) - this.eth.getTransactionReceipt = (txHash, cb) => this.getTransactionReceipt(txHash, cb) - this.eth.getTransactionFromBlock = (blockNumber, txIndex, cb) => this.getTransactionFromBlock(blockNumber, txIndex, cb) - this.eth.getBlockNumber = (cb) => this.getBlockNumber(cb) - this.eth.getStorageAt = (address: string, position: string, blockNumber: string, cb) => this.getStorageAt(address, position, blockNumber, cb) - this.debug.traceTransaction = (txHash, options, cb) => this.traceTransaction(txHash, options, cb) - this.debug.storageRangeAt = (blockNumber, txIndex, address, start, maxLength, cb) => this.storageRangeAt(blockNumber, txIndex, address, start, maxLength, cb) - this.debug.preimage = (hashedKey, cb) => this.preimage(hashedKey, cb) - this.providers = { HttpProvider: function (url) {} } - this.currentProvider = { host: 'vm provider' } - this.storageCache = {} - this.sha3Preimages = {} - // util - this.sha3 = (...args) => utils.sha3.apply(this, args) - this.toHex = (...args) => utils.toHex.apply(this, args) - this.toAscii = (...args) => utils.toAscii.apply(this, args) - this.fromAscii = (...args) => utils.fromAscii.apply(this, args) - this.fromDecimal = (...args) => utils.fromDecimal.apply(this, args) - this.fromWei = (...args) => utils.fromWei.apply(this, args) - this.toWei = (...args) => utils.toWei.apply(this, args) - this.toBigNumber = (...args) => utils.toBN.apply(this, args) - this.isAddress = (...args) => utils.isAddress.apply(this, args) - this.utils = utils - this.txsMapBlock = {} - this.blocks = {} - this.lastMemoryUpdate = [] - } + constructor (vmContext: VMContext) { + this.vmContext = vmContext + this.stateCopy + this.vm = null + this.vmTraces = {} + this.txs = {} + this.txsReceipt = {} + this.hhLogs = {} + this.processingHash = null + this.processingAddress = null + this.processingIndex = null + this.previousDepth = 0 + this.incr = 0 + this.eth = {} + this.debug = {} + this.eth.getCode = (address, cb) => this.getCode(address, cb) + this.eth.getTransaction = (txHash, cb) => this.getTransaction(txHash, cb) + this.eth.getTransactionReceipt = (txHash, cb) => this.getTransactionReceipt(txHash, cb) + this.eth.getTransactionFromBlock = (blockNumber, txIndex, cb) => this.getTransactionFromBlock(blockNumber, txIndex, cb) + this.eth.getBlockNumber = (cb) => this.getBlockNumber(cb) + this.eth.getStorageAt = (address: string, position: string, blockNumber: string, cb) => this.getStorageAt(address, position, blockNumber, cb) + this.debug.traceTransaction = (txHash, options, cb) => this.traceTransaction(txHash, options, cb) + this.debug.storageRangeAt = (blockNumber, txIndex, address, start, maxLength, cb) => this.storageRangeAt(blockNumber, txIndex, address, start, maxLength, cb) + this.debug.preimage = (hashedKey, cb) => this.preimage(hashedKey, cb) + this.providers = { HttpProvider: function (url) {} } + this.currentProvider = { host: 'vm provider' } + this.storageCache = {} + this.sha3Preimages = {} + // util + this.sha3 = (...args) => utils.sha3.apply(this, args) + this.toHex = (...args) => utils.toHex.apply(this, args) + this.toAscii = (...args) => utils.toAscii.apply(this, args) + this.fromAscii = (...args) => utils.fromAscii.apply(this, args) + this.fromDecimal = (...args) => utils.fromDecimal.apply(this, args) + this.fromWei = (...args) => utils.fromWei.apply(this, args) + this.toWei = (...args) => utils.toWei.apply(this, args) + this.toBigNumber = (...args) => utils.toBN.apply(this, args) + this.isAddress = (...args) => utils.isAddress.apply(this, args) + this.utils = utils + this.txsMapBlock = {} + this.blocks = {} + this.lastMemoryUpdate = [] + } - setVM (vm) { - if (this.vm === vm) return - this.vm = vm - this.vm.evm.events.on('step', async (data: InterpreterStep) => { - await this.pushTrace(data) - }) - this.vm.events.on('afterTx', async (data: AfterTxEvent, resolve: (result?: any) => void) => { - await this.txProcessed(data) - resolve() - }) - this.vm.events.on('beforeTx', async (data: TypedTransaction, resolve: (result?: any) => void) => { - await this.txWillProcess(data) - resolve() - }) - } + setVM (vm) { + if (this.vm === vm) return + this.vm = vm + this.vm.evm.events.on('step', async (data: InterpreterStep) => { + await this.pushTrace(data) + }) + this.vm.events.on('afterTx', async (data: AfterTxEvent, resolve: (result?: any) => void) => { + await this.txProcessed(data) + resolve() + }) + this.vm.events.on('beforeTx', async (data: TypedTransaction, resolve: (result?: any) => void) => { + await this.txWillProcess(data) + resolve() + }) + } - releaseCurrentHash () { - const ret = this.processingHash - this.processingHash = undefined - return ret - } + releaseCurrentHash () { + const ret = this.processingHash + this.processingHash = undefined + return ret + } - flagNextAsDoNotRecordEvmSteps () { - this.flagDoNotRecordEVMSteps = true - } + flagNextAsDoNotRecordEvmSteps () { + this.flagDoNotRecordEVMSteps = true + } - async txWillProcess (data: TypedTransaction) { - if (this.flagDoNotRecordEVMSteps) return - this.lastMemoryUpdate = [] - this.stateCopy = await this.vm.stateManager.copy() - this.incr++ - this.processingHash = bufferToHex(data.hash()) - this.vmTraces[this.processingHash] = { - gas: '0x0', - return: '0x0', - structLogs: [] - } - const tx = {} - tx['hash'] = this.processingHash - tx['from'] = toChecksumAddress(data.getSenderAddress().toString()) - if (data.to) { - tx['to'] = toChecksumAddress(data.to.toString()) - } - this.processingAddress = tx['to'] - tx['input'] = bufferToHex(data.data) - tx['gas'] = data.gasLimit.toString(10) - if (data.value) { - tx['value'] = data.value.toString(10) - } - this.txs[this.processingHash] = tx - this.txsReceipt[this.processingHash] = tx - this.storageCache[this.processingHash] = {} - this.storageCache['after_' + this.processingHash] = {} - if (data.to) { - (async (processingHash, processingAccount, processingAddress, self) => { - try { - const storage = await self.stateCopy.dumpStorage(processingAccount) - self.storageCache[processingHash][processingAddress] = storage - } catch (e) { - console.log(e) - } - })(this.processingHash, data.to, tx['to'], this) + async txWillProcess (data: TypedTransaction) { + if (this.flagDoNotRecordEVMSteps) return + this.lastMemoryUpdate = [] + this.stateCopy = await this.vm.stateManager.copy() + this.incr++ + this.processingHash = bufferToHex(data.hash()) + this.vmTraces[this.processingHash] = { + gas: '0x0', + return: '0x0', + structLogs: [] + } + const tx = {} + tx['hash'] = this.processingHash + tx['from'] = toChecksumAddress(data.getSenderAddress().toString()) + if (data.to) { + tx['to'] = toChecksumAddress(data.to.toString()) + } + this.processingAddress = tx['to'] + tx['input'] = bufferToHex(data.data) + tx['gas'] = data.gasLimit.toString(10) + if (data.value) { + tx['value'] = data.value.toString(10) + } + this.txs[this.processingHash] = tx + this.txsReceipt[this.processingHash] = tx + this.storageCache[this.processingHash] = {} + this.storageCache['after_' + this.processingHash] = {} + if (data.to) { + (async (processingHash, processingAccount, processingAddress, self) => { + try { + const storage = await self.stateCopy.dumpStorage(processingAccount) + self.storageCache[processingHash][processingAddress] = storage + } catch (e) { + console.log(e) } - this.processingIndex = 0 + })(this.processingHash, data.to, tx['to'], this) } + this.processingIndex = 0 + } - async txProcessed (data: AfterTxEvent) { - if (this.flagDoNotRecordEVMSteps) { - this.flagDoNotRecordEVMSteps = false - return - } - const lastOp = this.vmTraces[this.processingHash].structLogs[this.processingIndex - 1] - if (lastOp) { - lastOp.error = lastOp.op !== 'RETURN' && lastOp.op !== 'STOP' && lastOp.op !== 'DESTRUCT' - } - const gasUsed = '0x' + data.totalGasSpent.toString(16) - this.vmTraces[this.processingHash].gas = gasUsed - this.txsReceipt[this.processingHash].gasUsed = gasUsed - const logs = [] - for (const l in data.execResult.logs) { - const log = data.execResult.logs[l] - const topics = [] - if (log[1].length > 0) { - for (const k in log[1]) { - topics.push('0x' + log[1][k].toString('hex')) - } - } else { - topics.push('0x') - } - logs.push({ - address: '0x' + log[0].toString('hex'), - data: '0x' + log[2].toString('hex'), - topics: topics, - rawVMResponse: log - }) + async txProcessed (data: AfterTxEvent) { + if (this.flagDoNotRecordEVMSteps) { + this.flagDoNotRecordEVMSteps = false + return + } + const lastOp = this.vmTraces[this.processingHash].structLogs[this.processingIndex - 1] + if (lastOp) { + lastOp.error = lastOp.op !== 'RETURN' && lastOp.op !== 'STOP' && lastOp.op !== 'DESTRUCT' + } + const gasUsed = '0x' + data.totalGasSpent.toString(16) + this.vmTraces[this.processingHash].gas = gasUsed + this.txsReceipt[this.processingHash].gasUsed = gasUsed + const logs = [] + for (const l in data.execResult.logs) { + const log = data.execResult.logs[l] + const topics = [] + if (log[1].length > 0) { + for (const k in log[1]) { + topics.push('0x' + log[1][k].toString('hex')) } - this.txsReceipt[this.processingHash].logs = logs - this.txsReceipt[this.processingHash].transactionHash = this.processingHash - const status = data.execResult.exceptionError ? 0 : 1 - this.txsReceipt[this.processingHash].status = `0x${status}` + } else { + topics.push('0x') + } + logs.push({ + address: '0x' + log[0].toString('hex'), + data: '0x' + log[2].toString('hex'), + topics: topics, + rawVMResponse: log + }) + } + this.txsReceipt[this.processingHash].logs = logs + this.txsReceipt[this.processingHash].transactionHash = this.processingHash + const status = data.execResult.exceptionError ? 0 : 1 + this.txsReceipt[this.processingHash].status = `0x${status}` - const to = this.txs[this.processingHash].to - if (to) { - try { - await (async (processingHash, processingAddress, self) => { - try { - const account = Address.fromString(processingAddress) - const storage = await self.vm.stateManager.dumpStorage(account) - self.storageCache['after_' + processingHash][processingAddress] = storage - } catch (e) { - console.log(e) - } - })(this.processingHash, to, this) - } catch (e) { - console.log(e) - } - } + const to = this.txs[this.processingHash].to + if (to) { + try { + await (async (processingHash, processingAddress, self) => { + try { + const account = Address.fromString(processingAddress) + const storage = await self.vm.stateManager.dumpStorage(account) + self.storageCache['after_' + processingHash][processingAddress] = storage + } catch (e) { + console.log(e) + } + })(this.processingHash, to, this) + } catch (e) { + console.log(e) + } + } - if (data.createdAddress) { - const address = data.createdAddress.toString() - const checksumedAddress = toChecksumAddress(address) - this.vmTraces[this.processingHash].return = checksumedAddress - this.txsReceipt[this.processingHash].contractAddress = checksumedAddress - } else if (data.execResult.returnValue) { - this.vmTraces[this.processingHash].return = '0x' + data.execResult.returnValue.toString('hex') - } else { - this.vmTraces[this.processingHash].return = '0x' - } - this.processingIndex = null - this.processingAddress = null - this.previousDepth = 0 - this.stateCopy = null + if (data.createdAddress) { + const address = data.createdAddress.toString() + const checksumedAddress = toChecksumAddress(address) + this.vmTraces[this.processingHash].return = checksumedAddress + this.txsReceipt[this.processingHash].contractAddress = checksumedAddress + } else if (data.execResult.returnValue) { + this.vmTraces[this.processingHash].return = '0x' + data.execResult.returnValue.toString('hex') + } else { + this.vmTraces[this.processingHash].return = '0x' } + this.processingIndex = null + this.processingAddress = null + this.previousDepth = 0 + this.stateCopy = null + } - async pushTrace (data: InterpreterStep) { - if (this.flagDoNotRecordEVMSteps) return - try { - const depth = data.depth + 1 // geth starts the depth from 1 - if (!this.processingHash) { - return - } - let previousOpcode - if (this.vmTraces[this.processingHash] && this.vmTraces[this.processingHash].structLogs[this.processingIndex - 1]) { - previousOpcode = this.vmTraces[this.processingHash].structLogs[this.processingIndex - 1] - } - if (this.previousDepth > depth && previousOpcode) { - // returning from context, set error it is not STOP, RETURN - previousOpcode.invalidDepthChange = previousOpcode.op !== 'RETURN' && previousOpcode.op !== 'STOP' - } - const step = { - stack: { ...data.stack }, - storage: {}, - memory: null, - op: data.opcode.name, - pc: data.pc, - gasCost: data.opcode.fee.toString(), - gas: data.gasLeft.toString(), - depth: depth - } - step.stack.length = Object.keys(data.stack).length + async pushTrace (data: InterpreterStep) { + if (this.flagDoNotRecordEVMSteps) return + try { + const depth = data.depth + 1 // geth starts the depth from 1 + if (!this.processingHash) { + return + } + let previousOpcode + if (this.vmTraces[this.processingHash] && this.vmTraces[this.processingHash].structLogs[this.processingIndex - 1]) { + previousOpcode = this.vmTraces[this.processingHash].structLogs[this.processingIndex - 1] + } + if (this.previousDepth > depth && previousOpcode) { + // returning from context, set error it is not STOP, RETURN + previousOpcode.invalidDepthChange = previousOpcode.op !== 'RETURN' && previousOpcode.op !== 'STOP' + } + const step = { + stack: { ...data.stack }, + storage: {}, + memory: null, + op: data.opcode.name, + pc: data.pc, + gasCost: data.opcode.fee.toString(), + gas: data.gasLeft.toString(), + depth: depth + } + step.stack.length = Object.keys(data.stack).length - if (previousOpcode && (previousOpcode.op === 'CALLDATACOPY' || previousOpcode.op === 'CODECOPY' || previousOpcode.op === 'EXTCODECOPY' || previousOpcode.op === 'RETURNDATACOPY' || previousOpcode.op === 'MSTORE' || previousOpcode.op === 'MSTORE8')) { - step.memory = new Uint8Array(data.memory) - this.lastMemoryUpdate = step.memory - } - this.vmTraces[this.processingHash].structLogs.push(step) - // Track hardhat console.log call - if (step.op === 'STATICCALL' && toHexPaddedString(step.stack[step.stack.length - 2]) === '0x000000000000000000000000000000000000000000636f6e736f6c652e6c6f67') { - const payloadStart = parseInt(toHexPaddedString(step.stack[step.stack.length - 3]), 16) - const memory = formatMemory(data.memory) - const memoryStr = memory.join('') - let payload = memoryStr.substring(payloadStart * 2, memoryStr.length) - const fnselectorStr = payload.substring(0, 8) - const fnselectorStrInHex = '0x' + fnselectorStr - const fnselector = parseInt(fnselectorStrInHex) - const fnArgs = ConsoleLogs[fnselector] - const iface = new ethers.utils.Interface([`function log${fnArgs} view`]) - const functionDesc = iface.getFunction(`log${fnArgs}`) - const sigHash = iface.getSighash(`log${fnArgs}`) - if (fnArgs.includes('uint') && sigHash !== fnselectorStrInHex) { - payload = payload.replace(fnselectorStr, sigHash) - } else { - payload = '0x' + payload - } - let consoleArgs = iface.decodeFunctionData(functionDesc, payload) - consoleArgs = consoleArgs.map((value) => { - if (isBigNumber(value)) { - return value.toString() - } - return value - }) - this.hhLogs[this.processingHash] = this.hhLogs[this.processingHash] ? this.hhLogs[this.processingHash] : [] - this.hhLogs[this.processingHash].push(consoleArgs) - } + if (previousOpcode && (previousOpcode.op === 'CALLDATACOPY' || previousOpcode.op === 'CODECOPY' || previousOpcode.op === 'EXTCODECOPY' || previousOpcode.op === 'RETURNDATACOPY' || previousOpcode.op === 'MSTORE' || previousOpcode.op === 'MSTORE8')) { + step.memory = new Uint8Array(data.memory) + this.lastMemoryUpdate = step.memory + } + this.vmTraces[this.processingHash].structLogs.push(step) + // Track hardhat console.log call + if (step.op === 'STATICCALL' && toHexPaddedString(step.stack[step.stack.length - 2]) === '0x000000000000000000000000000000000000000000636f6e736f6c652e6c6f67') { + const payloadStart = parseInt(toHexPaddedString(step.stack[step.stack.length - 3]), 16) + const memory = formatMemory(data.memory) + const memoryStr = memory.join('') + let payload = memoryStr.substring(payloadStart * 2, memoryStr.length) + const fnselectorStr = payload.substring(0, 8) + const fnselectorStrInHex = '0x' + fnselectorStr + const fnselector = parseInt(fnselectorStrInHex) + const fnArgs = ConsoleLogs[fnselector] + const iface = new ethers.utils.Interface([`function log${fnArgs} view`]) + const functionDesc = iface.getFunction(`log${fnArgs}`) + const sigHash = iface.getSighash(`log${fnArgs}`) + if (fnArgs.includes('uint') && sigHash !== fnselectorStrInHex) { + payload = payload.replace(fnselectorStr, sigHash) + } else { + payload = '0x' + payload + } + let consoleArgs = iface.decodeFunctionData(functionDesc, payload) + consoleArgs = consoleArgs.map((value) => { + if (isBigNumber(value)) { + return value.toString() + } + return value + }) + this.hhLogs[this.processingHash] = this.hhLogs[this.processingHash] ? this.hhLogs[this.processingHash] : [] + this.hhLogs[this.processingHash].push(consoleArgs) + } - if (step.op === 'CREATE' || step.op === 'CALL') { - if (step.op === 'CREATE') { - this.processingAddress = '(Contract Creation - Step ' + this.processingIndex + ')' - this.storageCache[this.processingHash][this.processingAddress] = {} - } else { - this.processingAddress = normalizeHexAddress(toHexPaddedString(step.stack[step.stack.length - 2])) - this.processingAddress = toChecksumAddress(this.processingAddress) - if (!this.storageCache[this.processingHash][this.processingAddress]) { - (async (processingHash, processingAddress, self) => { - try { - const account = Address.fromString(processingAddress) - const storage = await self.stateCopy.dumpStorage(account) - self.storageCache[processingHash][processingAddress] = storage - } catch (e) { - console.log(e) - } - })(this.processingHash, this.processingAddress, this) - } - } - } - if (previousOpcode && previousOpcode.op === 'SHA3') { - const preimage = this.getSha3Input(previousOpcode.stack, formatMemory(this.lastMemoryUpdate)) - const imageHash = toHexPaddedString(step.stack[step.stack.length - 1]).replace('0x', '') - this.sha3Preimages[imageHash] = { - preimage: preimage - } - } - this.processingIndex++ - this.previousDepth = depth - } catch (e) { - console.log(e) + if (step.op === 'CREATE' || step.op === 'CALL') { + if (step.op === 'CREATE') { + this.processingAddress = '(Contract Creation - Step ' + this.processingIndex + ')' + this.storageCache[this.processingHash][this.processingAddress] = {} + } else { + this.processingAddress = normalizeHexAddress(toHexPaddedString(step.stack[step.stack.length - 2])) + this.processingAddress = toChecksumAddress(this.processingAddress) + if (!this.storageCache[this.processingHash][this.processingAddress]) { + (async (processingHash, processingAddress, self) => { + try { + const account = Address.fromString(processingAddress) + const storage = await self.stateCopy.dumpStorage(account) + self.storageCache[processingHash][processingAddress] = storage + } catch (e) { + console.log(e) + } + })(this.processingHash, this.processingAddress, this) + } + } + } + if (previousOpcode && previousOpcode.op === 'SHA3') { + const preimage = this.getSha3Input(previousOpcode.stack, formatMemory(this.lastMemoryUpdate)) + const imageHash = toHexPaddedString(step.stack[step.stack.length - 1]).replace('0x', '') + this.sha3Preimages[imageHash] = { + preimage: preimage } + } + this.processingIndex++ + this.previousDepth = depth + } catch (e) { + console.log(e) } + } - getCode (address, cb) { - address = toChecksumAddress(address) - this.vm.stateManager.getContractCode(Address.fromString(address)).then((result) => { - cb(null, bufferToHex(result)) - }).catch((error) => { - cb(error) - }) - } + getCode (address, cb) { + address = toChecksumAddress(address) + this.vm.stateManager.getContractCode(Address.fromString(address)).then((result) => { + cb(null, bufferToHex(result)) + }).catch((error) => { + cb(error) + }) + } - setProvider (provider) {} + setProvider (provider) {} - traceTransaction (txHash, options, cb) { - if (this.vmTraces[txHash]) { - if (cb) { - cb(null, this.vmTraces[txHash]) - } - return this.vmTraces[txHash] - } - if (cb) { - cb('unable to retrieve traces ' + txHash, null) - } + traceTransaction (txHash, options, cb) { + if (this.vmTraces[txHash]) { + if (cb) { + cb(null, this.vmTraces[txHash]) + } + return this.vmTraces[txHash] } + if (cb) { + cb('unable to retrieve traces ' + txHash, null) + } + } - getStorageAt (address: string, position: string, blockNumber: string, cb) { - // we don't use the range params here - address = toChecksumAddress(address) + getStorageAt (address: string, position: string, blockNumber: string, cb) { + // we don't use the range params here + address = toChecksumAddress(address) - blockNumber = blockNumber === 'latest' ? this.vmContext.latestBlockNumber : blockNumber + blockNumber = blockNumber === 'latest' ? this.vmContext.latestBlockNumber : blockNumber - const block = this.vmContext.blocks[blockNumber] - const txHash = '0x' + block.transactions[block.transactions.length - 1].hash().toString('hex') + const block = this.vmContext.blocks[blockNumber] + const txHash = '0x' + block.transactions[block.transactions.length - 1].hash().toString('hex') - if (this.storageCache['after_' + txHash] && this.storageCache['after_' + txHash][address]) { - const slot = '0x' + hash.keccak(toBuffer(ethers.utils.hexZeroPad(position, 32))).toString('hex') - const storage = this.storageCache['after_' + txHash][address] - return cb(null, storage[slot].value) - } - // Before https://github.com/ethereum/remix-project/pull/1703, it used to throw error as - // 'unable to retrieve storage ' + txIndex + ' ' + address - cb(null, '0x0') + if (this.storageCache['after_' + txHash] && this.storageCache['after_' + txHash][address]) { + const slot = '0x' + hash.keccak(toBuffer(ethers.utils.hexZeroPad(position, 32))).toString('hex') + const storage = this.storageCache['after_' + txHash][address] + return cb(null, storage[slot].value) } + // Before https://github.com/ethereum/remix-project/pull/1703, it used to throw error as + // 'unable to retrieve storage ' + txIndex + ' ' + address + cb(null, '0x0') + } - storageRangeAt (blockNumber, txIndex, address, start, maxLength, cb) { - // we don't use the range params here - address = toChecksumAddress(address) + storageRangeAt (blockNumber, txIndex, address, start, maxLength, cb) { + // we don't use the range params here + address = toChecksumAddress(address) - const block = this.vmContext.blocks[blockNumber] - const txHash = '0x' + block.transactions[txIndex].hash().toString('hex') + const block = this.vmContext.blocks[blockNumber] + const txHash = '0x' + block.transactions[txIndex].hash().toString('hex') - if (this.storageCache[txHash] && this.storageCache[txHash][address]) { - const storage = this.storageCache[txHash][address] - return cb(null, { - storage: JSON.parse(JSON.stringify(storage)), - nextKey: null - }) - } - // Before https://github.com/ethereum/remix-project/pull/1703, it used to throw error as - // 'unable to retrieve storage ' + txIndex + ' ' + address - cb(null, { storage: {} }) + if (this.storageCache[txHash] && this.storageCache[txHash][address]) { + const storage = this.storageCache[txHash][address] + return cb(null, { + storage: JSON.parse(JSON.stringify(storage)), + nextKey: null + }) } + // Before https://github.com/ethereum/remix-project/pull/1703, it used to throw error as + // 'unable to retrieve storage ' + txIndex + ' ' + address + cb(null, { storage: {} }) + } - getBlockNumber (cb) { cb(null, 'vm provider') } + getBlockNumber (cb) { cb(null, 'vm provider') } - getTransaction (txHash, cb) { - if (this.txs[txHash]) { - if (cb) { - cb(null, this.txs[txHash]) - } - return this.txs[txHash] - } - if (cb) { - cb('unable to retrieve tx ' + txHash, null) - } + getTransaction (txHash, cb) { + if (this.txs[txHash]) { + if (cb) { + cb(null, this.txs[txHash]) + } + return this.txs[txHash] } - - getTransactionReceipt (txHash, cb) { - // same as getTransaction but return the created address also - if (this.txsReceipt[txHash]) { - if (cb) { - cb(null, this.txsReceipt[txHash]) - } - return this.txsReceipt[txHash] - } - if (cb) { - cb('unable to retrieve txReceipt ' + txHash, null) - } + if (cb) { + cb('unable to retrieve tx ' + txHash, null) } + } - getTransactionFromBlock (blockNumber, txIndex, cb) { - const mes = 'not supposed to be needed by remix in vmmode' - console.log(mes) - if (cb) { - cb(mes, null) - } + getTransactionReceipt (txHash, cb) { + // same as getTransaction but return the created address also + if (this.txsReceipt[txHash]) { + if (cb) { + cb(null, this.txsReceipt[txHash]) + } + return this.txsReceipt[txHash] + } + if (cb) { + cb('unable to retrieve txReceipt ' + txHash, null) } + } - preimage (hashedKey, cb) { - hashedKey = hashedKey.replace('0x', '') - cb(null, this.sha3Preimages[hashedKey] !== undefined ? this.sha3Preimages[hashedKey].preimage : null) + getTransactionFromBlock (blockNumber, txIndex, cb) { + const mes = 'not supposed to be needed by remix in vmmode' + console.log(mes) + if (cb) { + cb(mes, null) } + } - getSha3Input (stack, memory) { - const memoryStart = toHexPaddedString(stack[stack.length - 1]) - const memoryLength = toHexPaddedString(stack[stack.length - 2]) - const memStartDec = (new BN(memoryStart.replace('0x', ''), 16)).toString(10) - const memoryStartInt = parseInt(memStartDec) * 2 - const memLengthDec = (new BN(memoryLength.replace('0x', ''), 16).toString(10)) - const memoryLengthInt = parseInt(memLengthDec) * 2 + preimage (hashedKey, cb) { + hashedKey = hashedKey.replace('0x', '') + cb(null, this.sha3Preimages[hashedKey] !== undefined ? this.sha3Preimages[hashedKey].preimage : null) + } - let i = Math.floor(memoryStartInt / 32) - const maxIndex = Math.floor(memoryLengthInt / 32) + i - if (!memory[i]) { - return this.emptyFill(memoryLength) - } - let sha3Input = memory[i].slice(memoryStartInt - 32 * i) - i++ - while (i < maxIndex) { - sha3Input += memory[i] ? memory[i] : this.emptyFill(32) - i++ - } - if (sha3Input.length < memoryLength) { - const leftSize = memoryLengthInt - sha3Input.length - sha3Input += memory[i] ? memory[i].slice(0, leftSize) : this.emptyFill(leftSize) - } - return sha3Input - } + getSha3Input (stack, memory) { + const memoryStart = toHexPaddedString(stack[stack.length - 1]) + const memoryLength = toHexPaddedString(stack[stack.length - 2]) + const memStartDec = (new BN(memoryStart.replace('0x', ''), 16)).toString(10) + const memoryStartInt = parseInt(memStartDec) * 2 + const memLengthDec = (new BN(memoryLength.replace('0x', ''), 16).toString(10)) + const memoryLengthInt = parseInt(memLengthDec) * 2 - emptyFill (size) { - return (new Array(size)).join('0') + let i = Math.floor(memoryStartInt / 32) + const maxIndex = Math.floor(memoryLengthInt / 32) + i + if (!memory[i]) { + return this.emptyFill(memoryLength) } + let sha3Input = memory[i].slice(memoryStartInt - 32 * i) + i++ + while (i < maxIndex) { + sha3Input += memory[i] ? memory[i] : this.emptyFill(32) + i++ + } + if (sha3Input.length < memoryLength) { + const leftSize = memoryLengthInt - sha3Input.length + sha3Input += memory[i] ? memory[i].slice(0, leftSize) : this.emptyFill(leftSize) + } + return sha3Input + } + + emptyFill (size) { + return (new Array(size)).join('0') + } } diff --git a/libs/remix-simulator/src/methods/accounts.ts b/libs/remix-simulator/src/methods/accounts.ts index ffbfc89a3a..9463c7ec4c 100644 --- a/libs/remix-simulator/src/methods/accounts.ts +++ b/libs/remix-simulator/src/methods/accounts.ts @@ -4,105 +4,105 @@ const Web3EthAccounts = require('web3-eth-accounts'); import * as crypto from 'crypto' export class Web3Accounts { - accounts: Record - accountsKeys: Record - web3Accounts: any - vmContext + accounts: Record + accountsKeys: Record + web3Accounts: any + vmContext - constructor (vmContext) { - this.vmContext = vmContext - // TODO: make it random and/or use remix-libs + constructor (vmContext) { + this.vmContext = vmContext + // TODO: make it random and/or use remix-libs - this.web3Accounts = new Web3EthAccounts() - this.accounts = {} - this.accountsKeys = {} - } + this.web3Accounts = new Web3EthAccounts() + this.accounts = {} + this.accountsKeys = {} + } - async resetAccounts (): Promise { - this.accounts = {} - this.accountsKeys = {} - await this._addAccount('503f38a9c967ed597e47fe25643985f032b072db8075426a92110f82df48dfcb', '0x56BC75E2D63100000') - await this._addAccount('7e5bfb82febc4c2c8529167104271ceec190eafdca277314912eaabdb67c6e5f', '0x56BC75E2D63100000') - await this._addAccount('cc6d63f85de8fef05446ebdd3c537c72152d0fc437fd7aa62b3019b79bd1fdd4', '0x56BC75E2D63100000') - await this._addAccount('638b5c6c8c5903b15f0d3bf5d3f175c64e6e98a10bdb9768a2003bf773dcb86a', '0x56BC75E2D63100000') - await this._addAccount('f49bf239b6e554fdd08694fde6c67dac4d01c04e0dda5ee11abee478983f3bc0', '0x56BC75E2D63100000') - await this._addAccount('adeee250542d3790253046eee928d8058fd544294a5219bea152d1badbada395', '0x56BC75E2D63100000') - await this._addAccount('097ffe12069dcb3c3d99e6771e2cbf491a9b8b2f93ff4d3468f550c5e8264755', '0x56BC75E2D63100000') - await this._addAccount('5f58e8b9f1867ef00578b6f03e159428ab168f776aa445bc3ecdb02c7db8e865', '0x56BC75E2D63100000') - await this._addAccount('290e721ac87c7b3f31bef7b70104b9280ed3fa1425a59451490c9c02bf50d08f', '0x56BC75E2D63100000') - await this._addAccount('27efe944ff128cf510ab447b529eec28772f13bf65ebf1cbd504192c4f26e9d8', '0x56BC75E2D63100000') - await this._addAccount('3cd7232cd6f3fc66a57a6bedc1a8ed6c228fff0a327e169c2bcc5e869ed49511', '0x56BC75E2D63100000') - await this._addAccount('2ac6c190b09897cd8987869cc7b918cfea07ee82038d492abce033c75c1b1d0c', '0x56BC75E2D63100000') - await this._addAccount('dae9801649ba2d95a21e688b56f77905e5667c44ce868ec83f82e838712a2c7a', '0x56BC75E2D63100000') - await this._addAccount('d74aa6d18aa79a05f3473dd030a97d3305737cbc8337d940344345c1f6b72eea', '0x56BC75E2D63100000') - await this._addAccount('71975fbf7fe448e004ac7ae54cad0a383c3906055a65468714156a07385e96ce', '0x56BC75E2D63100000') - } + async resetAccounts (): Promise { + this.accounts = {} + this.accountsKeys = {} + await this._addAccount('503f38a9c967ed597e47fe25643985f032b072db8075426a92110f82df48dfcb', '0x56BC75E2D63100000') + await this._addAccount('7e5bfb82febc4c2c8529167104271ceec190eafdca277314912eaabdb67c6e5f', '0x56BC75E2D63100000') + await this._addAccount('cc6d63f85de8fef05446ebdd3c537c72152d0fc437fd7aa62b3019b79bd1fdd4', '0x56BC75E2D63100000') + await this._addAccount('638b5c6c8c5903b15f0d3bf5d3f175c64e6e98a10bdb9768a2003bf773dcb86a', '0x56BC75E2D63100000') + await this._addAccount('f49bf239b6e554fdd08694fde6c67dac4d01c04e0dda5ee11abee478983f3bc0', '0x56BC75E2D63100000') + await this._addAccount('adeee250542d3790253046eee928d8058fd544294a5219bea152d1badbada395', '0x56BC75E2D63100000') + await this._addAccount('097ffe12069dcb3c3d99e6771e2cbf491a9b8b2f93ff4d3468f550c5e8264755', '0x56BC75E2D63100000') + await this._addAccount('5f58e8b9f1867ef00578b6f03e159428ab168f776aa445bc3ecdb02c7db8e865', '0x56BC75E2D63100000') + await this._addAccount('290e721ac87c7b3f31bef7b70104b9280ed3fa1425a59451490c9c02bf50d08f', '0x56BC75E2D63100000') + await this._addAccount('27efe944ff128cf510ab447b529eec28772f13bf65ebf1cbd504192c4f26e9d8', '0x56BC75E2D63100000') + await this._addAccount('3cd7232cd6f3fc66a57a6bedc1a8ed6c228fff0a327e169c2bcc5e869ed49511', '0x56BC75E2D63100000') + await this._addAccount('2ac6c190b09897cd8987869cc7b918cfea07ee82038d492abce033c75c1b1d0c', '0x56BC75E2D63100000') + await this._addAccount('dae9801649ba2d95a21e688b56f77905e5667c44ce868ec83f82e838712a2c7a', '0x56BC75E2D63100000') + await this._addAccount('d74aa6d18aa79a05f3473dd030a97d3305737cbc8337d940344345c1f6b72eea', '0x56BC75E2D63100000') + await this._addAccount('71975fbf7fe448e004ac7ae54cad0a383c3906055a65468714156a07385e96ce', '0x56BC75E2D63100000') + } - _addAccount (privateKey, balance) { - return new Promise((resolve, reject) => { - privateKey = Buffer.from(privateKey, 'hex') - const address: Buffer = privateToAddress(privateKey) - const addressStr = toChecksumAddress('0x' + address.toString('hex')) - this.accounts[addressStr] = { privateKey, nonce: 0 } - this.accountsKeys[addressStr] = '0x' + privateKey.toString('hex') + _addAccount (privateKey, balance) { + return new Promise((resolve, reject) => { + privateKey = Buffer.from(privateKey, 'hex') + const address: Buffer = privateToAddress(privateKey) + const addressStr = toChecksumAddress('0x' + address.toString('hex')) + this.accounts[addressStr] = { privateKey, nonce: 0 } + this.accountsKeys[addressStr] = '0x' + privateKey.toString('hex') - const stateManager = this.vmContext.vm().stateManager - stateManager.getAccount(Address.fromString(addressStr)).then((account) => { - account.balance = new BN(balance.replace('0x', '') || 'f00000000000000001', 16) - stateManager.putAccount(Address.fromString(addressStr), account).catch((error) => { - reject(error) - }).then(() => { - resolve({}) - }) - }).catch((error) => { - reject(error) - }) + const stateManager = this.vmContext.vm().stateManager + stateManager.getAccount(Address.fromString(addressStr)).then((account) => { + account.balance = new BN(balance.replace('0x', '') || 'f00000000000000001', 16) + stateManager.putAccount(Address.fromString(addressStr), account).catch((error) => { + reject(error) + }).then(() => { + resolve({}) }) - } + }).catch((error) => { + reject(error) + }) + }) + } - newAccount (cb) { - let privateKey:Buffer - do { - privateKey = crypto.randomBytes(32) - } while (!isValidPrivate(privateKey)) - this._addAccount(privateKey, '0x56BC75E2D63100000') - return cb(null, '0x' + privateToAddress(privateKey).toString('hex')) - } + newAccount (cb) { + let privateKey:Buffer + do { + privateKey = crypto.randomBytes(32) + } while (!isValidPrivate(privateKey)) + this._addAccount(privateKey, '0x56BC75E2D63100000') + return cb(null, '0x' + privateToAddress(privateKey).toString('hex')) + } - methods (): Record { - return { - eth_accounts: this.eth_accounts.bind(this), - eth_getBalance: this.eth_getBalance.bind(this), - eth_sign: this.eth_sign.bind(this) - } + methods (): Record { + return { + eth_accounts: this.eth_accounts.bind(this), + eth_getBalance: this.eth_getBalance.bind(this), + eth_sign: this.eth_sign.bind(this) } + } - eth_accounts (_payload, cb) { - return cb(null, Object.keys(this.accounts)) - } + eth_accounts (_payload, cb) { + return cb(null, Object.keys(this.accounts)) + } - eth_getBalance (payload, cb) { - const address = payload.params[0] + eth_getBalance (payload, cb) { + const address = payload.params[0] - this.vmContext.vm().stateManager.getAccount(Address.fromString(address)).then((account) => { - cb(null, new BN(account.balance).toString(10)) - }).catch((error) => { - cb(error) - }) - } + this.vmContext.vm().stateManager.getAccount(Address.fromString(address)).then((account) => { + cb(null, new BN(account.balance).toString(10)) + }).catch((error) => { + cb(error) + }) + } - eth_sign (payload, cb) { - const address = payload.params[0] - const message = payload.params[1] + eth_sign (payload, cb) { + const address = payload.params[0] + const message = payload.params[1] - const privateKey = this.accountsKeys[toChecksumAddress(address)] - if (!privateKey) { - return cb(new Error('unknown account')) - } - const account = this.web3Accounts.privateKeyToAccount(privateKey as string) + const privateKey = this.accountsKeys[toChecksumAddress(address)] + if (!privateKey) { + return cb(new Error('unknown account')) + } + const account = this.web3Accounts.privateKeyToAccount(privateKey as string) - const data = account.sign(message) + const data = account.sign(message) - cb(null, data.signature) - } + cb(null, data.signature) + } } diff --git a/libs/remix-simulator/src/methods/blocks.ts b/libs/remix-simulator/src/methods/blocks.ts index 60b47bab25..320fd454c6 100644 --- a/libs/remix-simulator/src/methods/blocks.ts +++ b/libs/remix-simulator/src/methods/blocks.ts @@ -3,182 +3,182 @@ import { VMContext } from '../vm-context' import { bigIntToHex } from '@ethereumjs/util' export class Blocks { - vmContext: VMContext - 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) { - this.vmContext = vmContext - const options = _options || {} - this.coinbase = options.coinbase || '0x0000000000000000000000000000000000000000' + vmContext: VMContext + 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) { + this.vmContext = vmContext + const options = _options || {} + this.coinbase = options.coinbase || '0x0000000000000000000000000000000000000000' + } + + methods (): Record { + return { + eth_getBlockByNumber: this.eth_getBlockByNumber.bind(this), + eth_gasPrice: this.eth_gasPrice.bind(this), + eth_coinbase: this.eth_coinbase.bind(this), + eth_blockNumber: this.eth_blockNumber.bind(this), + eth_getBlockByHash: this.eth_getBlockByHash.bind(this), + eth_getBlockTransactionCountByHash: this.eth_getBlockTransactionCountByHash.bind(this), + eth_getBlockTransactionCountByNumber: this.eth_getBlockTransactionCountByNumber.bind(this), + eth_getUncleCountByBlockHash: this.eth_getUncleCountByBlockHash.bind(this), + eth_getUncleCountByBlockNumber: this.eth_getUncleCountByBlockNumber.bind(this), + eth_getStorageAt: this.eth_getStorageAt.bind(this) } + } - methods (): Record { - return { - eth_getBlockByNumber: this.eth_getBlockByNumber.bind(this), - eth_gasPrice: this.eth_gasPrice.bind(this), - eth_coinbase: this.eth_coinbase.bind(this), - eth_blockNumber: this.eth_blockNumber.bind(this), - eth_getBlockByHash: this.eth_getBlockByHash.bind(this), - eth_getBlockTransactionCountByHash: this.eth_getBlockTransactionCountByHash.bind(this), - eth_getBlockTransactionCountByNumber: this.eth_getBlockTransactionCountByNumber.bind(this), - eth_getUncleCountByBlockHash: this.eth_getUncleCountByBlockHash.bind(this), - eth_getUncleCountByBlockNumber: this.eth_getUncleCountByBlockNumber.bind(this), - eth_getStorageAt: this.eth_getStorageAt.bind(this) - } + eth_getBlockByNumber (payload, cb) { + let blockIndex = payload.params[0] + if (blockIndex === 'latest') { + blockIndex = this.vmContext.latestBlockNumber } - eth_getBlockByNumber (payload, cb) { - let blockIndex = payload.params[0] - if (blockIndex === 'latest') { - blockIndex = this.vmContext.latestBlockNumber - } - - if (Number.isInteger(blockIndex)) { - blockIndex = '0x' + blockIndex.toString(16) - } - const block = this.vmContext.blocks[blockIndex] - - if (!block) { - return cb(new Error('block not found')) - } - - 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: bigIntToHex(block.header.number), - from: receipt.from, - gas: bigIntToHex(receipt.gas), - chainId: '0xd05', - gasPrice: '0x4a817c800', // 20000000000 - hash: receipt.transactionHash, - input: receipt.input, - nonce: bigIntToHex(tx.nonce), - transactionIndex: this.TX_INDEX, - value: bigIntToHex(tx.value), - to: receipt.to ? receipt.to : null - } - } - }) - const b = { - baseFeePerGas: '0x01', - number: bigIntToHex(block.header.number), - hash: this.toHex(block.hash()), - parentHash: this.toHex(block.header.parentHash), - nonce: this.toHex(block.header.nonce), - sha3Uncles: '0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347', - logsBloom: '0xe670ec64341771606e55d6b4ca35a1a6b75ee3d5145a99d05921026d1527331', - transactionsRoot: '0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421', - stateRoot: this.toHex(block.header.stateRoot), - miner: this.coinbase, - difficulty: bigIntToHex(block.header.difficulty), - totalDifficulty: bigIntToHex((block.header as any).totalDifficulty || 0), - extraData: this.toHex(block.header.extraData), - size: '0x027f07', // 163591 - gasLimit: bigIntToHex(block.header.gasLimit), - gasUsed: bigIntToHex(block.header.gasUsed), - timestamp: bigIntToHex(block.header.timestamp), - transactions, - uncles: [] - } - cb(null, b) + if (Number.isInteger(blockIndex)) { + blockIndex = '0x' + blockIndex.toString(16) } + const block = this.vmContext.blocks[blockIndex] - toHex (value) { - if (!value) return '0x0' - const v = value.toString('hex') - return ((v === '0x' || v === '') ? '0x0' : ('0x' + v)) + if (!block) { + return cb(new Error('block not found')) } - eth_getBlockByHash (payload, cb) { - const block = this.vmContext.blocks[payload.params[0]] - - 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: bigIntToHex(block.header.number), - from: receipt.from, - gas: toHex(receipt.gas), - chainId: '0xd05', - gasPrice: '0x4a817c800', // 20000000000 - hash: receipt.transactionHash, - input: receipt.input, - nonce: bigIntToHex(tx.nonce), - transactionIndex: this.TX_INDEX, - value: bigIntToHex(tx.value), - to: receipt.to ? receipt.to : null - } - } - }) - const b = { - baseFeePerGas: '0x01', - number: bigIntToHex(block.header.number), - hash: this.toHex(block.hash()), - parentHash: this.toHex(block.header.parentHash), - nonce: this.toHex(block.header.nonce), - sha3Uncles: '0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347', - logsBloom: '0xe670ec64341771606e55d6b4ca35a1a6b75ee3d5145a99d05921026d1527331', - transactionsRoot: '0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421', - stateRoot: this.toHex(block.header.stateRoot), - miner: this.coinbase, - difficulty: bigIntToHex(block.header.difficulty), - totalDifficulty: bigIntToHex((block.header as any).totalDifficulty || 0), - extraData: this.toHex(block.header.extraData), - size: '0x027f07', // 163591 - gasLimit: bigIntToHex(block.header.gasLimit), - gasUsed: bigIntToHex(block.header.gasUsed), - timestamp: bigIntToHex(block.header.timestamp), - transactions, - uncles: [] + 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: bigIntToHex(block.header.number), + from: receipt.from, + gas: bigIntToHex(receipt.gas), + chainId: '0xd05', + gasPrice: '0x4a817c800', // 20000000000 + hash: receipt.transactionHash, + input: receipt.input, + nonce: bigIntToHex(tx.nonce), + transactionIndex: this.TX_INDEX, + value: bigIntToHex(tx.value), + to: receipt.to ? receipt.to : null } - - cb(null, b) + } + }) + const b = { + baseFeePerGas: '0x01', + number: bigIntToHex(block.header.number), + hash: this.toHex(block.hash()), + parentHash: this.toHex(block.header.parentHash), + nonce: this.toHex(block.header.nonce), + sha3Uncles: '0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347', + logsBloom: '0xe670ec64341771606e55d6b4ca35a1a6b75ee3d5145a99d05921026d1527331', + transactionsRoot: '0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421', + stateRoot: this.toHex(block.header.stateRoot), + miner: this.coinbase, + difficulty: bigIntToHex(block.header.difficulty), + totalDifficulty: bigIntToHex((block.header as any).totalDifficulty || 0), + extraData: this.toHex(block.header.extraData), + size: '0x027f07', // 163591 + gasLimit: bigIntToHex(block.header.gasLimit), + gasUsed: bigIntToHex(block.header.gasUsed), + timestamp: bigIntToHex(block.header.timestamp), + transactions, + uncles: [] } - - eth_gasPrice (payload, cb) { - cb(null, 1) + cb(null, b) + } + + toHex (value) { + if (!value) return '0x0' + const v = value.toString('hex') + return ((v === '0x' || v === '') ? '0x0' : ('0x' + v)) + } + + eth_getBlockByHash (payload, cb) { + const block = this.vmContext.blocks[payload.params[0]] + + 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: bigIntToHex(block.header.number), + from: receipt.from, + gas: toHex(receipt.gas), + chainId: '0xd05', + gasPrice: '0x4a817c800', // 20000000000 + hash: receipt.transactionHash, + input: receipt.input, + nonce: bigIntToHex(tx.nonce), + transactionIndex: this.TX_INDEX, + value: bigIntToHex(tx.value), + to: receipt.to ? receipt.to : null + } + } + }) + const b = { + baseFeePerGas: '0x01', + number: bigIntToHex(block.header.number), + hash: this.toHex(block.hash()), + parentHash: this.toHex(block.header.parentHash), + nonce: this.toHex(block.header.nonce), + sha3Uncles: '0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347', + logsBloom: '0xe670ec64341771606e55d6b4ca35a1a6b75ee3d5145a99d05921026d1527331', + transactionsRoot: '0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421', + stateRoot: this.toHex(block.header.stateRoot), + miner: this.coinbase, + difficulty: bigIntToHex(block.header.difficulty), + totalDifficulty: bigIntToHex((block.header as any).totalDifficulty || 0), + extraData: this.toHex(block.header.extraData), + size: '0x027f07', // 163591 + gasLimit: bigIntToHex(block.header.gasLimit), + gasUsed: bigIntToHex(block.header.gasUsed), + timestamp: bigIntToHex(block.header.timestamp), + transactions, + uncles: [] } - eth_coinbase (payload, cb) { - cb(null, this.coinbase) - } + cb(null, b) + } - eth_blockNumber (payload, cb) { - cb(null, parseInt(this.vmContext.latestBlockNumber)) - } + eth_gasPrice (payload, cb) { + cb(null, 1) + } - eth_getBlockTransactionCountByHash (payload, cb) { - const block = this.vmContext.blocks[payload.params[0]] + eth_coinbase (payload, cb) { + cb(null, this.coinbase) + } - cb(null, block.transactions.length) - } + eth_blockNumber (payload, cb) { + cb(null, parseInt(this.vmContext.latestBlockNumber)) + } - eth_getBlockTransactionCountByNumber (payload, cb) { - const block = this.vmContext.blocks[payload.params[0]] + eth_getBlockTransactionCountByHash (payload, cb) { + const block = this.vmContext.blocks[payload.params[0]] - cb(null, block.transactions.length) - } + cb(null, block.transactions.length) + } - eth_getUncleCountByBlockHash (payload, cb) { - cb(null, 0) - } + eth_getBlockTransactionCountByNumber (payload, cb) { + const block = this.vmContext.blocks[payload.params[0]] - eth_getUncleCountByBlockNumber (payload, cb) { - cb(null, 0) - } + cb(null, block.transactions.length) + } - eth_getStorageAt (payload, cb) { - return this.vmContext.web3().eth.getStorageAt( - payload.params[0], - payload.params[1], - payload.params[2], - cb) - } + eth_getUncleCountByBlockHash (payload, cb) { + cb(null, 0) + } + + eth_getUncleCountByBlockNumber (payload, cb) { + cb(null, 0) + } + + eth_getStorageAt (payload, cb) { + return this.vmContext.web3().eth.getStorageAt( + payload.params[0], + payload.params[1], + payload.params[2], + cb) + } } diff --git a/libs/remix-simulator/src/methods/debug.ts b/libs/remix-simulator/src/methods/debug.ts index b9bf886e50..0732dfa7cf 100644 --- a/libs/remix-simulator/src/methods/debug.ts +++ b/libs/remix-simulator/src/methods/debug.ts @@ -1,33 +1,33 @@ export class Debug { - vmContext + vmContext - constructor (vmContext) { - this.vmContext = vmContext - } + constructor (vmContext) { + this.vmContext = vmContext + } - methods () { - return { - debug_traceTransaction: this.debug_traceTransaction.bind(this), - debug_preimage: this.debug_preimage.bind(this), - debug_storageRangeAt: this.debug_storageRangeAt.bind(this) - } + methods () { + return { + debug_traceTransaction: this.debug_traceTransaction.bind(this), + debug_preimage: this.debug_preimage.bind(this), + debug_storageRangeAt: this.debug_storageRangeAt.bind(this) } + } - debug_traceTransaction (payload, cb) { - this.vmContext.web3().debug.traceTransaction(payload.params[0], {}, cb) - } + debug_traceTransaction (payload, cb) { + this.vmContext.web3().debug.traceTransaction(payload.params[0], {}, cb) + } - debug_preimage (payload, cb) { - this.vmContext.web3().debug.preimage(payload.params[0], cb) - } + debug_preimage (payload, cb) { + this.vmContext.web3().debug.preimage(payload.params[0], cb) + } - debug_storageRangeAt (payload, cb) { - this.vmContext.web3().debug.storageRangeAt( - payload.params[0], - payload.params[1], - payload.params[2], - payload.params[3], - payload.params[4], - cb) - } + debug_storageRangeAt (payload, cb) { + this.vmContext.web3().debug.storageRangeAt( + payload.params[0], + payload.params[1], + payload.params[2], + payload.params[3], + payload.params[4], + cb) + } } diff --git a/libs/remix-simulator/src/methods/filters.ts b/libs/remix-simulator/src/methods/filters.ts index 43404a1c31..9df319c992 100644 --- a/libs/remix-simulator/src/methods/filters.ts +++ b/libs/remix-simulator/src/methods/filters.ts @@ -1,62 +1,62 @@ export class Filters { - vmContext - - constructor (vmContext) { - this.vmContext = vmContext - } - - methods () { - return { - eth_getLogs: this.eth_getLogs.bind(this), - eth_subscribe: this.eth_subscribe.bind(this), - eth_unsubscribe: this.eth_unsubscribe.bind(this) - } - } - - eth_getLogs (payload, cb) { - const results = this.vmContext.logsManager.getLogsFor(payload.params[0]) - cb(null, results) - } - - eth_subscribe (payload, cb) { - const subscriptionId = this.vmContext.logsManager.subscribe(payload.params) - cb(null, subscriptionId) - } - - eth_unsubscribe (payload, cb) { - this.vmContext.logsManager.unsubscribe(payload.params[0]) - cb(null, true) - } - - eth_newFilter (payload, cb) { - const filterId = this.vmContext.logsManager.newFilter('filter', payload.params[0]) - cb(null, filterId) - } - - eth_newBlockFilter (payload, cb) { - const filterId = this.vmContext.logsManager.newFilter('block') - cb(null, filterId) - } - - eth_newPendingTransactionFilter (payload, cb) { - const filterId = this.vmContext.logsManager.newFilter('pendingTransactions') - cb(null, filterId) - } - - eth_uninstallfilter (payload, cb) { - const result = this.vmContext.logsManager.uninstallFilter(payload.params[0]) - cb(null, result) - } - - eth_getFilterChanges (payload, cb) { - const filterId = payload.params[0] - const results = this.vmContext.logsManager.getLogsForFilter(filterId) - cb(null, results) - } - - eth_getFilterLogs (payload, cb) { - const filterId = payload.params[0] - const results = this.vmContext.logsManager.getLogsForFilter(filterId, true) - cb(null, results) - } + vmContext + + constructor (vmContext) { + this.vmContext = vmContext + } + + methods () { + return { + eth_getLogs: this.eth_getLogs.bind(this), + eth_subscribe: this.eth_subscribe.bind(this), + eth_unsubscribe: this.eth_unsubscribe.bind(this) + } + } + + eth_getLogs (payload, cb) { + const results = this.vmContext.logsManager.getLogsFor(payload.params[0]) + cb(null, results) + } + + eth_subscribe (payload, cb) { + const subscriptionId = this.vmContext.logsManager.subscribe(payload.params) + cb(null, subscriptionId) + } + + eth_unsubscribe (payload, cb) { + this.vmContext.logsManager.unsubscribe(payload.params[0]) + cb(null, true) + } + + eth_newFilter (payload, cb) { + const filterId = this.vmContext.logsManager.newFilter('filter', payload.params[0]) + cb(null, filterId) + } + + eth_newBlockFilter (payload, cb) { + const filterId = this.vmContext.logsManager.newFilter('block') + cb(null, filterId) + } + + eth_newPendingTransactionFilter (payload, cb) { + const filterId = this.vmContext.logsManager.newFilter('pendingTransactions') + cb(null, filterId) + } + + eth_uninstallfilter (payload, cb) { + const result = this.vmContext.logsManager.uninstallFilter(payload.params[0]) + cb(null, result) + } + + eth_getFilterChanges (payload, cb) { + const filterId = payload.params[0] + const results = this.vmContext.logsManager.getLogsForFilter(filterId) + cb(null, results) + } + + eth_getFilterLogs (payload, cb) { + const filterId = payload.params[0] + const results = this.vmContext.logsManager.getLogsForFilter(filterId, true) + cb(null, results) + } } diff --git a/libs/remix-simulator/src/methods/misc.ts b/libs/remix-simulator/src/methods/misc.ts index e6f8a3716a..b3a57fff59 100644 --- a/libs/remix-simulator/src/methods/misc.ts +++ b/libs/remix-simulator/src/methods/misc.ts @@ -2,58 +2,58 @@ import { sha3 } from 'web3-utils' const version = require('../../package.json').version export function methods () { - return { - web3_clientVersion: web3_clientVersion, - eth_protocolVersion: eth_protocolVersion, - eth_syncing: eth_syncing, - eth_mining: eth_mining, - eth_hashrate: eth_hashrate, - web3_sha3: web3_sha3, - eth_getCompilers: eth_getCompilers, - eth_compileSolidity: eth_compileSolidity, - eth_compileLLL: eth_compileLLL, - eth_compileSerpent: eth_compileSerpent - } + return { + web3_clientVersion: web3_clientVersion, + eth_protocolVersion: eth_protocolVersion, + eth_syncing: eth_syncing, + eth_mining: eth_mining, + eth_hashrate: eth_hashrate, + web3_sha3: web3_sha3, + eth_getCompilers: eth_getCompilers, + eth_compileSolidity: eth_compileSolidity, + eth_compileLLL: eth_compileLLL, + eth_compileSerpent: eth_compileSerpent + } } export function web3_clientVersion (payload, cb) { - cb(null, 'Remix Simulator/' + version) + cb(null, 'Remix Simulator/' + version) } export function eth_protocolVersion (payload, cb) { - cb(null, '0x3f') + cb(null, '0x3f') } export function eth_syncing (payload, cb) { - cb(null, false) + cb(null, false) } export function eth_mining (payload, cb) { - // TODO: should depend on the state - cb(null, false) + // TODO: should depend on the state + cb(null, false) } export function eth_hashrate (payload, cb) { - cb(null, '0x0') + cb(null, '0x0') } export function web3_sha3 (payload, cb) { - const str: string = payload.params[0] - cb(null, sha3(str)) + const str: string = payload.params[0] + cb(null, sha3(str)) } export function eth_getCompilers (payload, cb) { - cb(null, []) + cb(null, []) } export function eth_compileSolidity (payload, cb) { - cb(null, 'unsupported') + cb(null, 'unsupported') } export function eth_compileLLL (payload, cb) { - cb(null, 'unsupported') + cb(null, 'unsupported') } export function eth_compileSerpent (payload, cb) { - cb(null, 'unsupported') + cb(null, 'unsupported') } diff --git a/libs/remix-simulator/src/methods/net.ts b/libs/remix-simulator/src/methods/net.ts index 1a064719f4..f8b05c3fab 100644 --- a/libs/remix-simulator/src/methods/net.ts +++ b/libs/remix-simulator/src/methods/net.ts @@ -1,20 +1,20 @@ export function methods (): Record { - return { - net_version: net_version, - net_listening: net_listening, - net_peerCount: net_peerCount - } + return { + net_version: net_version, + net_listening: net_listening, + net_peerCount: net_peerCount + } } export function net_version (payload, cb): void { - // should be configured networkId - cb(null, 1337) + // should be configured networkId + cb(null, 1337) } export function net_listening (payload, cb): void { - cb(null, true) + cb(null, true) } export function net_peerCount (payload, cb): void { - cb(null, 0) + cb(null, 0) } diff --git a/libs/remix-simulator/src/methods/transactions.ts b/libs/remix-simulator/src/methods/transactions.ts index dc6d940e65..5ba492b587 100644 --- a/libs/remix-simulator/src/methods/transactions.ts +++ b/libs/remix-simulator/src/methods/transactions.ts @@ -20,361 +20,361 @@ export type VMExecResult = { } export class Transactions { - vmContext - accounts - tags - txRunnerVMInstance - txRunnerInstance - TX_INDEX = '0x0' // currently there's always only 1 tx per block, so the transaction index will always be 0x0 - - constructor (vmContext) { - this.vmContext = vmContext - this.tags = {} - } - - init (accounts) { - this.accounts = accounts - const api = { - logMessage: (msg) => { - }, - logHtmlMessage: (msg) => { - }, - config: { - getUnpersistedProperty: (key) => { - return true - }, - get: () => { - return true - } - }, - detectNetwork: (cb) => { - cb() - }, - personalMode: () => { - return false - } - } - - this.txRunnerVMInstance = new TxRunnerVM(accounts, api, _ => this.vmContext.vmObject()) - this.txRunnerInstance = new TxRunner(this.txRunnerVMInstance, {}) - this.txRunnerInstance.vmaccounts = accounts - } - - methods () { - return { - eth_sendTransaction: this.eth_sendTransaction.bind(this), - eth_getTransactionReceipt: this.eth_getTransactionReceipt.bind(this), - eth_getCode: this.eth_getCode.bind(this), - eth_call: this.eth_call.bind(this), - eth_estimateGas: this.eth_estimateGas.bind(this), - eth_getTransactionCount: this.eth_getTransactionCount.bind(this), - eth_getTransactionByHash: this.eth_getTransactionByHash.bind(this), - eth_getTransactionByBlockHashAndIndex: this.eth_getTransactionByBlockHashAndIndex.bind(this), - eth_getTransactionByBlockNumberAndIndex: this.eth_getTransactionByBlockNumberAndIndex.bind(this), - eth_getExecutionResultFromSimulator: this.eth_getExecutionResultFromSimulator.bind(this), - eth_getHHLogsForTx: this.eth_getHHLogsForTx.bind(this), - eth_getHashFromTagBySimulator: this.eth_getHashFromTagBySimulator.bind(this) - } - } - - eth_sendTransaction (payload, cb) { - // 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) + vmContext + accounts + tags + txRunnerVMInstance + txRunnerInstance + TX_INDEX = '0x0' // currently there's always only 1 tx per block, so the transaction index will always be 0x0 + + constructor (vmContext) { + this.vmContext = vmContext + this.tags = {} + } + + init (accounts) { + this.accounts = accounts + const api = { + logMessage: (msg) => { + }, + logHtmlMessage: (msg) => { + }, + config: { + getUnpersistedProperty: (key) => { + return true + }, + get: () => { + return true } - processTx(this.txRunnerInstance, payload, false, (error, result: VMexecutionResult) => { - if (!error && result) { - this.vmContext.addBlock(result.block) - const hash = '0x' + result.tx.hash().toString('hex') - this.vmContext.trackTx(hash, result.block, result.tx) - const returnValue = `0x${result.result.execResult.returnValue.toString('hex') || '0'}` - const execResult: VMExecResult = { - exceptionError: result.result.execResult.exceptionError, - executionGasUsed: result.result.execResult.executionGasUsed, - gas: result.result.execResult.gas, - gasRefund: result.result.execResult.gasRefund, - logs: result.result.execResult.logs, - returnValue - } - this.vmContext.trackExecResult(hash, execResult) - return cb(null, result.transactionHash) - } - cb(error) - }) - } - - eth_getExecutionResultFromSimulator (payload, cb) { - const txHash = payload.params[0] - cb(null, this.vmContext.exeResults[txHash]) + }, + detectNetwork: (cb) => { + cb() + }, + personalMode: () => { + return false + } } - eth_getHHLogsForTx (payload, cb) { - const txHash = payload.params[0] - cb(null, this.vmContext.currentVm.web3vm.hhLogs[txHash] ? this.vmContext.currentVm.web3vm.hhLogs[txHash] : []) + this.txRunnerVMInstance = new TxRunnerVM(accounts, api, _ => this.vmContext.vmObject()) + this.txRunnerInstance = new TxRunner(this.txRunnerVMInstance, {}) + this.txRunnerInstance.vmaccounts = accounts + } + + methods () { + return { + eth_sendTransaction: this.eth_sendTransaction.bind(this), + eth_getTransactionReceipt: this.eth_getTransactionReceipt.bind(this), + eth_getCode: this.eth_getCode.bind(this), + eth_call: this.eth_call.bind(this), + eth_estimateGas: this.eth_estimateGas.bind(this), + eth_getTransactionCount: this.eth_getTransactionCount.bind(this), + eth_getTransactionByHash: this.eth_getTransactionByHash.bind(this), + eth_getTransactionByBlockHashAndIndex: this.eth_getTransactionByBlockHashAndIndex.bind(this), + eth_getTransactionByBlockNumberAndIndex: this.eth_getTransactionByBlockNumberAndIndex.bind(this), + eth_getExecutionResultFromSimulator: this.eth_getExecutionResultFromSimulator.bind(this), + eth_getHHLogsForTx: this.eth_getHHLogsForTx.bind(this), + eth_getHashFromTagBySimulator: this.eth_getHashFromTagBySimulator.bind(this) } + } - eth_getTransactionReceipt (payload, cb) { - this.vmContext.web3().eth.getTransactionReceipt(payload.params[0], (error, receipt) => { - if (error) { - return cb(error) - } - - const txBlock = this.vmContext.blockByTxHash[receipt.hash] - - const logs = this.vmContext.logsManager.getLogsByTxHash(receipt.hash) - - const r: Record = { - transactionHash: receipt.hash, - transactionIndex: this.TX_INDEX, - blockHash: '0x' + txBlock.hash().toString('hex'), - blockNumber: bigIntToHex(txBlock.header.number), - gasUsed: receipt.gasUsed, - cumulativeGasUsed: receipt.gasUsed, // only 1 tx per block - contractAddress: receipt.contractAddress, - logs, - status: receipt.status, - to: receipt.to - } - - if (r.blockNumber === '0x') { - r.blockNumber = '0x0' - } - - cb(null, r) - }) + eth_sendTransaction (payload, cb) { + // 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) } - - eth_estimateGas (payload, cb) { - // 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) + processTx(this.txRunnerInstance, payload, false, (error, result: VMexecutionResult) => { + if (!error && result) { + this.vmContext.addBlock(result.block) + const hash = '0x' + result.tx.hash().toString('hex') + this.vmContext.trackTx(hash, result.block, result.tx) + const returnValue = `0x${result.result.execResult.returnValue.toString('hex') || '0'}` + const execResult: VMExecResult = { + exceptionError: result.result.execResult.exceptionError, + executionGasUsed: result.result.execResult.executionGasUsed, + gas: result.result.execResult.gas, + gasRefund: result.result.execResult.gasRefund, + logs: result.result.execResult.logs, + returnValue } - - payload.params[0].gas = 10000000 * 10 - - this.vmContext.web3().flagNextAsDoNotRecordEvmSteps() - processTx(this.txRunnerInstance, payload, true, (error, value: VMexecutionResult) => { - if (error) return cb(error) - const result: RunTxResult = value.result - if ((result as any).receipt?.status === '0x0' || (result as any).receipt?.status === 0) { - try { - const msg = `0x${result.execResult.returnValue.toString('hex') || '0'}` - const abiCoder = new ethers.utils.AbiCoder() - const reason = abiCoder.decode(['string'], '0x' + msg.slice(10))[0] - return cb('revert ' + reason) - } catch (e) { - return cb(e.message) - } - } - let gasUsed = result.execResult.executionGasUsed - if (result.execResult.gasRefund) { - gasUsed += result.execResult.gasRefund - } - gasUsed = gasUsed + value.tx.getBaseFee() - cb(null, Math.ceil(Number(gasUsed) + (15 * Number(gasUsed)) / 100)) - }) + this.vmContext.trackExecResult(hash, execResult) + return cb(null, result.transactionHash) + } + cb(error) + }) + } + + eth_getExecutionResultFromSimulator (payload, cb) { + const txHash = payload.params[0] + cb(null, this.vmContext.exeResults[txHash]) + } + + eth_getHHLogsForTx (payload, cb) { + const txHash = payload.params[0] + cb(null, this.vmContext.currentVm.web3vm.hhLogs[txHash] ? this.vmContext.currentVm.web3vm.hhLogs[txHash] : []) + } + + eth_getTransactionReceipt (payload, cb) { + this.vmContext.web3().eth.getTransactionReceipt(payload.params[0], (error, receipt) => { + if (error) { + return cb(error) + } + + const txBlock = this.vmContext.blockByTxHash[receipt.hash] + + const logs = this.vmContext.logsManager.getLogsByTxHash(receipt.hash) + + const r: Record = { + transactionHash: receipt.hash, + transactionIndex: this.TX_INDEX, + blockHash: '0x' + txBlock.hash().toString('hex'), + blockNumber: bigIntToHex(txBlock.header.number), + gasUsed: receipt.gasUsed, + cumulativeGasUsed: receipt.gasUsed, // only 1 tx per block + contractAddress: receipt.contractAddress, + logs, + status: receipt.status, + to: receipt.to + } + + if (r.blockNumber === '0x') { + r.blockNumber = '0x0' + } + + cb(null, r) + }) + } + + eth_estimateGas (payload, cb) { + // 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) } - - eth_getCode (payload, cb) { - const address = payload.params[0] - - this.vmContext.web3().eth.getCode(address, (error, result) => { - if (error) { - console.dir('error getting code') - console.dir(error) - } - cb(error, result) - }) + if (payload.params && payload.params.length > 0 && payload.params[0].to) { + payload.params[0].to = toChecksumAddress(payload.params[0].to) } - eth_call (payload, cb) { - // 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) + payload.params[0].gas = 10000000 * 10 + + this.vmContext.web3().flagNextAsDoNotRecordEvmSteps() + processTx(this.txRunnerInstance, payload, true, (error, value: VMexecutionResult) => { + if (error) return cb(error) + const result: RunTxResult = value.result + if ((result as any).receipt?.status === '0x0' || (result as any).receipt?.status === 0) { + try { + const msg = `0x${result.execResult.returnValue.toString('hex') || '0'}` + const abiCoder = new ethers.utils.AbiCoder() + const reason = abiCoder.decode(['string'], '0x' + msg.slice(10))[0] + return cb('revert ' + reason) + } catch (e) { + return cb(e.message) } - if (payload.params && payload.params.length > 0 && payload.params[0].to) { - payload.params[0].to = toChecksumAddress(payload.params[0].to) - } - - payload.params[0].value = undefined - - const tag = payload.params[0].timestamp // e2e reference - - processTx(this.txRunnerInstance, payload, true, (error, result: VMexecutionResult) => { - if (!error && result) { - this.vmContext.addBlock(result.block) - const hash = '0x' + result.tx.hash().toString('hex') - this.vmContext.trackTx(hash, result.block, result.tx) - const returnValue = `0x${result.result.execResult.returnValue.toString('hex') || '0'}` - const execResult: VMExecResult = { - exceptionError: result.result.execResult.exceptionError, - executionGasUsed: result.result.execResult.executionGasUsed, - gas: result.result.execResult.gas, - gasRefund: result.result.execResult.gasRefund, - logs: result.result.execResult.logs, - returnValue: returnValue - } - this.vmContext.trackExecResult(hash, execResult) - this.tags[tag] = result.transactionHash - // calls are not supposed to return a transaction hash. we do this for keeping track of it and allowing debugging calls. - return cb(null, returnValue) - } - cb(error) - }) - } - - eth_getHashFromTagBySimulator (payload, cb) { - return cb(null, this.tags[payload.params[0]]) - } - - eth_getTransactionCount (payload, cb) { - const address = payload.params[0] - - this.vmContext.vm().stateManager.getAccount(Address.fromString(address)).then((account) => { - const nonce = new BN(account.nonce).toString(10) - cb(null, nonce) - }).catch((error) => { - cb(error) - }) + } + let gasUsed = result.execResult.executionGasUsed + if (result.execResult.gasRefund) { + gasUsed += result.execResult.gasRefund + } + gasUsed = gasUsed + value.tx.getBaseFee() + cb(null, Math.ceil(Number(gasUsed) + (15 * Number(gasUsed)) / 100)) + }) + } + + eth_getCode (payload, cb) { + const address = payload.params[0] + + this.vmContext.web3().eth.getCode(address, (error, result) => { + if (error) { + console.dir('error getting code') + console.dir(error) + } + cb(error, result) + }) + } + + eth_call (payload, cb) { + // 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) } - - eth_getTransactionByHash (payload, cb) { - const address = payload.params[0] - - this.vmContext.web3().eth.getTransactionReceipt(address, (error, receipt) => { - if (error) { - return cb(error) - } - - const txBlock = this.vmContext.blockByTxHash[receipt.transactionHash] - const tx = this.vmContext.txByHash[receipt.transactionHash] - - // TODO: params to add later - const r: Record = { - blockHash: '0x' + txBlock.hash().toString('hex'), - blockNumber: bigIntToHex(txBlock.header.number), - from: receipt.from, - gas: toHex(receipt.gas), - chainId: '0xd05', - // 'gasPrice': '2000000000000', // 0x123 - gasPrice: '0x4a817c800', // 20000000000 - hash: receipt.transactionHash, - input: receipt.input, - nonce: bigIntToHex(tx.nonce), - transactionIndex: this.TX_INDEX, - value: bigIntToHex(tx.value) - // "value":"0xf3dbb76162000" // 4290000000000000 - // "v": "0x25", // 37 - // "r": "0x1b5e176d927f8e9ab405058b2d2457392da3e20f328b16ddabcebc33eaac5fea", - // "s": "0x4ba69724e8f69de52f0125ad8b3c5c2cef33019bac3249e2c0a2192766d1721c" - } - - if (receipt.to) { - r['to'] = receipt.to - } - - if (r.value === '0x') { - r.value = '0x0' - } - - if (r.blockNumber === '0x') { - r.blockNumber = '0x0' - } - - cb(null, r) - }) + if (payload.params && payload.params.length > 0 && payload.params[0].to) { + payload.params[0].to = toChecksumAddress(payload.params[0].to) } - eth_getTransactionByBlockHashAndIndex (payload, cb) { - const txIndex = payload.params[1] - - const txBlock = this.vmContext.blocks[payload.params[0]] - const txHash = '0x' + txBlock.transactions[toDecimal(txIndex)].hash().toString('hex') - - this.vmContext.web3().eth.getTransactionReceipt(txHash, (error, receipt) => { - if (error) { - return cb(error) - } - - const tx = this.vmContext.txByHash[receipt.transactionHash] - - // TODO: params to add later - const r: Record = { - blockHash: '0x' + txBlock.hash().toString('hex'), - blockNumber: bigIntToHex(txBlock.header.number), - from: receipt.from, - gas: toHex(receipt.gas), - chainId: '0xd05', - // 'gasPrice': '2000000000000', // 0x123 - gasPrice: '0x4a817c800', // 20000000000 - hash: receipt.transactionHash, - input: receipt.input, - nonce: bigIntToHex(tx.nonce), - transactionIndex: this.TX_INDEX, - value: receipt.value - // "value":"0xf3dbb76162000" // 4290000000000000 - // "v": "0x25", // 37 - // "r": "0x1b5e176d927f8e9ab405058b2d2457392da3e20f328b16ddabcebc33eaac5fea", - // "s": "0x4ba69724e8f69de52f0125ad8b3c5c2cef33019bac3249e2c0a2192766d1721c" - } - - if (receipt.to) { - r['to'] = receipt.to - } - - if (r.value === '0x') { - r.value = '0x0' - } - - cb(null, r) - }) - } - - eth_getTransactionByBlockNumberAndIndex (payload, cb) { - const txIndex = payload.params[1] - - const txBlock = this.vmContext.blocks[payload.params[0]] - const txHash = '0x' + txBlock.transactions[toDecimal(txIndex)].hash().toString('hex') - - this.vmContext.web3().eth.getTransactionReceipt(txHash, (error, receipt) => { - if (error) { - return cb(error) - } - - const tx = this.vmContext.txByHash[receipt.transactionHash] - - // TODO: params to add later - const r: Record = { - blockHash: '0x' + txBlock.hash().toString('hex'), - blockNumber: bigIntToHex(txBlock.header.number), - from: receipt.from, - gas: toHex(receipt.gas), - // 'gasPrice': '2000000000000', // 0x123 - chainId: '0xd05', - gasPrice: '0x4a817c800', // 20000000000 - hash: receipt.transactionHash, - input: receipt.input, - nonce: bigIntToHex(tx.nonce), - transactionIndex: this.TX_INDEX, - value: receipt.value - // "value":"0xf3dbb76162000" // 4290000000000000 - // "v": "0x25", // 37 - // "r": "0x1b5e176d927f8e9ab405058b2d2457392da3e20f328b16ddabcebc33eaac5fea", - // "s": "0x4ba69724e8f69de52f0125ad8b3c5c2cef33019bac3249e2c0a2192766d1721c" - } - - if (receipt.to) { - r['to'] = receipt.to - } - - if (r.value === '0x') { - r.value = '0x0' - } - - cb(null, r) - }) - } + payload.params[0].value = undefined + + const tag = payload.params[0].timestamp // e2e reference + + processTx(this.txRunnerInstance, payload, true, (error, result: VMexecutionResult) => { + if (!error && result) { + this.vmContext.addBlock(result.block) + const hash = '0x' + result.tx.hash().toString('hex') + this.vmContext.trackTx(hash, result.block, result.tx) + const returnValue = `0x${result.result.execResult.returnValue.toString('hex') || '0'}` + const execResult: VMExecResult = { + exceptionError: result.result.execResult.exceptionError, + executionGasUsed: result.result.execResult.executionGasUsed, + gas: result.result.execResult.gas, + gasRefund: result.result.execResult.gasRefund, + logs: result.result.execResult.logs, + returnValue: returnValue + } + this.vmContext.trackExecResult(hash, execResult) + this.tags[tag] = result.transactionHash + // calls are not supposed to return a transaction hash. we do this for keeping track of it and allowing debugging calls. + return cb(null, returnValue) + } + cb(error) + }) + } + + eth_getHashFromTagBySimulator (payload, cb) { + return cb(null, this.tags[payload.params[0]]) + } + + eth_getTransactionCount (payload, cb) { + const address = payload.params[0] + + this.vmContext.vm().stateManager.getAccount(Address.fromString(address)).then((account) => { + const nonce = new BN(account.nonce).toString(10) + cb(null, nonce) + }).catch((error) => { + cb(error) + }) + } + + eth_getTransactionByHash (payload, cb) { + const address = payload.params[0] + + this.vmContext.web3().eth.getTransactionReceipt(address, (error, receipt) => { + if (error) { + return cb(error) + } + + const txBlock = this.vmContext.blockByTxHash[receipt.transactionHash] + const tx = this.vmContext.txByHash[receipt.transactionHash] + + // TODO: params to add later + const r: Record = { + blockHash: '0x' + txBlock.hash().toString('hex'), + blockNumber: bigIntToHex(txBlock.header.number), + from: receipt.from, + gas: toHex(receipt.gas), + chainId: '0xd05', + // 'gasPrice': '2000000000000', // 0x123 + gasPrice: '0x4a817c800', // 20000000000 + hash: receipt.transactionHash, + input: receipt.input, + nonce: bigIntToHex(tx.nonce), + transactionIndex: this.TX_INDEX, + value: bigIntToHex(tx.value) + // "value":"0xf3dbb76162000" // 4290000000000000 + // "v": "0x25", // 37 + // "r": "0x1b5e176d927f8e9ab405058b2d2457392da3e20f328b16ddabcebc33eaac5fea", + // "s": "0x4ba69724e8f69de52f0125ad8b3c5c2cef33019bac3249e2c0a2192766d1721c" + } + + if (receipt.to) { + r['to'] = receipt.to + } + + if (r.value === '0x') { + r.value = '0x0' + } + + if (r.blockNumber === '0x') { + r.blockNumber = '0x0' + } + + cb(null, r) + }) + } + + eth_getTransactionByBlockHashAndIndex (payload, cb) { + const txIndex = payload.params[1] + + const txBlock = this.vmContext.blocks[payload.params[0]] + const txHash = '0x' + txBlock.transactions[toDecimal(txIndex)].hash().toString('hex') + + this.vmContext.web3().eth.getTransactionReceipt(txHash, (error, receipt) => { + if (error) { + return cb(error) + } + + const tx = this.vmContext.txByHash[receipt.transactionHash] + + // TODO: params to add later + const r: Record = { + blockHash: '0x' + txBlock.hash().toString('hex'), + blockNumber: bigIntToHex(txBlock.header.number), + from: receipt.from, + gas: toHex(receipt.gas), + chainId: '0xd05', + // 'gasPrice': '2000000000000', // 0x123 + gasPrice: '0x4a817c800', // 20000000000 + hash: receipt.transactionHash, + input: receipt.input, + nonce: bigIntToHex(tx.nonce), + transactionIndex: this.TX_INDEX, + value: receipt.value + // "value":"0xf3dbb76162000" // 4290000000000000 + // "v": "0x25", // 37 + // "r": "0x1b5e176d927f8e9ab405058b2d2457392da3e20f328b16ddabcebc33eaac5fea", + // "s": "0x4ba69724e8f69de52f0125ad8b3c5c2cef33019bac3249e2c0a2192766d1721c" + } + + if (receipt.to) { + r['to'] = receipt.to + } + + if (r.value === '0x') { + r.value = '0x0' + } + + cb(null, r) + }) + } + + eth_getTransactionByBlockNumberAndIndex (payload, cb) { + const txIndex = payload.params[1] + + const txBlock = this.vmContext.blocks[payload.params[0]] + const txHash = '0x' + txBlock.transactions[toDecimal(txIndex)].hash().toString('hex') + + this.vmContext.web3().eth.getTransactionReceipt(txHash, (error, receipt) => { + if (error) { + return cb(error) + } + + const tx = this.vmContext.txByHash[receipt.transactionHash] + + // TODO: params to add later + const r: Record = { + blockHash: '0x' + txBlock.hash().toString('hex'), + blockNumber: bigIntToHex(txBlock.header.number), + from: receipt.from, + gas: toHex(receipt.gas), + // 'gasPrice': '2000000000000', // 0x123 + chainId: '0xd05', + gasPrice: '0x4a817c800', // 20000000000 + hash: receipt.transactionHash, + input: receipt.input, + nonce: bigIntToHex(tx.nonce), + transactionIndex: this.TX_INDEX, + value: receipt.value + // "value":"0xf3dbb76162000" // 4290000000000000 + // "v": "0x25", // 37 + // "r": "0x1b5e176d927f8e9ab405058b2d2457392da3e20f328b16ddabcebc33eaac5fea", + // "s": "0x4ba69724e8f69de52f0125ad8b3c5c2cef33019bac3249e2c0a2192766d1721c" + } + + if (receipt.to) { + r['to'] = receipt.to + } + + if (r.value === '0x') { + r.value = '0x0' + } + + cb(null, r) + }) + } } diff --git a/libs/remix-simulator/src/methods/txProcess.ts b/libs/remix-simulator/src/methods/txProcess.ts index f269ca1baf..aaaa0c7907 100644 --- a/libs/remix-simulator/src/methods/txProcess.ts +++ b/libs/remix-simulator/src/methods/txProcess.ts @@ -2,62 +2,62 @@ import { execution } from '@remix-project/remix-lib' const TxExecution = execution.txExecution function runCall (payload, from, to, data, value, gasLimit, txRunner, callbacks, callback) { - const finalCallback = function (err, result) { - if (err) { - return callback(err) - } - return callback(null, result) + const finalCallback = function (err, result) { + if (err) { + return callback(err) } + return callback(null, result) + } - TxExecution.callFunction(from, to, data, value, gasLimit, { constant: true }, txRunner, callbacks, finalCallback) + TxExecution.callFunction(from, to, data, value, gasLimit, { constant: true }, txRunner, callbacks, finalCallback) } function runTx (payload, from, to, data, value, gasLimit, txRunner, callbacks, callback) { - const finalCallback = function (err, result) { - if (err) { - return callback(err) - } - callback(null, result) + const finalCallback = function (err, result) { + if (err) { + return callback(err) } + callback(null, result) + } - TxExecution.callFunction(from, to, data, value, gasLimit, { constant: false }, txRunner, callbacks, finalCallback) + TxExecution.callFunction(from, to, data, value, gasLimit, { constant: false }, txRunner, callbacks, finalCallback) } function createContract (payload, from, data, value, gasLimit, txRunner, callbacks, callback) { - const finalCallback = function (err, result) { - if (err) { - return callback(err) - } - callback(null, result) + const finalCallback = function (err, result) { + if (err) { + return callback(err) } + callback(null, result) + } - TxExecution.createContract(from, data, value, gasLimit, txRunner, callbacks, finalCallback) + TxExecution.createContract(from, data, value, gasLimit, txRunner, callbacks, finalCallback) } export function processTx (txRunnerInstance, payload, isCall, callback) { let { from, to, data, value, gas } = payload.params[0] // eslint-disable-line - gas = gas || 3000000 - - const callbacks = { - confirmationCb: (network, tx, gasEstimation, continueTxExecution, cancelCb) => { - continueTxExecution(null) - }, - gasEstimationForceSend: (error, continueTxExecution, cancelCb) => { - if (error) { - continueTxExecution(error) - } - continueTxExecution() - }, - promptCb: (okCb, cancelCb) => { - okCb() - } - } - - if (isCall) { - runCall(payload, from, to, data, value, gas, txRunnerInstance, callbacks, callback) - } else if (to) { - runTx(payload, from, to, data, value, gas, txRunnerInstance, callbacks, callback) - } else { - createContract(payload, from, data, value, gas, txRunnerInstance, callbacks, callback) + gas = gas || 3000000 + + const callbacks = { + confirmationCb: (network, tx, gasEstimation, continueTxExecution, cancelCb) => { + continueTxExecution(null) + }, + gasEstimationForceSend: (error, continueTxExecution, cancelCb) => { + if (error) { + continueTxExecution(error) + } + continueTxExecution() + }, + promptCb: (okCb, cancelCb) => { + okCb() } + } + + if (isCall) { + runCall(payload, from, to, data, value, gas, txRunnerInstance, callbacks, callback) + } else if (to) { + runTx(payload, from, to, data, value, gas, txRunnerInstance, callbacks, callback) + } else { + createContract(payload, from, data, value, gas, txRunnerInstance, callbacks, callback) + } } diff --git a/libs/remix-simulator/src/provider.ts b/libs/remix-simulator/src/provider.ts index 997accddf9..5a3a04fed9 100644 --- a/libs/remix-simulator/src/provider.ts +++ b/libs/remix-simulator/src/provider.ts @@ -27,133 +27,133 @@ export interface JSONRPCResponsePayload { export type JSONRPCResponseCallback = (err: Error, result?: JSONRPCResponsePayload) => void export class Provider { - options: Record - vmContext - Accounts - Transactions - methods - connected: boolean - initialized: boolean - pendingRequests: Array - - constructor (options: Record = {}) { - this.options = options - this.connected = true - this.vmContext = new VMContext(options['fork'] as string, options['nodeUrl'] as string, options['blockNumber'] as (number | 'latest')) - - this.Accounts = new Web3Accounts(this.vmContext) - this.Transactions = new Transactions(this.vmContext) - - this.methods = {} - this.methods = merge(this.methods, this.Accounts.methods()) - this.methods = merge(this.methods, (new Blocks(this.vmContext, options)).methods()) - this.methods = merge(this.methods, miscMethods()) - this.methods = merge(this.methods, (new Filters(this.vmContext)).methods()) - this.methods = merge(this.methods, netMethods()) - this.methods = merge(this.methods, this.Transactions.methods()) - this.methods = merge(this.methods, (new Debug(this.vmContext)).methods()) + options: Record + vmContext + Accounts + Transactions + methods + connected: boolean + initialized: boolean + pendingRequests: Array + + constructor (options: Record = {}) { + this.options = options + this.connected = true + this.vmContext = new VMContext(options['fork'] as string, options['nodeUrl'] as string, options['blockNumber'] as (number | 'latest')) + + this.Accounts = new Web3Accounts(this.vmContext) + this.Transactions = new Transactions(this.vmContext) + + this.methods = {} + this.methods = merge(this.methods, this.Accounts.methods()) + this.methods = merge(this.methods, (new Blocks(this.vmContext, options)).methods()) + this.methods = merge(this.methods, miscMethods()) + this.methods = merge(this.methods, (new Filters(this.vmContext)).methods()) + this.methods = merge(this.methods, netMethods()) + this.methods = merge(this.methods, this.Transactions.methods()) + this.methods = merge(this.methods, (new Debug(this.vmContext)).methods()) + } + + async init () { + this.initialized = false + this.pendingRequests = [] + await this.vmContext.init() + await this.Accounts.resetAccounts() + this.Transactions.init(this.Accounts.accounts) + this.initialized = true + if (this.pendingRequests.length > 0) { + this.pendingRequests.map((req) => { + this.sendAsync(req.payload, req.callback) + }) + this.pendingRequests = [] } + } - async init () { - this.initialized = false - this.pendingRequests = [] - await this.vmContext.init() - await this.Accounts.resetAccounts() - this.Transactions.init(this.Accounts.accounts) - this.initialized = true - if (this.pendingRequests.length > 0) { - this.pendingRequests.map((req) => { - this.sendAsync(req.payload, req.callback) - }) - this.pendingRequests = [] - } + sendAsync (payload: JSONRPCRequestPayload, callback: (err: Error, result?: JSONRPCResponsePayload) => void) { + // log.info('payload method is ', payload.method) // commented because, this floods the IDE console + if (!this.initialized) { + this.pendingRequests.push({ payload, callback }) + return } - - sendAsync (payload: JSONRPCRequestPayload, callback: (err: Error, result?: JSONRPCResponsePayload) => void) { - // log.info('payload method is ', payload.method) // commented because, this floods the IDE console - if (!this.initialized) { - this.pendingRequests.push({ payload, callback }) - return - } - const method = this.methods[payload.method] + const method = this.methods[payload.method] + if (this.options.logDetails) { + info(payload) + } + if (method) { + return method.call(method, payload, (err, result) => { if (this.options.logDetails) { - info(payload) + info(err) + info(result) } - if (method) { - return method.call(method, payload, (err, result) => { - if (this.options.logDetails) { - info(err) - info(result) - } - if (err) { - return callback(err) - } - const response = { id: payload.id, jsonrpc: '2.0', result: result } - callback(null, response) - }) + if (err) { + return callback(err) } - callback(new Error('unknown method ' + payload.method)) + const response = { id: payload.id, jsonrpc: '2.0', result: result } + callback(null, response) + }) } + callback(new Error('unknown method ' + payload.method)) + } - send (payload, callback) { - this.sendAsync(payload, callback || function () {}) - } + send (payload, callback) { + this.sendAsync(payload, callback || function () {}) + } - isConnected () { - return true - } + isConnected () { + return true + } - disconnect () { - return false - } + disconnect () { + return false + } - supportsSubscriptions () { - return true - } + supportsSubscriptions () { + return true + } - on (type, cb) { - this.vmContext.logsManager.addListener(type, cb) - } + on (type, cb) { + this.vmContext.logsManager.addListener(type, cb) + } } export function extend (web3) { - if (!web3.extend) { - return - } - // DEBUG - const methods = [] - if (!(web3.eth && web3.eth.getExecutionResultFromSimulator)) { - methods.push(new web3.extend.Method({ - name: 'getExecutionResultFromSimulator', - call: 'eth_getExecutionResultFromSimulator', - inputFormatter: [null], - params: 1 - })) - } - - if (!(web3.eth && web3.eth.getHHLogsForTx)) { - methods.push(new web3.extend.Method({ - name: 'getHHLogsForTx', - call: 'eth_getHHLogsForTx', - inputFormatter: [null], - params: 1 - })) - } - - if (!(web3.eth && web3.eth.getHashFromTagBySimulator)) { - methods.push(new web3.extend.Method({ - name: 'getHashFromTagBySimulator', - call: 'eth_getHashFromTagBySimulator', - inputFormatter: [null], - params: 1 - })) - } - - if (methods.length > 0) { - web3.extend({ - property: 'eth', - methods: methods, - properties: [] - }) - } + if (!web3.extend) { + return + } + // DEBUG + const methods = [] + if (!(web3.eth && web3.eth.getExecutionResultFromSimulator)) { + methods.push(new web3.extend.Method({ + name: 'getExecutionResultFromSimulator', + call: 'eth_getExecutionResultFromSimulator', + inputFormatter: [null], + params: 1 + })) + } + + if (!(web3.eth && web3.eth.getHHLogsForTx)) { + methods.push(new web3.extend.Method({ + name: 'getHHLogsForTx', + call: 'eth_getHHLogsForTx', + inputFormatter: [null], + params: 1 + })) + } + + if (!(web3.eth && web3.eth.getHashFromTagBySimulator)) { + methods.push(new web3.extend.Method({ + name: 'getHashFromTagBySimulator', + call: 'eth_getHashFromTagBySimulator', + inputFormatter: [null], + params: 1 + })) + } + + if (methods.length > 0) { + web3.extend({ + property: 'eth', + methods: methods, + properties: [] + }) + } } diff --git a/libs/remix-simulator/src/server.ts b/libs/remix-simulator/src/server.ts index 73a2cd0552..b76bdfdcef 100644 --- a/libs/remix-simulator/src/server.ts +++ b/libs/remix-simulator/src/server.ts @@ -7,63 +7,63 @@ import { log } from './utils/logs' const app = express() class Server { - provider - rpcOnly + provider + rpcOnly - constructor (options) { - this.provider = new Provider(options) - this.provider.init().then(() => { - log('Provider initiated') - }).catch((error) => { - log(error) - }) - this.rpcOnly = options.rpc - } + constructor (options) { + this.provider = new Provider(options) + this.provider.init().then(() => { + log('Provider initiated') + }).catch((error) => { + log(error) + }) + this.rpcOnly = options.rpc + } - start (host, port) { - const wsApp = expressWs(app) + start (host, port) { + const wsApp = expressWs(app) - app.use(cors()) - app.use(bodyParser.urlencoded({ extended: true })) - app.use(bodyParser.json()) - - app.get('/', (req, res) => { - res.send('Welcome to remix-simulator') - }) - - if (this.rpcOnly) { - app.use((req, res) => { - this.provider.sendAsync(req.body, (err, jsonResponse) => { - if (err) { - return res.send(JSON.stringify({ error: err })) - } - res.send(jsonResponse) - }) - }) - } else { - wsApp.app.ws('/', (ws, req) => { - ws.on('message', (msg) => { - this.provider.sendAsync(JSON.parse(msg.toString()), (err, jsonResponse) => { - if (err) { - return ws.send(JSON.stringify({ error: err })) - } - ws.send(JSON.stringify(jsonResponse)) - }) - }) + app.use(cors()) + app.use(bodyParser.urlencoded({ extended: true })) + app.use(bodyParser.json()) - this.provider.on('data', (result) => { - ws.send(JSON.stringify(result)) - }) - }) - } + app.get('/', (req, res) => { + res.send('Welcome to remix-simulator') + }) - app.listen(port, host, () => { - log('Remix Simulator listening on ws://' + host + ':' + port) - if (!this.rpcOnly) { - log('http json-rpc is deprecated and disabled by default. To enable it use --rpc') + if (this.rpcOnly) { + app.use((req, res) => { + this.provider.sendAsync(req.body, (err, jsonResponse) => { + if (err) { + return res.send(JSON.stringify({ error: err })) + } + res.send(jsonResponse) + }) + }) + } else { + wsApp.app.ws('/', (ws, req) => { + ws.on('message', (msg) => { + this.provider.sendAsync(JSON.parse(msg.toString()), (err, jsonResponse) => { + if (err) { + return ws.send(JSON.stringify({ error: err })) } + ws.send(JSON.stringify(jsonResponse)) + }) + }) + + this.provider.on('data', (result) => { + ws.send(JSON.stringify(result)) }) + }) } + + app.listen(port, host, () => { + log('Remix Simulator listening on ws://' + host + ':' + port) + if (!this.rpcOnly) { + log('http json-rpc is deprecated and disabled by default. To enable it use --rpc') + } + }) + } } module.exports = Server diff --git a/libs/remix-simulator/src/utils/logs.ts b/libs/remix-simulator/src/utils/logs.ts index 0ea1271b3b..a4c85a03f7 100644 --- a/libs/remix-simulator/src/utils/logs.ts +++ b/libs/remix-simulator/src/utils/logs.ts @@ -5,66 +5,66 @@ import timestamp from 'time-stamp' import supportsColor from 'color-support' function hasFlag (flag) { - return ((typeof (process) !== 'undefined') && (process.argv.indexOf('--' + flag) !== -1)) + return ((typeof (process) !== 'undefined') && (process.argv.indexOf('--' + flag) !== -1)) } function addColor (str) { - if (hasFlag('no-color')) { - return str - } + if (hasFlag('no-color')) { + return str + } - if (hasFlag('color')) { - return gray(str) - } + if (hasFlag('color')) { + return gray(str) + } - if (supportsColor()) { - return gray(str) - } + if (supportsColor()) { + return gray(str) + } - return str + return str } function stdout (arg) { - if (typeof (process) === 'undefined' || !process.stdout) return - process.stdout.write(arg) + if (typeof (process) === 'undefined' || !process.stdout) return + process.stdout.write(arg) } function stderr (arg) { - if (typeof (process) === 'undefined' || process.stderr) return - process.stderr.write(arg) + if (typeof (process) === 'undefined' || process.stderr) return + process.stderr.write(arg) } function getTimestamp () { - const coloredTimestamp = addColor(timestamp('HH:mm:ss')) - return '[' + coloredTimestamp + ']' + const coloredTimestamp = addColor(timestamp('HH:mm:ss')) + return '[' + coloredTimestamp + ']' } export function log (...args: any[]) { - const time = getTimestamp() - stdout(time + ' ') - console.log(args) + const time = getTimestamp() + stdout(time + ' ') + console.log(args) } export function info (...args: any[]) { - const time = getTimestamp() - stdout(time + ' ') - console.info(args) + const time = getTimestamp() + stdout(time + ' ') + console.info(args) } export function dir (...args: any[]) { - const time = getTimestamp() - stdout(time + ' ') - console.dir(args) + const time = getTimestamp() + stdout(time + ' ') + console.dir(args) } export function warn (...args: any[]) { - const time = getTimestamp() - stderr(time + ' ') - console.warn(args) + const time = getTimestamp() + stderr(time + ' ') + console.warn(args) } export function error (...args: any[]) { - const time = getTimestamp() - stderr(time + ' ') - console.error(args) + const time = getTimestamp() + stderr(time + ' ') + console.error(args) } diff --git a/libs/remix-simulator/src/vm-context.ts b/libs/remix-simulator/src/vm-context.ts index 62122a61c9..140d2f2189 100644 --- a/libs/remix-simulator/src/vm-context.ts +++ b/libs/remix-simulator/src/vm-context.ts @@ -44,48 +44,48 @@ export interface DefaultStateManagerOpts { extend vm state manager and instanciate VM */ class StateManagerCommonStorageDump extends DefaultStateManager { - keyHashes: { [key: string]: string } - constructor (opts: DefaultStateManagerOpts = {}) { - super(opts) - this.keyHashes = {} - } - - putContractStorage (address, key, value) { - this.keyHashes[hash.keccak(key).toString('hex')] = bufferToHex(key) - return super.putContractStorage(address, key, value) - } - - copy(): StateManagerCommonStorageDump { - const copyState = new StateManagerCommonStorageDump({ - trie: this._trie.copy(false), + keyHashes: { [key: string]: string } + constructor (opts: DefaultStateManagerOpts = {}) { + super(opts) + this.keyHashes = {} + } + + putContractStorage (address, key, value) { + this.keyHashes[hash.keccak(key).toString('hex')] = bufferToHex(key) + return super.putContractStorage(address, key, value) + } + + copy(): StateManagerCommonStorageDump { + const copyState = new StateManagerCommonStorageDump({ + trie: this._trie.copy(false), + }) + copyState.keyHashes = this.keyHashes + return copyState + } + + async dumpStorage (address): Promise { + return new Promise((resolve, reject) => { + this._getStorageTrie(address) + .then((trie) => { + const storage = {} + const stream = trie.createReadStream() + + stream.on('data', (val) => { + const value = decode(val.value) + storage['0x' + val.key.toString('hex')] = { + key: this.keyHashes[val.key.toString('hex')], + value: '0x' + value.toString('hex') + } + }) + stream.on('end', () => { + resolve(storage) + }) }) - copyState.keyHashes = this.keyHashes - return copyState - } - - async dumpStorage (address): Promise { - return new Promise((resolve, reject) => { - this._getStorageTrie(address) - .then((trie) => { - const storage = {} - const stream = trie.createReadStream() - - stream.on('data', (val) => { - const value = decode(val.value) - storage['0x' + val.key.toString('hex')] = { - key: this.keyHashes[val.key.toString('hex')], - value: '0x' + value.toString('hex') - } - }) - stream.on('end', () => { - resolve(storage) - }) - }) - .catch((e) => { - reject(e) - }) + .catch((e) => { + reject(e) }) - } + }) + } } export interface CustomEthersStateManagerOpts { @@ -98,83 +98,83 @@ export interface CustomEthersStateManagerOpts { } class CustomEthersStateManager extends StateManagerCommonStorageDump { - private provider: ethers.providers.StaticJsonRpcProvider | ethers.providers.JsonRpcProvider - private blockTag: string - - constructor(opts: CustomEthersStateManagerOpts) { - super(opts) - if (typeof opts.provider === 'string') { - this.provider = new ethers.providers.StaticJsonRpcProvider(opts.provider) - } else if (opts.provider instanceof ethers.providers.JsonRpcProvider) { - this.provider = opts.provider - } else { - throw new Error(`valid JsonRpcProvider or url required; got ${opts.provider}`) - } - - this.blockTag = opts.blockTag - - /* + private provider: ethers.providers.StaticJsonRpcProvider | ethers.providers.JsonRpcProvider + private blockTag: string + + constructor(opts: CustomEthersStateManagerOpts) { + super(opts) + if (typeof opts.provider === 'string') { + this.provider = new ethers.providers.StaticJsonRpcProvider(opts.provider) + } else if (opts.provider instanceof ethers.providers.JsonRpcProvider) { + this.provider = opts.provider + } else { + throw new Error(`valid JsonRpcProvider or url required; got ${opts.provider}`) + } + + this.blockTag = opts.blockTag + + /* * For a custom StateManager implementation adopt these * callbacks passed to the `Cache` instantiated to perform * the `get`, `put` and `delete` operations with the * desired backend. */ - const getCb = async (address) => { - const rlp = await this._trie.get(address.buf) - if (rlp) { - const ac = Account.fromRlpSerializedAccount(rlp) - return ac - } else { - const ac = await this.getAccountFromProvider(address) - return ac - } - } - const putCb = async (keyBuf, accountRlp) => { - const trie = this._trie - await trie.put(keyBuf, accountRlp) - } - const deleteCb = async (keyBuf: Buffer) => { - const trie = this._trie - await trie.del(keyBuf) - } - this._cache = new Cache({ getCb, putCb, deleteCb }) + const getCb = async (address) => { + const rlp = await this._trie.get(address.buf) + if (rlp) { + const ac = Account.fromRlpSerializedAccount(rlp) + return ac + } else { + const ac = await this.getAccountFromProvider(address) + return ac + } + } + const putCb = async (keyBuf, accountRlp) => { + const trie = this._trie + await trie.put(keyBuf, accountRlp) + } + const deleteCb = async (keyBuf: Buffer) => { + const trie = this._trie + await trie.del(keyBuf) } + this._cache = new Cache({ getCb, putCb, deleteCb }) + } - /** + /** * Sets the new block tag used when querying the provider and clears the * internal cache. * @param blockTag - the new block tag to use when querying the provider */ - setBlockTag(blockTag: bigint | 'earliest'): void { - this.blockTag = blockTag === 'earliest' ? blockTag : bigIntToHex(blockTag) - } - - copy(): CustomEthersStateManager { - const newState = new CustomEthersStateManager({ - provider: this.provider, - blockTag: this.blockTag, - trie: this._trie.copy(false), - }) - return newState - } - - /** + setBlockTag(blockTag: bigint | 'earliest'): void { + this.blockTag = blockTag === 'earliest' ? blockTag : bigIntToHex(blockTag) + } + + copy(): CustomEthersStateManager { + const newState = new CustomEthersStateManager({ + provider: this.provider, + blockTag: this.blockTag, + trie: this._trie.copy(false), + }) + return newState + } + + /** * Gets the code corresponding to the provided `address`. * @param address - Address to get the `code` for * @returns {Promise} - Resolves with the code corresponding to the provided address. * Returns an empty `Buffer` if the account has no associated code. */ - async getContractCode(address: Address): Promise { - const code = await super.getContractCode(address) - if (code && code.length > 0) return code - else { - const code = toBuffer(await this.provider.getCode(address.toString(), this.blockTag)) - await super.putContractCode(address, code) - return code - } + async getContractCode(address: Address): Promise { + const code = await super.getContractCode(address) + if (code && code.length > 0) return code + else { + const code = toBuffer(await this.provider.getCode(address.toString(), this.blockTag)) + await super.putContractCode(address, code) + return code } + } - /** + /** * Gets the storage value associated with the provided `address` and `key`. This method returns * the shortest representation of the stored value. * @param address - Address of the account to get the storage for @@ -183,61 +183,61 @@ class CustomEthersStateManager extends StateManagerCommonStorageDump { * corresponding to the provided address at the provided key. * If this does not exist an empty `Buffer` is returned. */ - async getContractStorage(address: Address, key: Buffer): Promise { - let storage = await super.getContractStorage(address, key) - if (storage && storage.length > 0) return storage - else { - storage = toBuffer(await this.provider.getStorageAt( - address.toString(), - bufferToBigInt(key), - this.blockTag) - ) - await super.putContractStorage(address, key, storage) - return storage - } + async getContractStorage(address: Address, key: Buffer): Promise { + let storage = await super.getContractStorage(address, key) + if (storage && storage.length > 0) return storage + else { + storage = toBuffer(await this.provider.getStorageAt( + address.toString(), + bufferToBigInt(key), + this.blockTag) + ) + await super.putContractStorage(address, key, storage) + return storage } + } - /** + /** * Checks if an `account` exists at `address` * @param address - Address of the `account` to check */ - async accountExists(address: Address): Promise { - const localAccount = this._cache.get(address) - if (!localAccount.isEmpty()) return true - // Get merkle proof for `address` from provider - const proof = await this.provider.send('eth_getProof', [address.toString(), [], this.blockTag]) - - const proofBuf = proof.accountProof.map((proofNode: string) => toBuffer(proofNode)) - - const trie = new Trie({ useKeyHashing: true }) - const verified = await trie.verifyProof( - Buffer.from(keccak256(proofBuf[0])), - address.buf, - proofBuf - ) - // if not verified (i.e. verifyProof returns null), account does not exist - return verified === null ? false : true - } - - /** + async accountExists(address: Address): Promise { + const localAccount = this._cache.get(address) + if (!localAccount.isEmpty()) return true + // Get merkle proof for `address` from provider + const proof = await this.provider.send('eth_getProof', [address.toString(), [], this.blockTag]) + + const proofBuf = proof.accountProof.map((proofNode: string) => toBuffer(proofNode)) + + const trie = new Trie({ useKeyHashing: true }) + const verified = await trie.verifyProof( + Buffer.from(keccak256(proofBuf[0])), + address.buf, + proofBuf + ) + // if not verified (i.e. verifyProof returns null), account does not exist + return verified === null ? false : true + } + + /** * Retrieves an account from the provider and stores in the local trie * @param address Address of account to be retrieved from provider * @private */ - async getAccountFromProvider(address: Address): Promise { - const accountData = await this.provider.send('eth_getProof', [ - address.toString(), - [], - this.blockTag, - ]) - const account = Account.fromAccountData({ - balance: BigInt(accountData.balance), - nonce: BigInt(accountData.nonce), - codeHash: toBuffer(accountData.codeHash) - // storageRoot: toBuffer([]), // we have to remove this in order to force the creation of the Trie in the local state. - }) - return account - } + async getAccountFromProvider(address: Address): Promise { + const accountData = await this.provider.send('eth_getProof', [ + address.toString(), + [], + this.blockTag, + ]) + const account = Account.fromAccountData({ + balance: BigInt(accountData.balance), + nonce: BigInt(accountData.nonce), + codeHash: toBuffer(accountData.codeHash) + // storageRoot: toBuffer([]), // we have to remove this in order to force the creation of the Trie in the local state. + }) + return account + } } export type CurrentVm = { @@ -249,7 +249,7 @@ export type CurrentVm = { export class VMCommon extends Common { - /** + /** * Override "setHardforkByBlockNumber" to disable updating the original fork state * * @param blockNumber @@ -257,142 +257,142 @@ export class VMCommon extends Common { * @param timestamp * @returns The name of the HF set */ - setHardforkByBlockNumber( - blockNumber: BigIntLike, - td?: BigIntLike, - timestamp?: BigIntLike - ): string { - return this.hardfork() - } + setHardforkByBlockNumber( + blockNumber: BigIntLike, + td?: BigIntLike, + timestamp?: BigIntLike + ): string { + return this.hardfork() + } } /* trigger contextChanged, web3EndpointChanged */ export class VMContext { - currentFork: string - blockGasLimitDefault: number - blockGasLimit: number - blocks: Record - latestBlockNumber: string - blockByTxHash: Record - txByHash: Record - currentVm: CurrentVm - web3vm: VmProxy - logsManager: any // LogsManager - exeResults: Record - nodeUrl: string - blockNumber: number | 'latest' - - constructor (fork?: string, nodeUrl?: string, blockNumber?: number | 'latest') { - this.blockGasLimitDefault = 4300000 - this.blockGasLimit = this.blockGasLimitDefault - this.currentFork = fork || 'merge' - this.nodeUrl = nodeUrl - this.blockNumber = blockNumber - this.blocks = {} - this.latestBlockNumber = "0x0" - this.blockByTxHash = {} - this.txByHash = {} - this.exeResults = {} - this.logsManager = new LogsManager() - } - - async init () { - this.currentVm = await this.createVm(this.currentFork) - } - - async createVm (hardfork) { - let stateManager: StateManager - if (this.nodeUrl) { - let block = this.blockNumber - if (this.blockNumber === 'latest') { - const provider = new ethers.providers.StaticJsonRpcProvider(this.nodeUrl) - block = await provider.getBlockNumber() - stateManager = new CustomEthersStateManager({ - provider: this.nodeUrl, - blockTag: '0x' + block.toString(16) - }) - } else { - stateManager = new CustomEthersStateManager({ - provider: this.nodeUrl, - blockTag: '0x' + this.blockNumber.toString(16) - }) - } + currentFork: string + blockGasLimitDefault: number + blockGasLimit: number + blocks: Record + latestBlockNumber: string + blockByTxHash: Record + txByHash: Record + currentVm: CurrentVm + web3vm: VmProxy + logsManager: any // LogsManager + exeResults: Record + nodeUrl: string + blockNumber: number | 'latest' + + constructor (fork?: string, nodeUrl?: string, blockNumber?: number | 'latest') { + this.blockGasLimitDefault = 4300000 + this.blockGasLimit = this.blockGasLimitDefault + this.currentFork = fork || 'merge' + this.nodeUrl = nodeUrl + this.blockNumber = blockNumber + this.blocks = {} + this.latestBlockNumber = "0x0" + this.blockByTxHash = {} + this.txByHash = {} + this.exeResults = {} + this.logsManager = new LogsManager() + } + + async init () { + this.currentVm = await this.createVm(this.currentFork) + } + + async createVm (hardfork) { + let stateManager: StateManager + if (this.nodeUrl) { + let block = this.blockNumber + if (this.blockNumber === 'latest') { + const provider = new ethers.providers.StaticJsonRpcProvider(this.nodeUrl) + block = await provider.getBlockNumber() + stateManager = new CustomEthersStateManager({ + provider: this.nodeUrl, + blockTag: '0x' + block.toString(16) + }) + } else { + stateManager = new CustomEthersStateManager({ + provider: this.nodeUrl, + blockTag: '0x' + this.blockNumber.toString(16) + }) + } - } else - stateManager = new StateManagerCommonStorageDump() - - const consensusType = hardfork === 'berlin' || hardfork === 'london' ? ConsensusType.ProofOfWork : ConsensusType.ProofOfStake - const difficulty = consensusType === ConsensusType.ProofOfStake ? 0 : 69762765929000 - - const common = new VMCommon({ chain: 'mainnet', hardfork }) - const genesisBlock: Block = Block.fromBlockData({ - header: { - timestamp: (new Date().getTime() / 1000 | 0), - number: 0, - coinbase: '0x0e9281e9c6a0808672eaba6bd1220e144c9bb07a', - difficulty, - gasLimit: 8000000 - } - }, { common, hardforkByBlockNumber: false, hardforkByTTD: undefined }) - - const blockchain = await Blockchain.create({ common, validateBlocks: false, validateConsensus: false, genesisBlock }) - const eei = new EEI(stateManager, common, blockchain) - const evm = new EVM({ common, eei, allowUnlimitedContractSize: true }) + } else + stateManager = new StateManagerCommonStorageDump() + + const consensusType = hardfork === 'berlin' || hardfork === 'london' ? ConsensusType.ProofOfWork : ConsensusType.ProofOfStake + const difficulty = consensusType === ConsensusType.ProofOfStake ? 0 : 69762765929000 + + const common = new VMCommon({ chain: 'mainnet', hardfork }) + const genesisBlock: Block = Block.fromBlockData({ + header: { + timestamp: (new Date().getTime() / 1000 | 0), + number: 0, + coinbase: '0x0e9281e9c6a0808672eaba6bd1220e144c9bb07a', + difficulty, + gasLimit: 8000000 + } + }, { common, hardforkByBlockNumber: false, hardforkByTTD: undefined }) + + const blockchain = await Blockchain.create({ common, validateBlocks: false, validateConsensus: false, genesisBlock }) + const eei = new EEI(stateManager, common, blockchain) + const evm = new EVM({ common, eei, allowUnlimitedContractSize: true }) - const vm = await VM.create({ - common, - activatePrecompiles: true, - hardforkByBlockNumber: false, - stateManager, - blockchain, - evm - }) - - // VmProxy and VMContext are very intricated. - // VmProxy is used to track the EVM execution (to listen on opcode execution, in order for instance to generate the VM trace) - const web3vm = new VmProxy(this) - web3vm.setVM(vm) - this.addBlock(genesisBlock, true) - return { vm, web3vm, stateManager, common } - } - - getCurrentFork () { - return this.currentFork + const vm = await VM.create({ + common, + activatePrecompiles: true, + hardforkByBlockNumber: false, + stateManager, + blockchain, + evm + }) + + // VmProxy and VMContext are very intricated. + // VmProxy is used to track the EVM execution (to listen on opcode execution, in order for instance to generate the VM trace) + const web3vm = new VmProxy(this) + web3vm.setVM(vm) + this.addBlock(genesisBlock, true) + return { vm, web3vm, stateManager, common } + } + + getCurrentFork () { + return this.currentFork + } + + web3 () { + return this.currentVm.web3vm + } + + vm () { + return this.currentVm.vm + } + + vmObject () { + return this.currentVm + } + + addBlock (block: Block, genesis?: boolean) { + let blockNumber = bigIntToHex(block.header.number) + if (blockNumber === '0x') { + blockNumber = '0x0' } - web3 () { - return this.currentVm.web3vm - } + this.blocks['0x' + block.hash().toString('hex')] = block + this.blocks[blockNumber] = block + this.latestBlockNumber = blockNumber - vm () { - return this.currentVm.vm - } - - vmObject () { - return this.currentVm - } + if (!genesis) this.logsManager.checkBlock(blockNumber, block, this.web3()) + } - addBlock (block: Block, genesis?: boolean) { - let blockNumber = bigIntToHex(block.header.number) - if (blockNumber === '0x') { - blockNumber = '0x0' - } + trackTx (txHash, block, tx) { + this.blockByTxHash[txHash] = block + this.txByHash[txHash] = tx + } - this.blocks['0x' + block.hash().toString('hex')] = block - this.blocks[blockNumber] = block - this.latestBlockNumber = blockNumber - - if (!genesis) this.logsManager.checkBlock(blockNumber, block, this.web3()) - } - - trackTx (txHash, block, tx) { - this.blockByTxHash[txHash] = block - this.txByHash[txHash] = tx - } - - trackExecResult (tx, execReult) { - this.exeResults[tx] = execReult - } + trackExecResult (tx, execReult) { + this.exeResults[tx] = execReult + } } diff --git a/libs/remix-simulator/test/accounts.ts b/libs/remix-simulator/test/accounts.ts index 336d438380..f83b3cc08b 100644 --- a/libs/remix-simulator/test/accounts.ts +++ b/libs/remix-simulator/test/accounts.ts @@ -5,38 +5,38 @@ const web3 = new Web3() import * as assert from 'assert' describe('Accounts', () => { - before(async function () { - const provider = new Provider() - await provider.init() - web3.setProvider(provider as any) - }) + before(async function () { + const provider = new Provider() + await provider.init() + web3.setProvider(provider as any) + }) - describe('eth_getAccounts', () => { - it('should get a list of accounts', async function () { - const accounts: string[] = await web3.eth.getAccounts() - assert.notEqual(accounts.length, 0) - }) + describe('eth_getAccounts', () => { + it('should get a list of accounts', async function () { + const accounts: string[] = await web3.eth.getAccounts() + assert.notEqual(accounts.length, 0) }) + }) - describe('eth_getBalance', () => { - it('should get a account balance', async () => { - const accounts: string[] = await web3.eth.getAccounts() - const balance0: string = await web3.eth.getBalance(accounts[0]) - const balance1: string = await web3.eth.getBalance(accounts[1]) - const balance2: string = await web3.eth.getBalance(accounts[2]) + describe('eth_getBalance', () => { + it('should get a account balance', async () => { + const accounts: string[] = await web3.eth.getAccounts() + const balance0: string = await web3.eth.getBalance(accounts[0]) + const balance1: string = await web3.eth.getBalance(accounts[1]) + const balance2: string = await web3.eth.getBalance(accounts[2]) - assert.deepEqual(balance0, '100000000000000000000') - assert.deepEqual(balance1, '100000000000000000000') - assert.deepEqual(balance2, '100000000000000000000') - }) + assert.deepEqual(balance0, '100000000000000000000') + assert.deepEqual(balance1, '100000000000000000000') + assert.deepEqual(balance2, '100000000000000000000') }) + }) - describe('eth_sign', () => { - it('should sign payloads', async () => { - const accounts: string[] = await web3.eth.getAccounts() - const signature: string = await web3.eth.sign('Hello world', accounts[0]) + describe('eth_sign', () => { + it('should sign payloads', async () => { + const accounts: string[] = await web3.eth.getAccounts() + const signature: string = await web3.eth.sign('Hello world', accounts[0]) - assert.deepEqual(signature.length, 132) - }) + assert.deepEqual(signature.length, 132) }) + }) }) diff --git a/libs/remix-simulator/test/blocks.ts b/libs/remix-simulator/test/blocks.ts index 72b454b8dd..3ae74ed0c3 100644 --- a/libs/remix-simulator/test/blocks.ts +++ b/libs/remix-simulator/test/blocks.ts @@ -5,317 +5,317 @@ const web3 = new Web3() import * as assert from 'assert' describe('blocks', () => { - before(async () => { - const provider = new Provider({ - coinbase: '0x0000000000000000000000000000000000000001' - }) - await provider.init() - web3.setProvider(provider as any) + before(async () => { + const provider = new Provider({ + coinbase: '0x0000000000000000000000000000000000000001' }) + await provider.init() + web3.setProvider(provider as any) + }) - describe('eth_getBlockByNumber', () => { - it('should get block given its number', async () => { - const block = await web3.eth.getBlock(0) + describe('eth_getBlockByNumber', () => { + it('should get block given its number', async () => { + const block = await web3.eth.getBlock(0) - const expectedBlock = { - baseFeePerGas: 1, - difficulty: 0, - extraData: '0x0', - gasLimit: 8000000, - gasUsed: 0, - hash: block.hash.toString(), - logsBloom: '0xe670ec64341771606e55d6b4ca35a1a6b75ee3d5145a99d05921026d1527331', - miner: '0x0000000000000000000000000000000000000001', - nonce: '0x0000000000000000', - number: 0, - parentHash: '0x0000000000000000000000000000000000000000000000000000000000000000', - sha3Uncles: '0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347', - size: 163591, - stateRoot: '0x0000000000000000000000000000000000000000000000000000000000000000', - timestamp: block.timestamp, - totalDifficulty: '0', - transactions: [], - transactionsRoot: '0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421', - uncles: [] - } + const expectedBlock = { + baseFeePerGas: 1, + difficulty: 0, + extraData: '0x0', + gasLimit: 8000000, + gasUsed: 0, + hash: block.hash.toString(), + logsBloom: '0xe670ec64341771606e55d6b4ca35a1a6b75ee3d5145a99d05921026d1527331', + miner: '0x0000000000000000000000000000000000000001', + nonce: '0x0000000000000000', + number: 0, + parentHash: '0x0000000000000000000000000000000000000000000000000000000000000000', + sha3Uncles: '0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347', + size: 163591, + stateRoot: '0x0000000000000000000000000000000000000000000000000000000000000000', + timestamp: block.timestamp, + totalDifficulty: '0', + transactions: [], + transactionsRoot: '0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421', + uncles: [] + } - assert.deepEqual(block, expectedBlock) - }) + assert.deepEqual(block, expectedBlock) }) + }) - describe('eth_getGasPrice', () => { - it('should get gas price', async () => { - const gasPrice = await web3.eth.getGasPrice() - assert.equal(gasPrice, 1) - }) + describe('eth_getGasPrice', () => { + it('should get gas price', async () => { + const gasPrice = await web3.eth.getGasPrice() + assert.equal(gasPrice, 1) }) + }) - describe('eth_coinbase', () => { - it('should get coinbase', async () => { - const coinbase = await web3.eth.getCoinbase() - assert.equal(coinbase, '0x0000000000000000000000000000000000000001') - }) + describe('eth_coinbase', () => { + it('should get coinbase', async () => { + const coinbase = await web3.eth.getCoinbase() + assert.equal(coinbase, '0x0000000000000000000000000000000000000001') }) + }) - describe('eth_blockNumber', () => { - it('should get current block number', async () => { - const number = await web3.eth.getBlockNumber() - assert.equal(number, 0) - }) + describe('eth_blockNumber', () => { + it('should get current block number', async () => { + const number = await web3.eth.getBlockNumber() + assert.equal(number, 0) }) + }) - describe('eth_getBlockByHash', () => { - it('should get block given its hash', async () => { - const correctBlock = await web3.eth.getBlock(0) - const block = await web3.eth.getBlock(correctBlock.hash) + describe('eth_getBlockByHash', () => { + it('should get block given its hash', async () => { + const correctBlock = await web3.eth.getBlock(0) + const block = await web3.eth.getBlock(correctBlock.hash) - assert.deepEqual(block, correctBlock) - }) + assert.deepEqual(block, correctBlock) }) + }) - describe('eth_getBlockTransactionCountByHash', () => { - it('should get block given its hash', async () => { - const correctBlock = await web3.eth.getBlock(0) - const numberTransactions = await web3.eth.getBlockTransactionCount(correctBlock.hash) + describe('eth_getBlockTransactionCountByHash', () => { + it('should get block given its hash', async () => { + const correctBlock = await web3.eth.getBlock(0) + const numberTransactions = await web3.eth.getBlockTransactionCount(correctBlock.hash) - assert.deepEqual(numberTransactions, 0) - }) + assert.deepEqual(numberTransactions, 0) }) + }) - describe('eth_getBlockTransactionCountByNumber', () => { - it('should get block given its hash', async () => { - const numberTransactions = await web3.eth.getBlockTransactionCount(0) + describe('eth_getBlockTransactionCountByNumber', () => { + it('should get block given its hash', async () => { + const numberTransactions = await web3.eth.getBlockTransactionCount(0) - assert.deepEqual(numberTransactions, 0) - }) + assert.deepEqual(numberTransactions, 0) }) + }) - describe('eth_getUncleCountByBlockHash', () => { - it('should get block given its hash', async () => { - const correctBlock = await web3.eth.getBlock(0) - const numberTransactions = await (new Promise((resolve, reject) => { - web3['_requestManager'].send({method: 'eth_getUncleCountByBlockHash', params: [correctBlock.hash]}, (err, numberTransactions) => { - if (err) return reject(err) - resolve(numberTransactions) - }) - })) - assert.deepEqual(numberTransactions, correctBlock.uncles.length) + describe('eth_getUncleCountByBlockHash', () => { + it('should get block given its hash', async () => { + const correctBlock = await web3.eth.getBlock(0) + const numberTransactions = await (new Promise((resolve, reject) => { + web3['_requestManager'].send({method: 'eth_getUncleCountByBlockHash', params: [correctBlock.hash]}, (err, numberTransactions) => { + if (err) return reject(err) + resolve(numberTransactions) }) + })) + assert.deepEqual(numberTransactions, correctBlock.uncles.length) }) + }) - describe('eth_getUncleCountByBlockNumber', () => { - it('should get block given its number', async () => { - const correctBlock = await web3.eth.getBlock(0) - const numberTransactions = await (new Promise((resolve, reject) => { - web3['_requestManager'].send({method: 'eth_getUncleCountByBlockHash', params: [0]}, (err, numberTransactions) => { - if (err) return reject(err) - resolve(numberTransactions) - }) - })) - assert.deepEqual(numberTransactions, correctBlock.uncles.length) + describe('eth_getUncleCountByBlockNumber', () => { + it('should get block given its number', async () => { + const correctBlock = await web3.eth.getBlock(0) + const numberTransactions = await (new Promise((resolve, reject) => { + web3['_requestManager'].send({method: 'eth_getUncleCountByBlockHash', params: [0]}, (err, numberTransactions) => { + if (err) return reject(err) + resolve(numberTransactions) }) + })) + assert.deepEqual(numberTransactions, correctBlock.uncles.length) }) - describe('eth_getStorageAt', () => { - it('should get storage at position at given address', async () => { - const abi: any = [ - { - 'constant': false, - 'inputs': [ - { - 'name': 'x', - 'type': 'uint256' - } - ], - 'name': 'set', - 'outputs': [], - 'payable': false, - 'stateMutability': 'nonpayable', - 'type': 'function' - }, - { - 'constant': false, - 'inputs': [ - { - 'name': 'x', - 'type': 'uint256' - } - ], - 'name': 'set2', - 'outputs': [], - 'payable': false, - 'stateMutability': 'nonpayable', - 'type': 'function' - }, - { - 'inputs': [ - { - 'name': 'initialValue', - 'type': 'uint256' - } - ], - 'payable': false, - 'stateMutability': 'nonpayable', - 'type': 'constructor' - }, - { - 'anonymous': false, - 'inputs': [ - { - 'indexed': true, - 'name': 'value', - 'type': 'uint256' - } - ], - 'name': 'Test', - 'type': 'event' - }, - { - 'constant': true, - 'inputs': [], - 'name': 'get', - 'outputs': [ - { - 'name': 'retVal', - 'type': 'uint256' - } - ], - 'payable': false, - 'stateMutability': 'view', - 'type': 'function' - }, - { - 'constant': true, - 'inputs': [], - 'name': 'storedData', - 'outputs': [ - { - 'name': '', - 'type': 'uint256' - } - ], - 'payable': false, - 'stateMutability': 'view', - 'type': 'function' - } - ] + }) + describe('eth_getStorageAt', () => { + it('should get storage at position at given address', async () => { + const abi: any = [ + { + 'constant': false, + 'inputs': [ + { + 'name': 'x', + 'type': 'uint256' + } + ], + 'name': 'set', + 'outputs': [], + 'payable': false, + 'stateMutability': 'nonpayable', + 'type': 'function' + }, + { + 'constant': false, + 'inputs': [ + { + 'name': 'x', + 'type': 'uint256' + } + ], + 'name': 'set2', + 'outputs': [], + 'payable': false, + 'stateMutability': 'nonpayable', + 'type': 'function' + }, + { + 'inputs': [ + { + 'name': 'initialValue', + 'type': 'uint256' + } + ], + 'payable': false, + 'stateMutability': 'nonpayable', + 'type': 'constructor' + }, + { + 'anonymous': false, + 'inputs': [ + { + 'indexed': true, + 'name': 'value', + 'type': 'uint256' + } + ], + 'name': 'Test', + 'type': 'event' + }, + { + 'constant': true, + 'inputs': [], + 'name': 'get', + 'outputs': [ + { + 'name': 'retVal', + 'type': 'uint256' + } + ], + 'payable': false, + 'stateMutability': 'view', + 'type': 'function' + }, + { + 'constant': true, + 'inputs': [], + 'name': 'storedData', + 'outputs': [ + { + 'name': '', + 'type': 'uint256' + } + ], + 'payable': false, + 'stateMutability': 'view', + 'type': 'function' + } + ] - const code = '0x608060405234801561001057600080fd5b506040516020806102018339810180604052602081101561003057600080fd5b810190808051906020019092919050505080600081905550506101a9806100586000396000f3fe60806040526004361061005c576000357c0100000000000000000000000000000000000000000000000000000000900480632a1afcd91461006157806360fe47b11461008c5780636d4ce63c146100c7578063ce01e1ec146100f2575b600080fd5b34801561006d57600080fd5b5061007661012d565b6040518082815260200191505060405180910390f35b34801561009857600080fd5b506100c5600480360360208110156100af57600080fd5b8101908080359060200190929190505050610133565b005b3480156100d357600080fd5b506100dc61013d565b6040518082815260200191505060405180910390f35b3480156100fe57600080fd5b5061012b6004803603602081101561011557600080fd5b8101908080359060200190929190505050610146565b005b60005481565b8060008190555050565b60008054905090565b80600081905550807f63a242a632efe33c0e210e04e4173612a17efa4f16aa4890bc7e46caece80de060405160405180910390a25056fea165627a7a7230582063160eb16dc361092a85ced1a773eed0b63738b83bea1e1c51cf066fa90e135d0029' + const code = '0x608060405234801561001057600080fd5b506040516020806102018339810180604052602081101561003057600080fd5b810190808051906020019092919050505080600081905550506101a9806100586000396000f3fe60806040526004361061005c576000357c0100000000000000000000000000000000000000000000000000000000900480632a1afcd91461006157806360fe47b11461008c5780636d4ce63c146100c7578063ce01e1ec146100f2575b600080fd5b34801561006d57600080fd5b5061007661012d565b6040518082815260200191505060405180910390f35b34801561009857600080fd5b506100c5600480360360208110156100af57600080fd5b8101908080359060200190929190505050610133565b005b3480156100d357600080fd5b506100dc61013d565b6040518082815260200191505060405180910390f35b3480156100fe57600080fd5b5061012b6004803603602081101561011557600080fd5b8101908080359060200190929190505050610146565b005b60005481565b8060008190555050565b60008054905090565b80600081905550807f63a242a632efe33c0e210e04e4173612a17efa4f16aa4890bc7e46caece80de060405160405180910390a25056fea165627a7a7230582063160eb16dc361092a85ced1a773eed0b63738b83bea1e1c51cf066fa90e135d0029' - const contract = new web3.eth.Contract(abi) - const accounts = await web3.eth.getAccounts() + const contract = new web3.eth.Contract(abi) + const accounts = await web3.eth.getAccounts() - const contractInstance: any = await contract.deploy({ data: code, arguments: [100] }).send({ from: accounts[0], gas: 400000 }) - contractInstance.currentProvider = web3.eth.currentProvider - contractInstance.givenProvider = web3.eth.currentProvider + const contractInstance: any = await contract.deploy({ data: code, arguments: [100] }).send({ from: accounts[0], gas: 400000 }) + contractInstance.currentProvider = web3.eth.currentProvider + contractInstance.givenProvider = web3.eth.currentProvider - await contractInstance.methods.set(100).send({ from: accounts[0].toLowerCase(), gas: 400000 }) - let storage = await web3.eth.getStorageAt(contractInstance.options.address, 0) - assert.deepEqual(storage, '0x64') + await contractInstance.methods.set(100).send({ from: accounts[0].toLowerCase(), gas: 400000 }) + let storage = await web3.eth.getStorageAt(contractInstance.options.address, 0) + assert.deepEqual(storage, '0x64') - await contractInstance.methods.set(200).send({ from: accounts[0].toLowerCase(), gas: 400000 }) - storage = await web3.eth.getStorageAt(contractInstance.options.address, 0) - assert.deepEqual(storage, '0xc8') + await contractInstance.methods.set(200).send({ from: accounts[0].toLowerCase(), gas: 400000 }) + storage = await web3.eth.getStorageAt(contractInstance.options.address, 0) + assert.deepEqual(storage, '0xc8') - await contractInstance.methods.set(1).send({ from: accounts[0].toLowerCase(), gas: 400000 }) - storage = await web3.eth.getStorageAt(contractInstance.options.address, 0) - assert.deepEqual(storage, '0x01') - }) + await contractInstance.methods.set(1).send({ from: accounts[0].toLowerCase(), gas: 400000 }) + storage = await web3.eth.getStorageAt(contractInstance.options.address, 0) + assert.deepEqual(storage, '0x01') }) - describe('eth_call', () => { - it('should get a value', async () => { - const abi: any = [ - { - 'constant': false, - 'inputs': [ - { - 'name': 'x', - 'type': 'uint256' - } - ], - 'name': 'set', - 'outputs': [], - 'payable': false, - 'stateMutability': 'nonpayable', - 'type': 'function' - }, - { - 'constant': false, - 'inputs': [ - { - 'name': 'x', - 'type': 'uint256' - } - ], - 'name': 'set2', - 'outputs': [], - 'payable': false, - 'stateMutability': 'nonpayable', - 'type': 'function' - }, - { - 'inputs': [ - { - 'name': 'initialValue', - 'type': 'uint256' - } - ], - 'payable': false, - 'stateMutability': 'nonpayable', - 'type': 'constructor' - }, - { - 'anonymous': false, - 'inputs': [ - { - 'indexed': true, - 'name': 'value', - 'type': 'uint256' - } - ], - 'name': 'Test', - 'type': 'event' - }, - { - 'constant': true, - 'inputs': [], - 'name': 'get', - 'outputs': [ - { - 'name': 'retVal', - 'type': 'uint256' - } - ], - 'payable': false, - 'stateMutability': 'view', - 'type': 'function' - }, - { - 'constant': true, - 'inputs': [], - 'name': 'storedData', - 'outputs': [ - { - 'name': '', - 'type': 'uint256' - } - ], - 'payable': false, - 'stateMutability': 'view', - 'type': 'function' - } - ] + }) + describe('eth_call', () => { + it('should get a value', async () => { + const abi: any = [ + { + 'constant': false, + 'inputs': [ + { + 'name': 'x', + 'type': 'uint256' + } + ], + 'name': 'set', + 'outputs': [], + 'payable': false, + 'stateMutability': 'nonpayable', + 'type': 'function' + }, + { + 'constant': false, + 'inputs': [ + { + 'name': 'x', + 'type': 'uint256' + } + ], + 'name': 'set2', + 'outputs': [], + 'payable': false, + 'stateMutability': 'nonpayable', + 'type': 'function' + }, + { + 'inputs': [ + { + 'name': 'initialValue', + 'type': 'uint256' + } + ], + 'payable': false, + 'stateMutability': 'nonpayable', + 'type': 'constructor' + }, + { + 'anonymous': false, + 'inputs': [ + { + 'indexed': true, + 'name': 'value', + 'type': 'uint256' + } + ], + 'name': 'Test', + 'type': 'event' + }, + { + 'constant': true, + 'inputs': [], + 'name': 'get', + 'outputs': [ + { + 'name': 'retVal', + 'type': 'uint256' + } + ], + 'payable': false, + 'stateMutability': 'view', + 'type': 'function' + }, + { + 'constant': true, + 'inputs': [], + 'name': 'storedData', + 'outputs': [ + { + 'name': '', + 'type': 'uint256' + } + ], + 'payable': false, + 'stateMutability': 'view', + 'type': 'function' + } + ] - const code = '0x608060405234801561001057600080fd5b506040516020806102018339810180604052602081101561003057600080fd5b810190808051906020019092919050505080600081905550506101a9806100586000396000f3fe60806040526004361061005c576000357c0100000000000000000000000000000000000000000000000000000000900480632a1afcd91461006157806360fe47b11461008c5780636d4ce63c146100c7578063ce01e1ec146100f2575b600080fd5b34801561006d57600080fd5b5061007661012d565b6040518082815260200191505060405180910390f35b34801561009857600080fd5b506100c5600480360360208110156100af57600080fd5b8101908080359060200190929190505050610133565b005b3480156100d357600080fd5b506100dc61013d565b6040518082815260200191505060405180910390f35b3480156100fe57600080fd5b5061012b6004803603602081101561011557600080fd5b8101908080359060200190929190505050610146565b005b60005481565b8060008190555050565b60008054905090565b80600081905550807f63a242a632efe33c0e210e04e4173612a17efa4f16aa4890bc7e46caece80de060405160405180910390a25056fea165627a7a7230582063160eb16dc361092a85ced1a773eed0b63738b83bea1e1c51cf066fa90e135d0029' + const code = '0x608060405234801561001057600080fd5b506040516020806102018339810180604052602081101561003057600080fd5b810190808051906020019092919050505080600081905550506101a9806100586000396000f3fe60806040526004361061005c576000357c0100000000000000000000000000000000000000000000000000000000900480632a1afcd91461006157806360fe47b11461008c5780636d4ce63c146100c7578063ce01e1ec146100f2575b600080fd5b34801561006d57600080fd5b5061007661012d565b6040518082815260200191505060405180910390f35b34801561009857600080fd5b506100c5600480360360208110156100af57600080fd5b8101908080359060200190929190505050610133565b005b3480156100d357600080fd5b506100dc61013d565b6040518082815260200191505060405180910390f35b3480156100fe57600080fd5b5061012b6004803603602081101561011557600080fd5b8101908080359060200190929190505050610146565b005b60005481565b8060008190555050565b60008054905090565b80600081905550807f63a242a632efe33c0e210e04e4173612a17efa4f16aa4890bc7e46caece80de060405160405180910390a25056fea165627a7a7230582063160eb16dc361092a85ced1a773eed0b63738b83bea1e1c51cf066fa90e135d0029' - const contract = new web3.eth.Contract(abi) - const accounts = await web3.eth.getAccounts() + const contract = new web3.eth.Contract(abi) + const accounts = await web3.eth.getAccounts() - const contractInstance: any = await contract.deploy({ data: code, arguments: [100] }).send({ from: accounts[0], gas: 400000 }) - contractInstance.currentProvider = web3.eth.currentProvider - contractInstance.givenProvider = web3.eth.currentProvider + const contractInstance: any = await contract.deploy({ data: code, arguments: [100] }).send({ from: accounts[0], gas: 400000 }) + contractInstance.currentProvider = web3.eth.currentProvider + contractInstance.givenProvider = web3.eth.currentProvider - const value = await contractInstance.methods.get().call({ from: accounts[0] }) - assert.deepEqual(value, 100) - }) + const value = await contractInstance.methods.get().call({ from: accounts[0] }) + assert.deepEqual(value, 100) }) + }) }) diff --git a/libs/remix-simulator/test/misc.ts b/libs/remix-simulator/test/misc.ts index a339910996..b384f497a6 100644 --- a/libs/remix-simulator/test/misc.ts +++ b/libs/remix-simulator/test/misc.ts @@ -5,109 +5,109 @@ const web3 = new Web3() import * as assert from 'assert' describe('Misc', () => { - before(async () => { - const provider = new Provider() - await provider.init() - web3.setProvider(provider as any) - }) + before(async () => { + const provider = new Provider() + await provider.init() + web3.setProvider(provider as any) + }) - describe('web3_clientVersion', () => { - it('should get correct remix simulator version', async (done) => { - web3['_requestManager'].send({ method: 'web3_clientVersion', params: [] }, (err, version) => { - if (err) { - throw new Error(err) - } - const remixVersion = require('../package.json').version - assert.equal(version, 'Remix Simulator/' + remixVersion) - done() - }) - }) + describe('web3_clientVersion', () => { + it('should get correct remix simulator version', async (done) => { + web3['_requestManager'].send({ method: 'web3_clientVersion', params: [] }, (err, version) => { + if (err) { + throw new Error(err) + } + const remixVersion = require('../package.json').version + assert.equal(version, 'Remix Simulator/' + remixVersion) + done() + }) }) + }) - describe('eth_protocolVersion', () => { - it('should get protocol version', async () => { - web3['_requestManager'].send({ method: 'eth_protocolVersion', params: [] }, (err, result) => { - if (err) { - throw new Error(err) - } - assert.equal(result, '0x3f') - }) - }) + describe('eth_protocolVersion', () => { + it('should get protocol version', async () => { + web3['_requestManager'].send({ method: 'eth_protocolVersion', params: [] }, (err, result) => { + if (err) { + throw new Error(err) + } + assert.equal(result, '0x3f') + }) }) + }) - describe('eth_syncing', () => { - it('should get if is syncing', async () => { - const isSyncing = await web3.eth.isSyncing() - assert.equal(isSyncing, false) - }) + describe('eth_syncing', () => { + it('should get if is syncing', async () => { + const isSyncing = await web3.eth.isSyncing() + assert.equal(isSyncing, false) }) + }) - describe('eth_mining', () => { - it('should get if is mining', async () => { - const isMining = await web3.eth.isMining() - assert.equal(isMining, false) - }) + describe('eth_mining', () => { + it('should get if is mining', async () => { + const isMining = await web3.eth.isMining() + assert.equal(isMining, false) }) + }) - describe('eth_hashrate', () => { - it('should get hashrate', async () => { - const hashrate = await web3.eth.getHashrate() - assert.equal(hashrate, 0) - }) + describe('eth_hashrate', () => { + it('should get hashrate', async () => { + const hashrate = await web3.eth.getHashrate() + assert.equal(hashrate, 0) }) + }) - describe('web3_sha3', () => { - it('should get result of a sha3', async () => { - web3['_requestManager'].send({ method: 'web3_sha3', params: ['0x68656c6c6f20776f726c64'] }, (err, result) => { - if (err) { - throw new Error(err) - } - assert.equal(result, '0x47173285a8d7341e5e972fc677286384f802f8ef42a5ec5f03bbfa254cb01fad') - }) - }) + describe('web3_sha3', () => { + it('should get result of a sha3', async () => { + web3['_requestManager'].send({ method: 'web3_sha3', params: ['0x68656c6c6f20776f726c64'] }, (err, result) => { + if (err) { + throw new Error(err) + } + assert.equal(result, '0x47173285a8d7341e5e972fc677286384f802f8ef42a5ec5f03bbfa254cb01fad') + }) }) + }) - describe('eth_getCompilers', () => { - it('should get list of compilers', async () => { - web3['_requestManager'].send({ method: 'eth_getCompilers', params: [] }, (err, result) => { - if (err) { - throw new Error(err) - } - assert.equal(result, 0) - }) - }) + describe('eth_getCompilers', () => { + it('should get list of compilers', async () => { + web3['_requestManager'].send({ method: 'eth_getCompilers', params: [] }, (err, result) => { + if (err) { + throw new Error(err) + } + assert.equal(result, 0) + }) }) + }) - describe('eth_compileSolidity', () => { - it('get unsupported result when requesting solidity compiler', async () => { - web3['_requestManager'].send({ method: 'eth_compileSolidity', params: [] }, (err, result) => { - if (err) { - throw new Error(err) - } - assert.equal(result, 'unsupported') - }) - }) + describe('eth_compileSolidity', () => { + it('get unsupported result when requesting solidity compiler', async () => { + web3['_requestManager'].send({ method: 'eth_compileSolidity', params: [] }, (err, result) => { + if (err) { + throw new Error(err) + } + assert.equal(result, 'unsupported') + }) }) + }) - describe('eth_compileLLL', () => { - it('get unsupported result when requesting LLL compiler', async () => { - web3['_requestManager'].send({ method: 'eth_compileLLL', params: [] }, (err, result) => { - if (err) { - throw new Error(err) - } - assert.equal(result, 'unsupported') - }) - }) + describe('eth_compileLLL', () => { + it('get unsupported result when requesting LLL compiler', async () => { + web3['_requestManager'].send({ method: 'eth_compileLLL', params: [] }, (err, result) => { + if (err) { + throw new Error(err) + } + assert.equal(result, 'unsupported') + }) }) + }) - describe('eth_compileSerpent', () => { - it('get unsupported result when requesting serpent compiler', async () => { - web3['_requestManager'].send({ method: 'eth_compileSerpent', params: [] }, (err, result) => { - if (err) { - throw new Error(err) - } - assert.equal(result, 'unsupported') - }) - }) + describe('eth_compileSerpent', () => { + it('get unsupported result when requesting serpent compiler', async () => { + web3['_requestManager'].send({ method: 'eth_compileSerpent', params: [] }, (err, result) => { + if (err) { + throw new Error(err) + } + assert.equal(result, 'unsupported') + }) }) + }) }) diff --git a/libs/remix-solidity/src/compiler/compiler-abstract.ts b/libs/remix-solidity/src/compiler/compiler-abstract.ts index 52371b1d4f..e16f0e7566 100644 --- a/libs/remix-solidity/src/compiler/compiler-abstract.ts +++ b/libs/remix-solidity/src/compiler/compiler-abstract.ts @@ -3,53 +3,53 @@ import helper from './helper' import { CompilationResult, CompilerInput, CompilationSourceCode } from './types' export class CompilerAbstract { - languageversion: string - data: CompilationResult - source: CompilationSourceCode - input: CompilerInput - constructor (languageversion: string, data: CompilationResult, source: CompilationSourceCode, input?: CompilerInput) { - this.languageversion = languageversion - this.data = data - this.source = source // source code - this.input = input - } - - getContracts () { - return this.data.contracts || {} - } - - getContract (name) { - return helper.getContract(name, this.data.contracts) - } - - visitContracts (calllback) { - return helper.visitContracts(this.data.contracts, calllback) - } - - getData () { - return this.data - } - - getInput () { - return this.input - } - - getAsts () { - return this.data.sources // ast - } - - getSourceName (fileIndex) { - if (this.data && this.data.sources) { - return Object.keys(this.data.sources)[fileIndex] - } else if (Object.keys(this.source.sources).length === 1) { - // if we don't have ast, we return the only one filename present. - const sourcesArray = Object.keys(this.source.sources) - return sourcesArray[0] - } - return null - } - - getSourceCode () { - return this.source - } + languageversion: string + data: CompilationResult + source: CompilationSourceCode + input: CompilerInput + constructor (languageversion: string, data: CompilationResult, source: CompilationSourceCode, input?: CompilerInput) { + this.languageversion = languageversion + this.data = data + this.source = source // source code + this.input = input + } + + getContracts () { + return this.data.contracts || {} + } + + getContract (name) { + return helper.getContract(name, this.data.contracts) + } + + visitContracts (calllback) { + return helper.visitContracts(this.data.contracts, calllback) + } + + getData () { + return this.data + } + + getInput () { + return this.input + } + + getAsts () { + return this.data.sources // ast + } + + getSourceName (fileIndex) { + if (this.data && this.data.sources) { + return Object.keys(this.data.sources)[fileIndex] + } else if (Object.keys(this.source.sources).length === 1) { + // if we don't have ast, we return the only one filename present. + const sourcesArray = Object.keys(this.source.sources) + return sourcesArray[0] + } + return null + } + + getSourceCode () { + return this.source + } } diff --git a/libs/remix-solidity/src/compiler/compiler-helpers.ts b/libs/remix-solidity/src/compiler/compiler-helpers.ts index 3097eeab28..16d17fc657 100644 --- a/libs/remix-solidity/src/compiler/compiler-helpers.ts +++ b/libs/remix-solidity/src/compiler/compiler-helpers.ts @@ -4,16 +4,16 @@ import { CompilerAbstract } from './compiler-abstract' import { Compiler } from './compiler' export const compile = (compilationTargets, settings, contentResolverCallback): Promise => { - return new Promise((resolve, reject) => { - const compiler = new Compiler(contentResolverCallback) - compiler.set('evmVersion', settings.evmVersion) - compiler.set('optimize', settings.optimize) - compiler.set('language', settings.language) - compiler.set('runs', settings.runs) - compiler.loadVersion(canUseWorker(settings.version), urlFromVersion(settings.version)) - compiler.event.register('compilationFinished', (success, compilationData, source, input, version) => { - resolve(new CompilerAbstract(settings.version, compilationData, source, input)) - }) - compiler.event.register('compilerLoaded', _ => compiler.compile(compilationTargets, '')) + return new Promise((resolve, reject) => { + const compiler = new Compiler(contentResolverCallback) + compiler.set('evmVersion', settings.evmVersion) + compiler.set('optimize', settings.optimize) + compiler.set('language', settings.language) + compiler.set('runs', settings.runs) + compiler.loadVersion(canUseWorker(settings.version), urlFromVersion(settings.version)) + compiler.event.register('compilationFinished', (success, compilationData, source, input, version) => { + resolve(new CompilerAbstract(settings.version, compilationData, source, input)) }) + compiler.event.register('compilerLoaded', _ => compiler.compile(compilationTargets, '')) + }) } diff --git a/libs/remix-solidity/src/compiler/compiler-input.ts b/libs/remix-solidity/src/compiler/compiler-input.ts index 8598c9dfd4..a2a3a71226 100644 --- a/libs/remix-solidity/src/compiler/compiler-input.ts +++ b/libs/remix-solidity/src/compiler/compiler-input.ts @@ -3,52 +3,52 @@ import { CompilerInput, Source, CompilerInputOptions, Language } from './types' export default (sources: Source, opts: CompilerInputOptions): string => { - const o: CompilerInput = { - language: 'Solidity', - sources: sources, - settings: { - optimizer: { - enabled: opts.optimize === true || opts.optimize === 1, - runs: opts.runs > -1 ? opts.runs : 200 - }, - libraries: opts.libraries, - outputSelection: { - '*': { - '': ['ast'], - '*': ['abi', 'metadata', 'devdoc', 'userdoc', 'storageLayout', 'evm.legacyAssembly', 'evm.bytecode', 'evm.deployedBytecode', 'evm.methodIdentifiers', 'evm.gasEstimates', 'evm.assembly'] - } - } + const o: CompilerInput = { + language: 'Solidity', + sources: sources, + settings: { + optimizer: { + enabled: opts.optimize === true || opts.optimize === 1, + runs: opts.runs > -1 ? opts.runs : 200 + }, + libraries: opts.libraries, + outputSelection: { + '*': { + '': ['ast'], + '*': ['abi', 'metadata', 'devdoc', 'userdoc', 'storageLayout', 'evm.legacyAssembly', 'evm.bytecode', 'evm.deployedBytecode', 'evm.methodIdentifiers', 'evm.gasEstimates', 'evm.assembly'] } - } - if (opts.evmVersion) { - if (opts.evmVersion.toLowerCase() == 'default') { - opts.evmVersion = null - } else { - o.settings.evmVersion = opts.evmVersion - } - } - if (opts.language) { - o.language = opts.language + } } - if (opts.language === 'Yul' && o.settings.optimizer.enabled) { - if (!o.settings.optimizer.details) { o.settings.optimizer.details = {} } - o.settings.optimizer.details.yul = true + } + if (opts.evmVersion) { + if (opts.evmVersion.toLowerCase() == 'default') { + opts.evmVersion = null + } else { + o.settings.evmVersion = opts.evmVersion } - return JSON.stringify(o) + } + if (opts.language) { + o.language = opts.language + } + if (opts.language === 'Yul' && o.settings.optimizer.enabled) { + if (!o.settings.optimizer.details) { o.settings.optimizer.details = {} } + o.settings.optimizer.details.yul = true + } + return JSON.stringify(o) } export const Languages = ['Solidity', 'Yul'] export function getValidLanguage (val: string): Language { - if (val !== undefined && val !== null && val) { - const lang = val.slice(0, 1).toUpperCase() + val.slice(1).toLowerCase() - return Languages.indexOf(lang) > -1 ? lang as Language : null - } - return null + if (val !== undefined && val !== null && val) { + const lang = val.slice(0, 1).toUpperCase() + val.slice(1).toLowerCase() + return Languages.indexOf(lang) > -1 ? lang as Language : null + } + return null } export function compilerInputForConfigFile(sources: Source, opts) { - opts.sources = sources - return JSON.stringify(opts) + opts.sources = sources + return JSON.stringify(opts) } diff --git a/libs/remix-solidity/src/compiler/compiler-utils.ts b/libs/remix-solidity/src/compiler/compiler-utils.ts index 456264aa03..de5817d1ba 100644 --- a/libs/remix-solidity/src/compiler/compiler-utils.ts +++ b/libs/remix-solidity/src/compiler/compiler-utils.ts @@ -11,22 +11,22 @@ export const pathToURL = {} * @param version is the version of compiler with or without 'soljson-v' prefix and .js postfix */ export function urlFromVersion (version) { - let url - if (version === 'builtin') { - let location: string | Location = window.document.location - let path = location.pathname - if (!path.startsWith('/')) path = '/' + path - location = `${location.protocol}//${location.host}${path}` - if (location.endsWith('index.html')) location = location.substring(0, location.length - 10) - if (!location.endsWith('/')) location += '/' - url = `${location}assets/js/soljson.js` - } else { - version = version.replace('.Emscripten.clang', '') - if (!version.startsWith('soljson-v')) version = 'soljson-v' + version - if (!version.endsWith('.js')) version = version + '.js' - url = `${pathToURL[version]}/${version}` - } - return url + let url + if (version === 'builtin') { + let location: string | Location = window.document.location + let path = location.pathname + if (!path.startsWith('/')) path = '/' + path + location = `${location.protocol}//${location.host}${path}` + if (location.endsWith('index.html')) location = location.substring(0, location.length - 10) + if (!location.endsWith('/')) location += '/' + url = `${location}assets/js/soljson.js` + } else { + version = version.replace('.Emscripten.clang', '') + if (!version.startsWith('soljson-v')) version = 'soljson-v' + version + if (!version.endsWith('.js')) version = version + '.js' + url = `${pathToURL[version]}/${version}` + } + return url } /** @@ -34,23 +34,23 @@ export function urlFromVersion (version) { * checks a compiler whitelist, browser support and OS. */ export function canUseWorker (selectedVersion) { - if (selectedVersion.startsWith('http')) { - return browserSupportWorker() - } - const version = semver.coerce(selectedVersion) - if (!version) { - return browserSupportWorker() - } - const isNightly = selectedVersion.includes('nightly') - return browserSupportWorker() && ( - // All compiler versions (including nightlies) after 0.6.3 are wasm compiled - semver.gt(version, '0.6.3') || + if (selectedVersion.startsWith('http')) { + return browserSupportWorker() + } + const version = semver.coerce(selectedVersion) + if (!version) { + return browserSupportWorker() + } + const isNightly = selectedVersion.includes('nightly') + return browserSupportWorker() && ( + // All compiler versions (including nightlies) after 0.6.3 are wasm compiled + semver.gt(version, '0.6.3') || // Only releases are wasm compiled starting with 0.3.6 (semver.gte(version, '0.3.6') && !isNightly) - ) + ) } function browserSupportWorker () { - return document ? document.location.protocol !== 'file:' && Worker !== undefined : false + return document ? document.location.protocol !== 'file:' && Worker !== undefined : false } diff --git a/libs/remix-solidity/src/compiler/compiler.ts b/libs/remix-solidity/src/compiler/compiler.ts index b26b2233b3..0b29afca73 100644 --- a/libs/remix-solidity/src/compiler/compiler.ts +++ b/libs/remix-solidity/src/compiler/compiler.ts @@ -6,509 +6,509 @@ import EventManager from '../lib/eventManager' import txHelper from './helper' import { - Source, SourceWithTarget, MessageFromWorker, CompilerState, CompilationResult, - visitContractsCallbackParam, visitContractsCallbackInterface, CompilationError, - gatherImportsCallbackInterface, - isFunctionDescription, CompilerRetriggerMode, EsWebWorkerHandlerInterface + Source, SourceWithTarget, MessageFromWorker, CompilerState, CompilationResult, + visitContractsCallbackParam, visitContractsCallbackInterface, CompilationError, + gatherImportsCallbackInterface, + isFunctionDescription, CompilerRetriggerMode, EsWebWorkerHandlerInterface } from './types' /* trigger compilationFinished, compilerLoaded, compilationStarted, compilationDuration */ export class Compiler { - event - state: CompilerState - handleImportCall - workerHandler: EsWebWorkerHandlerInterface - constructor(handleImportCall?: (fileurl: string, cb) => void) { - this.event = new EventManager() - this.handleImportCall = handleImportCall - this.state = { - compileJSON: null, - worker: null, - currentVersion: null, - compilerLicense: null, - optimize: false, - runs: 200, - evmVersion: null, - language: 'Solidity', - compilationStartTime: null, - target: null, - useFileConfiguration: false, - configFileContent: '', - compilerRetriggerMode: CompilerRetriggerMode.none, - lastCompilationResult: { - data: null, - source: null - } - } + event + state: CompilerState + handleImportCall + workerHandler: EsWebWorkerHandlerInterface + constructor(handleImportCall?: (fileurl: string, cb) => void) { + this.event = new EventManager() + this.handleImportCall = handleImportCall + this.state = { + compileJSON: null, + worker: null, + currentVersion: null, + compilerLicense: null, + optimize: false, + runs: 200, + evmVersion: null, + language: 'Solidity', + compilationStartTime: null, + target: null, + useFileConfiguration: false, + configFileContent: '', + compilerRetriggerMode: CompilerRetriggerMode.none, + lastCompilationResult: { + data: null, + source: null + } + } - this.loadWorkerHandler() + this.loadWorkerHandler() - this.event.register('compilationFinished', (success: boolean, data: CompilationResult, source: SourceWithTarget, input: string, version: string) => { - if (success && this.state.compilationStartTime) { - this.event.trigger('compilationDuration', [(new Date().getTime()) - this.state.compilationStartTime]) - } - this.state.compilationStartTime = null - }) + this.event.register('compilationFinished', (success: boolean, data: CompilationResult, source: SourceWithTarget, input: string, version: string) => { + if (success && this.state.compilationStartTime) { + this.event.trigger('compilationDuration', [(new Date().getTime()) - this.state.compilationStartTime]) + } + this.state.compilationStartTime = null + }) - this.event.register('compilationStarted', () => { - }) - } + this.event.register('compilationStarted', () => { + }) + } - /** + /** * @dev Setter function for CompilerState's properties (used by IDE) * @param key key * @param value value of key in CompilerState */ - set(key: K, value: CompilerState[K]): void { - this.state[key] = value - if (key === 'runs') this.state['runs'] = parseInt(value) - } + set(key: K, value: CompilerState[K]): void { + this.state[key] = value + if (key === 'runs') this.state['runs'] = parseInt(value) + } - async loadWorkerHandler() { - if (this.workerHandler) return - if (typeof (window) !== 'undefined' && Worker) { - const ESWebWorker = await import('../lib/es-web-worker/es-web-worker-handler') - this.workerHandler = new ESWebWorker.default() - } + async loadWorkerHandler() { + if (this.workerHandler) return + if (typeof (window) !== 'undefined' && Worker) { + const ESWebWorker = await import('../lib/es-web-worker/es-web-worker-handler') + this.workerHandler = new ESWebWorker.default() } + } - /** + /** * @dev Internal function to compile the contract after gathering imports * @param files source file * @param missingInputs missing import file path list */ - internalCompile(files: Source, missingInputs?: string[]): void { - this.gatherImports(files, missingInputs, (error, input) => { - if (error) { - this.state.lastCompilationResult = null - this.event.trigger('compilationFinished', [false, { error: { formattedMessage: error, severity: 'error' } }, files, input, this.state.currentVersion]) - } else if (this.state.compileJSON && input) { this.state.compileJSON(input) } - }) - } + internalCompile(files: Source, missingInputs?: string[]): void { + this.gatherImports(files, missingInputs, (error, input) => { + if (error) { + this.state.lastCompilationResult = null + this.event.trigger('compilationFinished', [false, { error: { formattedMessage: error, severity: 'error' } }, files, input, this.state.currentVersion]) + } else if (this.state.compileJSON && input) { this.state.compileJSON(input) } + }) + } - /** + /** * @dev Compile source files (used by IDE) * @param files source files * @param target target file name (This is passed as it is to IDE) */ - compile(files: Source, target: string): void { - this.state.target = target - this.state.compilationStartTime = new Date().getTime() - this.event.trigger('compilationStarted', []) - this.internalCompile(files) - } + compile(files: Source, target: string): void { + this.state.target = target + this.state.compilationStartTime = new Date().getTime() + this.event.trigger('compilationStarted', []) + this.internalCompile(files) + } - /** + /** * @dev Called when compiler is loaded, set current compiler version * @param version compiler version */ - onCompilerLoaded(version: string, license: string): void { - this.state.currentVersion = version - this.state.compilerLicense = license - this.event.trigger('compilerLoaded', [version, license]) - } + onCompilerLoaded(version: string, license: string): void { + this.state.currentVersion = version + this.state.compilerLicense = license + this.event.trigger('compilerLoaded', [version, license]) + } - /** + /** * @dev Called when compiler is loaded internally (without worker) */ - onInternalCompilerLoaded(): void { - if (this.state.worker === null) { + onInternalCompilerLoaded(): void { + if (this.state.worker === null) { const compiler: any = typeof (window) !== 'undefined' && window['Module'] ? require('solc/wrapper')(window['Module']) : require('solc') // eslint-disable-line - this.state.compileJSON = (source: SourceWithTarget) => { - const missingInputs: string[] = [] - const missingInputsCallback = (path: string) => { - missingInputs.push(path) - return { error: 'Deferred import' } - } - let result: CompilationResult = {} - let input = "" - try { - if (source && source.sources) { - const { optimize, runs, evmVersion, language, useFileConfiguration, configFileContent } = this.state - - if (useFileConfiguration) { - input = compilerInputForConfigFile(source.sources, JSON.parse(configFileContent)) - } else { - input = compilerInput(source.sources, { optimize, runs, evmVersion, language }) - } - - result = JSON.parse(compiler.compile(input, { import: missingInputsCallback })) - } - } catch (exception) { - result = { error: { formattedMessage: 'Uncaught JavaScript exception:\n' + exception, severity: 'error', mode: 'panic' } } - } - this.onCompilationFinished(result, missingInputs, source, input, this.state.currentVersion) + this.state.compileJSON = (source: SourceWithTarget) => { + const missingInputs: string[] = [] + const missingInputsCallback = (path: string) => { + missingInputs.push(path) + return { error: 'Deferred import' } + } + let result: CompilationResult = {} + let input = "" + try { + if (source && source.sources) { + const { optimize, runs, evmVersion, language, useFileConfiguration, configFileContent } = this.state + + if (useFileConfiguration) { + input = compilerInputForConfigFile(source.sources, JSON.parse(configFileContent)) + } else { + input = compilerInput(source.sources, { optimize, runs, evmVersion, language }) } - this.onCompilerLoaded(compiler.version(), compiler.license()) + + result = JSON.parse(compiler.compile(input, { import: missingInputsCallback })) + } + } catch (exception) { + result = { error: { formattedMessage: 'Uncaught JavaScript exception:\n' + exception, severity: 'error', mode: 'panic' } } } + this.onCompilationFinished(result, missingInputs, source, input, this.state.currentVersion) + } + this.onCompilerLoaded(compiler.version(), compiler.license()) } + } - /** + /** * @dev Called when compilation is finished * @param data compilation result data * @param missingInputs missing imports * @param source Source */ - onCompilationFinished(data: CompilationResult, missingInputs?: string[], source?: SourceWithTarget, input?: string, version?: string): void { - let noFatalErrors = true // ie warnings are ok + onCompilationFinished(data: CompilationResult, missingInputs?: string[], source?: SourceWithTarget, input?: string, version?: string): void { + let noFatalErrors = true // ie warnings are ok - const checkIfFatalError = (error: CompilationError) => { - // Ignore warnings and the 'Deferred import' error as those are generated by us as a workaround - const isValidError = (error.message && error.message.includes('Deferred import')) ? false : error.severity !== 'warning' - if (isValidError) noFatalErrors = false - } - if (data.error) checkIfFatalError(data.error) - if (data.errors) data.errors.forEach((err) => checkIfFatalError(err)) - if (!noFatalErrors) { - // There are fatal errors, abort here - this.state.lastCompilationResult = null - this.event.trigger('compilationFinished', [false, data, source, input, version]) - } else if (missingInputs !== undefined && missingInputs.length > 0 && source && source.sources) { - // try compiling again with the new set of inputs - this.internalCompile(source.sources, missingInputs) - } else { - data = this.updateInterface(data) - if (source) { - source.target = this.state.target - this.state.lastCompilationResult = { - data: data, - source: source - } - } - this.event.trigger('compilationFinished', [true, data, source, input, version]) + const checkIfFatalError = (error: CompilationError) => { + // Ignore warnings and the 'Deferred import' error as those are generated by us as a workaround + const isValidError = (error.message && error.message.includes('Deferred import')) ? false : error.severity !== 'warning' + if (isValidError) noFatalErrors = false + } + if (data.error) checkIfFatalError(data.error) + if (data.errors) data.errors.forEach((err) => checkIfFatalError(err)) + if (!noFatalErrors) { + // There are fatal errors, abort here + this.state.lastCompilationResult = null + this.event.trigger('compilationFinished', [false, data, source, input, version]) + } else if (missingInputs !== undefined && missingInputs.length > 0 && source && source.sources) { + // try compiling again with the new set of inputs + this.internalCompile(source.sources, missingInputs) + } else { + data = this.updateInterface(data) + if (source) { + source.target = this.state.target + this.state.lastCompilationResult = { + data: data, + source: source } + } + this.event.trigger('compilationFinished', [true, data, source, input, version]) } + } - /** + /** * @dev Load compiler using given version (used by remix-tests CLI) * @param version compiler version */ - loadRemoteVersion(version: string): void { - console.log(`Loading remote solc version ${version} ...`) + loadRemoteVersion(version: string): void { + console.log(`Loading remote solc version ${version} ...`) const compiler: any = require('solc') // eslint-disable-line - compiler.loadRemoteVersion(version, (err, remoteCompiler) => { - if (err) { - console.error('Error in loading remote solc compiler: ', err) - } else { - let license - this.state.compileJSON = (source: SourceWithTarget) => { - const missingInputs: string[] = [] - const missingInputsCallback = (path: string) => { - missingInputs.push(path) - return { error: 'Deferred import' } - } - let result: CompilationResult = {} - let input = "" - try { - if (source && source.sources) { - const { optimize, runs, evmVersion, language, useFileConfiguration, configFileContent } = this.state - - if (useFileConfiguration) { - input = compilerInputForConfigFile(source.sources, JSON.parse(configFileContent)) - } else { - input = compilerInput(source.sources, { optimize, runs, evmVersion, language }) - } - - result = JSON.parse(remoteCompiler.compile(input, { import: missingInputsCallback })) - license = remoteCompiler.license() - } - } catch (exception) { - result = { error: { formattedMessage: 'Uncaught JavaScript exception:\n' + exception, severity: 'error', mode: 'panic' } } - } - this.onCompilationFinished(result, missingInputs, source, input, version) - } - this.onCompilerLoaded(version, license) + compiler.loadRemoteVersion(version, (err, remoteCompiler) => { + if (err) { + console.error('Error in loading remote solc compiler: ', err) + } else { + let license + this.state.compileJSON = (source: SourceWithTarget) => { + const missingInputs: string[] = [] + const missingInputsCallback = (path: string) => { + missingInputs.push(path) + return { error: 'Deferred import' } + } + let result: CompilationResult = {} + let input = "" + try { + if (source && source.sources) { + const { optimize, runs, evmVersion, language, useFileConfiguration, configFileContent } = this.state + + if (useFileConfiguration) { + input = compilerInputForConfigFile(source.sources, JSON.parse(configFileContent)) + } else { + input = compilerInput(source.sources, { optimize, runs, evmVersion, language }) + } + + result = JSON.parse(remoteCompiler.compile(input, { import: missingInputsCallback })) + license = remoteCompiler.license() } - }) - } + } catch (exception) { + result = { error: { formattedMessage: 'Uncaught JavaScript exception:\n' + exception, severity: 'error', mode: 'panic' } } + } + this.onCompilationFinished(result, missingInputs, source, input, version) + } + this.onCompilerLoaded(version, license) + } + }) + } - /** + /** * @dev Load compiler using given URL (used by IDE) * @param usingWorker if true, load compiler using worker * @param url URL to load compiler from */ - loadVersion(usingWorker: boolean, url: string): void { - console.log('Loading ' + url + ' ' + (usingWorker ? 'with worker' : 'without worker')) - this.event.trigger('loadingCompiler', [url, usingWorker]) - if (this.state.worker) { - this.state.worker.terminate() - this.state.worker = null - } - if (usingWorker) { - this.loadWorkerHandler().then(() => { - this.loadWorker(url) - }) - } else { - this.loadInternal(url) - } + loadVersion(usingWorker: boolean, url: string): void { + console.log('Loading ' + url + ' ' + (usingWorker ? 'with worker' : 'without worker')) + this.event.trigger('loadingCompiler', [url, usingWorker]) + if (this.state.worker) { + this.state.worker.terminate() + this.state.worker = null } + if (usingWorker) { + this.loadWorkerHandler().then(() => { + this.loadWorker(url) + }) + } else { + this.loadInternal(url) + } + } - /** + /** * @dev Load compiler using 'script' element (without worker) * @param url URL to load compiler from */ - loadInternal(url: string): void { - delete window['Module'] - // NOTE: workaround some browsers? - window['Module'] = undefined - // Set a safe fallback until the new one is loaded - this.state.compileJSON = (source: SourceWithTarget) => { - this.onCompilationFinished({ error: { formattedMessage: 'Compiler not yet loaded.' } }) - } - const newScript: HTMLScriptElement = document.createElement('script') - newScript.type = 'text/javascript' - newScript.src = url - document.getElementsByTagName('head')[0].appendChild(newScript) - const check: number = window.setInterval(() => { - if (!window['Module']) { - return - } - window.clearInterval(check) - this.onInternalCompilerLoaded() - }, 200) + loadInternal(url: string): void { + delete window['Module'] + // NOTE: workaround some browsers? + window['Module'] = undefined + // Set a safe fallback until the new one is loaded + this.state.compileJSON = (source: SourceWithTarget) => { + this.onCompilationFinished({ error: { formattedMessage: 'Compiler not yet loaded.' } }) } - - /** + const newScript: HTMLScriptElement = document.createElement('script') + newScript.type = 'text/javascript' + newScript.src = url + document.getElementsByTagName('head')[0].appendChild(newScript) + const check: number = window.setInterval(() => { + if (!window['Module']) { + return + } + window.clearInterval(check) + this.onInternalCompilerLoaded() + }, 200) + } + + /** * @dev Load compiler using web worker * @param url URL to load compiler from */ - loadWorker(url: string): void { - - this.state.worker = this.workerHandler.getWorker() - const jobs: Record<'sources', SourceWithTarget>[] = [] - - this.state.worker.addEventListener('message', (msg: Record<'data', MessageFromWorker>) => { - const data: MessageFromWorker = msg.data - if (this.state.compilerRetriggerMode == CompilerRetriggerMode.retrigger && data.timestamp !== this.state.compilationStartTime) { - return - } - switch (data.cmd) { - case 'versionLoaded': - if (data.data) this.onCompilerLoaded(data.data, data.license) - break - case 'compiled': - { - let result: CompilationResult - if (data.data && data.job !== undefined && data.job >= 0) { - try { - result = JSON.parse(data.data) - } catch (exception) { - result = { error: { formattedMessage: 'Invalid JSON output from the compiler: ' + exception } } - } - let sources: SourceWithTarget = {} - if (data.job in jobs !== undefined) { - sources = jobs[data.job].sources - delete jobs[data.job] - } - this.onCompilationFinished(result, data.missingInputs, sources, data.input, this.state.currentVersion) - } - break - } - } - - }) - - this.state.worker.addEventListener('error', (msg: Record<'data', MessageFromWorker>) => { - const formattedMessage = `Worker error: ${msg.data && msg.data !== undefined ? msg.data : msg['message']}` - this.onCompilationFinished({ error: { formattedMessage } }) - }) - - this.state.compileJSON = (source: SourceWithTarget) => { - if (source && source.sources) { - const { optimize, runs, evmVersion, language, useFileConfiguration, configFileContent } = this.state - jobs.push({ sources: source }) - let input = "" - - try { - if (useFileConfiguration) { - input = compilerInputForConfigFile(source.sources, JSON.parse(configFileContent)) - } else { - input = compilerInput(source.sources, { optimize, runs, evmVersion, language }) - } - } catch (exception) { - this.onCompilationFinished({ error: { formattedMessage: exception.message } }, [], source, "", this.state.currentVersion) - return - } - - - this.state.worker.postMessage({ - cmd: 'compile', - job: jobs.length - 1, - input: input, - timestamp: this.state.compilationStartTime - }) - } + loadWorker(url: string): void { + + this.state.worker = this.workerHandler.getWorker() + const jobs: Record<'sources', SourceWithTarget>[] = [] + + this.state.worker.addEventListener('message', (msg: Record<'data', MessageFromWorker>) => { + const data: MessageFromWorker = msg.data + if (this.state.compilerRetriggerMode == CompilerRetriggerMode.retrigger && data.timestamp !== this.state.compilationStartTime) { + return + } + switch (data.cmd) { + case 'versionLoaded': + if (data.data) this.onCompilerLoaded(data.data, data.license) + break + case 'compiled': + { + let result: CompilationResult + if (data.data && data.job !== undefined && data.job >= 0) { + try { + result = JSON.parse(data.data) + } catch (exception) { + result = { error: { formattedMessage: 'Invalid JSON output from the compiler: ' + exception } } + } + let sources: SourceWithTarget = {} + if (data.job in jobs !== undefined) { + sources = jobs[data.job].sources + delete jobs[data.job] + } + this.onCompilationFinished(result, data.missingInputs, sources, data.input, this.state.currentVersion) + } + break + } + } + + }) + + this.state.worker.addEventListener('error', (msg: Record<'data', MessageFromWorker>) => { + const formattedMessage = `Worker error: ${msg.data && msg.data !== undefined ? msg.data : msg['message']}` + this.onCompilationFinished({ error: { formattedMessage } }) + }) + + this.state.compileJSON = (source: SourceWithTarget) => { + if (source && source.sources) { + const { optimize, runs, evmVersion, language, useFileConfiguration, configFileContent } = this.state + jobs.push({ sources: source }) + let input = "" + + try { + if (useFileConfiguration) { + input = compilerInputForConfigFile(source.sources, JSON.parse(configFileContent)) + } else { + input = compilerInput(source.sources, { optimize, runs, evmVersion, language }) + } + } catch (exception) { + this.onCompilationFinished({ error: { formattedMessage: exception.message } }, [], source, "", this.state.currentVersion) + return } + this.state.worker.postMessage({ - cmd: 'loadVersion', - data: url + cmd: 'compile', + job: jobs.length - 1, + input: input, + timestamp: this.state.compilationStartTime }) + } } - /** + this.state.worker.postMessage({ + cmd: 'loadVersion', + data: url + }) + } + + /** * @dev Gather imports for compilation * @param files file sources * @param importHints import file list * @param cb callback */ - gatherImports(files: Source, importHints?: string[], cb?: gatherImportsCallbackInterface): void { - importHints = importHints || [] - // FIXME: This will only match imports if the file begins with one '.' - // It should tokenize by lines and check each. - const importRegex = /^\s*import\s*['"]([^'"]+)['"];/g - for (const fileName in files) { - let match: RegExpExecArray | null - while ((match = importRegex.exec(files[fileName].content))) { - let importFilePath = match[1] - if (importFilePath.startsWith('./')) { - const path: RegExpExecArray | null = /(.*\/).*/.exec(fileName) - importFilePath = path ? importFilePath.replace('./', path[1]) : importFilePath.slice(2) - } - if (!importHints.includes(importFilePath)) importHints.push(importFilePath) - } - } - while (importHints.length > 0) { - const m: string = importHints.pop() as string - if (m && m in files) continue - - if (this.handleImportCall) { - this.handleImportCall(m, (err, content: string) => { - if (err && cb) cb(err) - else { - files[m] = { content } - this.gatherImports(files, importHints, cb) - } - }) - } - return + gatherImports(files: Source, importHints?: string[], cb?: gatherImportsCallbackInterface): void { + importHints = importHints || [] + // FIXME: This will only match imports if the file begins with one '.' + // It should tokenize by lines and check each. + const importRegex = /^\s*import\s*['"]([^'"]+)['"];/g + for (const fileName in files) { + let match: RegExpExecArray | null + while ((match = importRegex.exec(files[fileName].content))) { + let importFilePath = match[1] + if (importFilePath.startsWith('./')) { + const path: RegExpExecArray | null = /(.*\/).*/.exec(fileName) + importFilePath = path ? importFilePath.replace('./', path[1]) : importFilePath.slice(2) } - if (cb) { cb(null, { sources: files }) } + if (!importHints.includes(importFilePath)) importHints.push(importFilePath) + } + } + while (importHints.length > 0) { + const m: string = importHints.pop() as string + if (m && m in files) continue + + if (this.handleImportCall) { + this.handleImportCall(m, (err, content: string) => { + if (err && cb) cb(err) + else { + files[m] = { content } + this.gatherImports(files, importHints, cb) + } + }) + } + return } + if (cb) { cb(null, { sources: files }) } + } - /** + /** * @dev Truncate version string * @param version version */ - truncateVersion(version: string): string { - const tmp: RegExpExecArray | null = /^(\d+.\d+.\d+)/.exec(version) - return tmp ? tmp[1] : version - } + truncateVersion(version: string): string { + const tmp: RegExpExecArray | null = /^(\d+.\d+.\d+)/.exec(version) + return tmp ? tmp[1] : version + } - /** + /** * @dev Update ABI according to current compiler version * @param data Compilation result */ - updateInterface(data: CompilationResult): CompilationResult { - txHelper.visitContracts(data.contracts, (contract: visitContractsCallbackParam) => { - if (!contract.object.abi) contract.object.abi = [] - if (this.state.language === 'Yul' && contract.object.abi.length === 0) { - // yul compiler does not return any abi, - // we default to accept the fallback function (which expect raw data as argument). - contract.object.abi.push({ - payable: true, - stateMutability: 'payable', - type: 'fallback' - }) - } - if (data && data.contracts && this.state.currentVersion) { - const version = this.truncateVersion(this.state.currentVersion) - data.contracts[contract.file][contract.name].abi = update(version, contract.object.abi) - // if "constant" , payable must not be true and stateMutability must be view. - // see https://github.com/ethereum/solc-js/issues/500 - for (const item of data.contracts[contract.file][contract.name].abi) { - if (isFunctionDescription(item) && item.constant) { - item.payable = false - item.stateMutability = 'view' - } - } - } + updateInterface(data: CompilationResult): CompilationResult { + txHelper.visitContracts(data.contracts, (contract: visitContractsCallbackParam) => { + if (!contract.object.abi) contract.object.abi = [] + if (this.state.language === 'Yul' && contract.object.abi.length === 0) { + // yul compiler does not return any abi, + // we default to accept the fallback function (which expect raw data as argument). + contract.object.abi.push({ + payable: true, + stateMutability: 'payable', + type: 'fallback' }) - return data - } + } + if (data && data.contracts && this.state.currentVersion) { + const version = this.truncateVersion(this.state.currentVersion) + data.contracts[contract.file][contract.name].abi = update(version, contract.object.abi) + // if "constant" , payable must not be true and stateMutability must be view. + // see https://github.com/ethereum/solc-js/issues/500 + for (const item of data.contracts[contract.file][contract.name].abi) { + if (isFunctionDescription(item) && item.constant) { + item.payable = false + item.stateMutability = 'view' + } + } + } + }) + return data + } - /** + /** * @dev Get contract obj of the given contract name from last compilation result. * @param name contract name */ - getContract(name: string): Record | null { - if (this.state.lastCompilationResult && this.state.lastCompilationResult.data && this.state.lastCompilationResult.data.contracts) { - return txHelper.getContract(name, this.state.lastCompilationResult.data.contracts) - } - return null + getContract(name: string): Record | null { + if (this.state.lastCompilationResult && this.state.lastCompilationResult.data && this.state.lastCompilationResult.data.contracts) { + return txHelper.getContract(name, this.state.lastCompilationResult.data.contracts) } + return null + } - /** + /** * @dev Call the given callback for all the contracts from last compilation result * @param cb callback */ - visitContracts(cb: visitContractsCallbackInterface): void | null { - if (this.state.lastCompilationResult && this.state.lastCompilationResult.data && this.state.lastCompilationResult.data.contracts) { - return txHelper.visitContracts(this.state.lastCompilationResult.data.contracts, cb) - } - return null + visitContracts(cb: visitContractsCallbackInterface): void | null { + if (this.state.lastCompilationResult && this.state.lastCompilationResult.data && this.state.lastCompilationResult.data.contracts) { + return txHelper.visitContracts(this.state.lastCompilationResult.data.contracts, cb) } + return null + } - /** + /** * @dev Get the compiled contracts data from last compilation result */ - getContracts(): CompilationResult['contracts'] | null { - if (this.state.lastCompilationResult && this.state.lastCompilationResult.data && this.state.lastCompilationResult.data.contracts) { - return this.state.lastCompilationResult.data.contracts - } - return null + getContracts(): CompilationResult['contracts'] | null { + if (this.state.lastCompilationResult && this.state.lastCompilationResult.data && this.state.lastCompilationResult.data.contracts) { + return this.state.lastCompilationResult.data.contracts } + return null + } - /** + /** * @dev Get sources from last compilation result */ - getSources(): Source | null | undefined { - if (this.state.lastCompilationResult && this.state.lastCompilationResult.source) { - return this.state.lastCompilationResult.source.sources - } - return null + getSources(): Source | null | undefined { + if (this.state.lastCompilationResult && this.state.lastCompilationResult.source) { + return this.state.lastCompilationResult.source.sources } + return null + } - /** + /** * @dev Get sources of passed file name from last compilation result * @param fileName file name */ - getSource(fileName: string): Source['filename'] | null { - if (this.state.lastCompilationResult && this.state.lastCompilationResult.source && this.state.lastCompilationResult.source.sources) { - return this.state.lastCompilationResult.source.sources[fileName] - } - return null + getSource(fileName: string): Source['filename'] | null { + if (this.state.lastCompilationResult && this.state.lastCompilationResult.source && this.state.lastCompilationResult.source.sources) { + return this.state.lastCompilationResult.source.sources[fileName] } + return null + } - /** + /** * @dev Get source name at passed index from last compilation result * @param index - index of the source */ - getSourceName(index: number): string | null { - if (this.state.lastCompilationResult && this.state.lastCompilationResult.data && this.state.lastCompilationResult.data.sources) { - return Object.keys(this.state.lastCompilationResult.data.sources)[index] - } - return null + getSourceName(index: number): string | null { + if (this.state.lastCompilationResult && this.state.lastCompilationResult.data && this.state.lastCompilationResult.data.sources) { + return Object.keys(this.state.lastCompilationResult.data.sources)[index] } + return null + } } diff --git a/libs/remix-solidity/src/compiler/helper.ts b/libs/remix-solidity/src/compiler/helper.ts index f4eb8e3f21..eceffebde4 100644 --- a/libs/remix-solidity/src/compiler/helper.ts +++ b/libs/remix-solidity/src/compiler/helper.ts @@ -3,59 +3,59 @@ import { CompilationResult, visitContractsCallbackParam, visitContractsCallbackInterface } from './types' export default { - /** + /** * @dev Get contract obj of given contract name from last compilation result. * @param name contract name * @param contracts 'contracts' object from last compilation result */ - getContract: (contractName: string, contracts: CompilationResult['contracts']) : Record | null => { - for (const file in contracts) { - if (contracts[file][contractName]) { - return { object: contracts[file][contractName], file: file } - } - } - return null - }, + getContract: (contractName: string, contracts: CompilationResult['contracts']) : Record | null => { + for (const file in contracts) { + if (contracts[file][contractName]) { + return { object: contracts[file][contractName], file: file } + } + } + return null + }, - /** + /** * @dev call the given callback for all contracts from last compilation result, stop visiting when cb return true * @param contracts - 'contracts' object from last compilation result * @param cb - callback */ - visitContracts: (contracts: CompilationResult['contracts'], cb: visitContractsCallbackInterface) : void => { - for (const file in contracts) { - for (const name in contracts[file]) { - const param: visitContractsCallbackParam = { - name: name, - object: contracts[file][name], - file: file - } - if (cb(param)) return - } + visitContracts: (contracts: CompilationResult['contracts'], cb: visitContractsCallbackInterface) : void => { + for (const file in contracts) { + for (const name in contracts[file]) { + const param: visitContractsCallbackParam = { + name: name, + object: contracts[file][name], + file: file } - }, - - // ^ e.g: - // browser/gm.sol: Warning: Source file does not specify required compiler version! Consider adding "pragma solidity ^0.6.12 - // https://github.com/OpenZeppelin/openzeppelin-contracts/blob/release-v3.2.0/contracts/introspection/IERC1820Registry.sol:3:1: ParserError: Source file requires different compiler version (current compiler is 0.7.4+commit.3f05b770.Emscripten.clang) - note that nightly builds are considered to be strictly less than the released version - getPositionDetails: (msg: string) => { - const result = { } as Record - - // To handle some compiler warning without location like SPDX license warning etc - if (!msg.includes(':')) return { errLine: -1, errCol: -1, errFile: '' } - - if (msg.includes('-->')) msg = msg.split('-->')[1].trim() - - // extract line / column - let pos = msg.match(/^(.*?):([0-9]*?):([0-9]*?)?/) - result.errLine = pos ? parseInt(pos[2]) - 1 : -1 - result.errCol = pos ? parseInt(pos[3]) : -1 - - // extract file - pos = msg.match(/^(https:.*?|http:.*?|.*?):/) - result.errFile = pos ? pos[1] : msg - return result + if (cb(param)) return + } } + }, + + // ^ e.g: + // browser/gm.sol: Warning: Source file does not specify required compiler version! Consider adding "pragma solidity ^0.6.12 + // https://github.com/OpenZeppelin/openzeppelin-contracts/blob/release-v3.2.0/contracts/introspection/IERC1820Registry.sol:3:1: ParserError: Source file requires different compiler version (current compiler is 0.7.4+commit.3f05b770.Emscripten.clang) - note that nightly builds are considered to be strictly less than the released version + getPositionDetails: (msg: string) => { + const result = { } as Record + + // To handle some compiler warning without location like SPDX license warning etc + if (!msg.includes(':')) return { errLine: -1, errCol: -1, errFile: '' } + + if (msg.includes('-->')) msg = msg.split('-->')[1].trim() + + // extract line / column + let pos = msg.match(/^(.*?):([0-9]*?):([0-9]*?)?/) + result.errLine = pos ? parseInt(pos[2]) - 1 : -1 + result.errCol = pos ? parseInt(pos[3]) : -1 + + // extract file + pos = msg.match(/^(https:.*?|http:.*?|.*?):/) + result.errFile = pos ? pos[1] : msg + return result + } } diff --git a/libs/remix-solidity/src/compiler/types.ts b/libs/remix-solidity/src/compiler/types.ts index fd1158e7f0..e04cb17241 100644 --- a/libs/remix-solidity/src/compiler/types.ts +++ b/libs/remix-solidity/src/compiler/types.ts @@ -379,10 +379,10 @@ export interface CompiledContract { export type ABIDescription = FunctionDescription | EventDescription export const isFunctionDescription = (item: ABIDescription): item is FunctionDescription => - (item as FunctionDescription).stateMutability !== undefined + (item as FunctionDescription).stateMutability !== undefined export const isEventDescription = (item: ABIDescription): item is EventDescription => - (item as EventDescription).type === 'event' + (item as EventDescription).type === 'event' export interface FunctionDescription { /** Type of the method. default is 'function' */ diff --git a/libs/remix-solidity/src/lib/es-web-worker/compiler-worker.ts b/libs/remix-solidity/src/lib/es-web-worker/compiler-worker.ts index 60852272fb..a2875b4c76 100644 --- a/libs/remix-solidity/src/lib/es-web-worker/compiler-worker.ts +++ b/libs/remix-solidity/src/lib/es-web-worker/compiler-worker.ts @@ -4,44 +4,44 @@ let compileJSON: ((input: CompilerInput) => string) | null = (input) => { return const missingInputs: string[] = [] self.onmessage = (e: MessageEvent) => { - const data: MessageToWorker = e.data - switch (data.cmd) { - case 'loadVersion': - { - (self as any).importScripts(data.data) - const compiler = setupMethods(self) - compileJSON = (input) => { - try { - const missingInputsCallback = (path) => { - missingInputs.push(path) - return { error: 'Deferred import' } - } - return compiler.compile(input, { import: missingInputsCallback }) - } catch (exception) { - return JSON.stringify({ error: 'Uncaught JavaScript exception:\n' + exception }) - } + const data: MessageToWorker = e.data + switch (data.cmd) { + case 'loadVersion': + { + (self as any).importScripts(data.data) + const compiler = setupMethods(self) + compileJSON = (input) => { + try { + const missingInputsCallback = (path) => { + missingInputs.push(path) + return { error: 'Deferred import' } } - self.postMessage({ - cmd: 'versionLoaded', - data: compiler.version(), - license: compiler.license() - }) - break + return compiler.compile(input, { import: missingInputsCallback }) + } catch (exception) { + return JSON.stringify({ error: 'Uncaught JavaScript exception:\n' + exception }) + } } + self.postMessage({ + cmd: 'versionLoaded', + data: compiler.version(), + license: compiler.license() + }) + break + } - case 'compile': - missingInputs.length = 0 - if (data.input && compileJSON) { - self.postMessage({ - cmd: 'compiled', - job: data.job, - timestamp: data.timestamp, - data: compileJSON(data.input), - input: data.input, - missingInputs: missingInputs - }) - } - break + case 'compile': + missingInputs.length = 0 + if (data.input && compileJSON) { + self.postMessage({ + cmd: 'compiled', + job: data.job, + timestamp: data.timestamp, + data: compileJSON(data.input), + input: data.input, + missingInputs: missingInputs + }) } + break + } } diff --git a/libs/remix-solidity/src/lib/es-web-worker/es-web-worker-handler.ts b/libs/remix-solidity/src/lib/es-web-worker/es-web-worker-handler.ts index 03ba81cb3d..a46787bec0 100644 --- a/libs/remix-solidity/src/lib/es-web-worker/es-web-worker-handler.ts +++ b/libs/remix-solidity/src/lib/es-web-worker/es-web-worker-handler.ts @@ -1,12 +1,12 @@ class ESWebWorkerHandler { - constructor() { + constructor() { - } + } - getWorker () { - // @ts-ignore - return new Worker(new URL('./compiler-worker', import.meta.url)) - } + getWorker () { + // @ts-ignore + return new Worker(new URL('./compiler-worker', import.meta.url)) + } } export default ESWebWorkerHandler \ No newline at end of file diff --git a/libs/remix-solidity/src/lib/eventManager.ts b/libs/remix-solidity/src/lib/eventManager.ts index c8c1002430..15765f80fd 100644 --- a/libs/remix-solidity/src/lib/eventManager.ts +++ b/libs/remix-solidity/src/lib/eventManager.ts @@ -4,7 +4,7 @@ export default class EventManager { registered: any = {} // eslint-disable-line anonymous: any = {} // eslint-disable-line - /* + /* * Unregister a listener. * Note that if obj is a function. the unregistration will be applied to the dummy obj {}. * @@ -13,21 +13,21 @@ export default class EventManager { * @param {Func} func - function of the listeners that will be executed */ unregister (eventName: any, obj: any, func: any): void { // eslint-disable-line - if (!this.registered[eventName]) { - return - } - if (obj instanceof Function) { - func = obj - obj = this.anonymous - } - for (const reg in this.registered[eventName]) { - if (this.registered[eventName][reg].obj === obj && this.registered[eventName][reg].func.toString() === func.toString()) { - this.registered[eventName].splice(reg, 1) - } - } + if (!this.registered[eventName]) { + return } + if (obj instanceof Function) { + func = obj + obj = this.anonymous + } + for (const reg in this.registered[eventName]) { + if (this.registered[eventName][reg].obj === obj && this.registered[eventName][reg].func.toString() === func.toString()) { + this.registered[eventName].splice(reg, 1) + } + } + } - /* + /* * Register a new listener. * Note that if obj is a function, the function registration will be associated with the dummy object {} * @@ -36,20 +36,20 @@ export default class EventManager { * @param {Func} func - function of the listeners that will be executed */ register (eventName: any, obj: any, func: any): void { // eslint-disable-line - if (!this.registered[eventName]) { - this.registered[eventName] = [] - } - if (obj instanceof Function) { - func = obj - obj = this.anonymous - } - this.registered[eventName].push({ - obj: obj, - func: func - }) + if (!this.registered[eventName]) { + this.registered[eventName] = [] } + if (obj instanceof Function) { + func = obj + obj = this.anonymous + } + this.registered[eventName].push({ + obj: obj, + func: func + }) + } - /* + /* * trigger event. * Every listener have their associated function executed * @@ -57,12 +57,12 @@ export default class EventManager { * @param {Array}j - argument that will be passed to the executed function. */ trigger (eventName: any, args: any): void { // eslint-disable-line - if (!this.registered[eventName]) { - return - } - for (const listener in this.registered[eventName]) { - const l = this.registered[eventName][listener] - if (l.func) l.func.apply(l.obj === this.anonymous ? {} : l.obj, args) - } + if (!this.registered[eventName]) { + return + } + for (const listener in this.registered[eventName]) { + const l = this.registered[eventName][listener] + if (l.func) l.func.apply(l.obj === this.anonymous ? {} : l.obj, args) } + } } diff --git a/libs/remix-solidity/tests/compiler-input.spec.ts b/libs/remix-solidity/tests/compiler-input.spec.ts index 3dc247624b..466f53a968 100644 --- a/libs/remix-solidity/tests/compiler-input.spec.ts +++ b/libs/remix-solidity/tests/compiler-input.spec.ts @@ -2,24 +2,24 @@ import { getValidLanguage } from '../src/compiler/compiler-input' import { Language } from '../src/compiler/types' describe('compiler-input', () => { - test('getValidLanguage', () => { - const correctYul: Language = 'Yul' - const correctSolidity: Language = 'Solidity' + test('getValidLanguage', () => { + const correctYul: Language = 'Yul' + const correctSolidity: Language = 'Solidity' - const yulUpperCase = 'Yul' - const yulLowerCase = 'yul' + const yulUpperCase = 'Yul' + const yulLowerCase = 'yul' - const solidityUpperCase = 'Solidity' - const solidityLowerCase = 'solidity' + const solidityUpperCase = 'Solidity' + const solidityLowerCase = 'solidity' - expect(getValidLanguage(yulLowerCase)).toBe(correctYul) - expect(getValidLanguage(yulUpperCase)).toBe(correctYul) - expect(getValidLanguage(solidityUpperCase)).toBe(correctSolidity) - expect(getValidLanguage(solidityLowerCase)).toBe(correctSolidity) - expect(getValidLanguage(null)).toBe(null) - expect(getValidLanguage(undefined)).toBe(null) - expect(getValidLanguage('')).toBe(null) - expect(getValidLanguage('A')).toBe(null) - expect(getValidLanguage('Something')).toBe(null) - }) + expect(getValidLanguage(yulLowerCase)).toBe(correctYul) + expect(getValidLanguage(yulUpperCase)).toBe(correctYul) + expect(getValidLanguage(solidityUpperCase)).toBe(correctSolidity) + expect(getValidLanguage(solidityLowerCase)).toBe(correctSolidity) + expect(getValidLanguage(null)).toBe(null) + expect(getValidLanguage(undefined)).toBe(null) + expect(getValidLanguage('')).toBe(null) + expect(getValidLanguage('A')).toBe(null) + expect(getValidLanguage('Something')).toBe(null) + }) }) diff --git a/libs/remix-tests/src/assertionEvents.ts b/libs/remix-tests/src/assertionEvents.ts index f0d0f19428..95d08f2e0a 100644 --- a/libs/remix-tests/src/assertionEvents.ts +++ b/libs/remix-tests/src/assertionEvents.ts @@ -1,40 +1,40 @@ const assertionEvents = [ - { - name: 'AssertionEvent', - params: ['bool', 'string', 'string'] - }, - { - name: 'AssertionEventUint', - params: ['bool', 'string', 'string', 'uint256', 'uint256'] - }, - { - name: 'AssertionEventInt', - params: ['bool', 'string', 'string', 'int256', 'int256'] - }, - { - name: 'AssertionEventBool', - params: ['bool', 'string', 'string', 'bool', 'bool'] - }, - { - name: 'AssertionEventAddress', - params: ['bool', 'string', 'string', 'address', 'address'] - }, - { - name: 'AssertionEventBytes32', - params: ['bool', 'string', 'string', 'bytes32', 'bytes32'] - }, - { - name: 'AssertionEventString', - params: ['bool', 'string', 'string', 'string', 'string'] - }, - { - name: 'AssertionEventUintInt', - params: ['bool', 'string', 'string', 'uint256', 'int256'] - }, - { - name: 'AssertionEventIntUint', - params: ['bool', 'string', 'string', 'int256', 'uint256'] - } + { + name: 'AssertionEvent', + params: ['bool', 'string', 'string'] + }, + { + name: 'AssertionEventUint', + params: ['bool', 'string', 'string', 'uint256', 'uint256'] + }, + { + name: 'AssertionEventInt', + params: ['bool', 'string', 'string', 'int256', 'int256'] + }, + { + name: 'AssertionEventBool', + params: ['bool', 'string', 'string', 'bool', 'bool'] + }, + { + name: 'AssertionEventAddress', + params: ['bool', 'string', 'string', 'address', 'address'] + }, + { + name: 'AssertionEventBytes32', + params: ['bool', 'string', 'string', 'bytes32', 'bytes32'] + }, + { + name: 'AssertionEventString', + params: ['bool', 'string', 'string', 'string', 'string'] + }, + { + name: 'AssertionEventUintInt', + params: ['bool', 'string', 'string', 'uint256', 'int256'] + }, + { + name: 'AssertionEventIntUint', + params: ['bool', 'string', 'string', 'int256', 'uint256'] + } ] export default assertionEvents diff --git a/libs/remix-tests/src/compiler.ts b/libs/remix-tests/src/compiler.ts index 591609c5a8..ea49dadb9d 100644 --- a/libs/remix-tests/src/compiler.ts +++ b/libs/remix-tests/src/compiler.ts @@ -10,23 +10,23 @@ const logger = new Log() const log = logger.logger function regexIndexOf (inputString: string, regex: RegExp, startpos = 0) { - const indexOf = inputString.substring(startpos).search(regex) - return (indexOf >= 0) ? (indexOf + (startpos)) : indexOf + const indexOf = inputString.substring(startpos).search(regex) + return (indexOf >= 0) ? (indexOf + (startpos)) : indexOf } export function writeTestAccountsContract (accounts: string[]) { const testAccountContract = require('../sol/tests_accounts.sol') // eslint-disable-line - let body = '' - if (accounts.length) { - body = `address[${accounts.length}] memory accounts;` - accounts.map((address, index) => { - body += `\n\t\taccounts[${index}] = ${address};\n` - }) - body += `return accounts[index];` - } else { - body = `return address(0);` - } - return testAccountContract.replace('>accounts<', body) + let body = '' + if (accounts.length) { + body = `address[${accounts.length}] memory accounts;` + accounts.map((address, index) => { + body += `\n\t\taccounts[${index}] = ${address};\n` + }) + body += `return accounts[index];` + } else { + body = `return address(0);` + } + return testAccountContract.replace('>accounts<', body) } /** @@ -35,7 +35,7 @@ export function writeTestAccountsContract (accounts: string[]) { */ function isRemixTestFile (path: string) { - return ['tests.sol', 'remix_tests.sol', 'remix_accounts.sol'].some(name => path.includes(name)) + return ['tests.sol', 'remix_tests.sol', 'remix_accounts.sol'].some(name => path.includes(name)) } /** @@ -50,21 +50,21 @@ function isRemixTestFile (path: string) { */ function processFile (filePath: string, sources: SrcIfc, isRoot = false) { - const importRegEx = /import ['"](.+?)['"];/g - const isFileAlreadyInSources: boolean = Object.keys(sources).includes(filePath) + const importRegEx = /import ['"](.+?)['"];/g + const isFileAlreadyInSources: boolean = Object.keys(sources).includes(filePath) - // Return if file is a remix test file or already processed - if (isRemixTestFile(filePath) || isFileAlreadyInSources) { return } + // Return if file is a remix test file or already processed + if (isRemixTestFile(filePath) || isFileAlreadyInSources) { return } - let content: string = fs.readFileSync(filePath, { encoding: 'utf-8' }) - const testFileImportRegEx = /^(import)\s['"](remix_tests.sol|tests.sol)['"];/gm + let content: string = fs.readFileSync(filePath, { encoding: 'utf-8' }) + const testFileImportRegEx = /^(import)\s['"](remix_tests.sol|tests.sol)['"];/gm - // import 'remix_tests.sol', if file is a root test contract file and doesn't already have it - if (isRoot && filePath.endsWith('_test.sol') && regexIndexOf(content, testFileImportRegEx) < 0) { - const includeTestLibs = '\nimport \'remix_tests.sol\';\n' - content = includeTestLibs.concat(content) - } - sources[filePath] = { content } + // import 'remix_tests.sol', if file is a root test contract file and doesn't already have it + if (isRoot && filePath.endsWith('_test.sol') && regexIndexOf(content, testFileImportRegEx) < 0) { + const includeTestLibs = '\nimport \'remix_tests.sol\';\n' + content = includeTestLibs.concat(content) + } + sources[filePath] = { content } } const userAgent = (typeof (navigator) !== 'undefined') && navigator.userAgent ? navigator.userAgent.toLowerCase() : '-' @@ -81,92 +81,92 @@ const isBrowser = !(typeof (window) === 'undefined' || userAgent.indexOf(' elect */ export function compileFileOrFiles (filename: string, isDirectory: boolean, opts: any, compilerConfig: CompilerConfiguration, cb): void { - let compiler: any - const accounts: string[] = opts.accounts || [] - const sources: SrcIfc = { - 'tests.sol': { content: require('../sol/tests.sol') }, - 'remix_tests.sol': { content: require('../sol/tests.sol') }, - 'remix_accounts.sol': { content: writeTestAccountsContract(accounts) } + let compiler: any + const accounts: string[] = opts.accounts || [] + const sources: SrcIfc = { + 'tests.sol': { content: require('../sol/tests.sol') }, + 'remix_tests.sol': { content: require('../sol/tests.sol') }, + 'remix_accounts.sol': { content: writeTestAccountsContract(accounts) } + } + const filepath: string = (isDirectory ? filename : path.dirname(filename)) + const importsCallback = (url, cb) => { + try { + if(fs.existsSync(url)) cb(null, fs.readFileSync(url, 'utf-8')) + else { + const urlResolver = new RemixURLResolver() + urlResolver.resolve(url).then((result) => cb(null, result.content)).catch((error) => cb(error.message)) + } + } catch (e) { + cb(e.message) } - const filepath: string = (isDirectory ? filename : path.dirname(filename)) - const importsCallback = (url, cb) => { - try { - if(fs.existsSync(url)) cb(null, fs.readFileSync(url, 'utf-8')) - else { - const urlResolver = new RemixURLResolver() - urlResolver.resolve(url).then((result) => cb(null, result.content)).catch((error) => cb(error.message)) - } - } catch (e) { - cb(e.message) + } + try { + if (!isDirectory && fs.existsSync(filename)) { + if (filename.split('.').pop() === 'sol') { + processFile(filename, sources, true) + } else { + throw new Error('Not a solidity file') + } + } else { + // walkSync only if it is a directory + let testFileCount = 0 + fs.walkSync(filepath, (foundpath: string) => { + // only process .sol files + if (foundpath.split('.').pop() === 'sol' && foundpath.endsWith('_test.sol')) { + testFileCount++ + processFile(foundpath, sources, true) } + }) + if (testFileCount > 0) { + log.info(`${testFileCount} Solidity test file${testFileCount === 1 ? '' : 's'} found`) + } else { + log.error('No Solidity test file found. Make sure your test file ends with \'_test.sol\'') + process.exit() + } } - try { - if (!isDirectory && fs.existsSync(filename)) { - if (filename.split('.').pop() === 'sol') { - processFile(filename, sources, true) - } else { - throw new Error('Not a solidity file') - } - } else { - // walkSync only if it is a directory - let testFileCount = 0 - fs.walkSync(filepath, (foundpath: string) => { - // only process .sol files - if (foundpath.split('.').pop() === 'sol' && foundpath.endsWith('_test.sol')) { - testFileCount++ - processFile(foundpath, sources, true) - } + } catch (e) { // eslint-disable-line no-useless-catch + throw e + } finally { + async.waterfall([ + function loadCompiler (next) { + compiler = new RemixCompiler(importsCallback) + if (compilerConfig) { + const { currentCompilerUrl, evmVersion, optimize, runs } = compilerConfig + if (evmVersion) compiler.set('evmVersion', evmVersion) + if (optimize) compiler.set('optimize', optimize) + if (runs) compiler.set('runs', runs) + if (currentCompilerUrl) { + compiler.loadRemoteVersion(currentCompilerUrl) + compiler.event.register('compilerLoaded', this, function (version, license) { + next() }) - if (testFileCount > 0) { - log.info(`${testFileCount} Solidity test file${testFileCount === 1 ? '' : 's'} found`) - } else { - log.error('No Solidity test file found. Make sure your test file ends with \'_test.sol\'') - process.exit() - } + } else { + compiler.onInternalCompilerLoaded() + next() + } + } else { + compiler.onInternalCompilerLoaded() + next() } - } catch (e) { // eslint-disable-line no-useless-catch - throw e - } finally { - async.waterfall([ - function loadCompiler (next) { - compiler = new RemixCompiler(importsCallback) - if (compilerConfig) { - const { currentCompilerUrl, evmVersion, optimize, runs } = compilerConfig - if (evmVersion) compiler.set('evmVersion', evmVersion) - if (optimize) compiler.set('optimize', optimize) - if (runs) compiler.set('runs', runs) - if (currentCompilerUrl) { - compiler.loadRemoteVersion(currentCompilerUrl) - compiler.event.register('compilerLoaded', this, function (version, license) { - next() - }) - } else { - compiler.onInternalCompilerLoaded() - next() - } - } else { - compiler.onInternalCompilerLoaded() - next() - } - }, - function doCompilation (next) { - // @ts-ignore - compiler.event.register('compilationFinished', this, (success, data, source, input, version) => { - next(null, data) - }) - compiler.compile(sources, filepath) - } - ], function (err: Error | null | undefined, result: any) { - const error: Error[] = [] - if (result.error) error.push(result.error) - const errors = (result.errors || error).filter((e) => e.type === 'Error' || e.severity === 'error') - if (errors.length > 0) { - if (!isBrowser) require('signale').fatal(errors) // eslint-disable-line - return cb(new CompilationErrors(errors)) - } - cb(err, result.contracts, result.sources) // return callback with contract details & ASTs + }, + function doCompilation (next) { + // @ts-ignore + compiler.event.register('compilationFinished', this, (success, data, source, input, version) => { + next(null, data) }) - } + compiler.compile(sources, filepath) + } + ], function (err: Error | null | undefined, result: any) { + const error: Error[] = [] + if (result.error) error.push(result.error) + const errors = (result.errors || error).filter((e) => e.type === 'Error' || e.severity === 'error') + if (errors.length > 0) { + if (!isBrowser) require('signale').fatal(errors) // eslint-disable-line + return cb(new CompilationErrors(errors)) + } + cb(err, result.contracts, result.sources) // return callback with contract details & ASTs + }) + } } /** @@ -178,57 +178,57 @@ export function compileFileOrFiles (filename: string, isDirectory: boolean, opts * @param cb Callback */ export function compileContractSources (sources: SrcIfc, newCompConfig: any, importFileCb, UTRunner, opts: any, cb): void { - let compiler - const filepath = opts.testFilePath || '' - const testFileImportRegEx = /^(import)\s['"](remix_tests.sol|tests.sol)['"];/gm + let compiler + const filepath = opts.testFilePath || '' + const testFileImportRegEx = /^(import)\s['"](remix_tests.sol|tests.sol)['"];/gm - const includeTestLibs = '\nimport \'remix_tests.sol\';\n' - for (const file in sources) { - const c: string = sources[file].content - if (file.endsWith('_test.sol') && c && regexIndexOf(c, testFileImportRegEx) < 0) { - sources[file].content = includeTestLibs.concat(c) - } + const includeTestLibs = '\nimport \'remix_tests.sol\';\n' + for (const file in sources) { + const c: string = sources[file].content + if (file.endsWith('_test.sol') && c && regexIndexOf(c, testFileImportRegEx) < 0) { + sources[file].content = includeTestLibs.concat(c) } + } - async.waterfall([ - (next) => { - if (!compiler || !deepequal(UTRunner.compilerConfig, newCompConfig)) { - UTRunner.compilerConfig = newCompConfig - const { currentCompilerUrl, evmVersion, optimize, runs, usingWorker } = newCompConfig - compiler = new RemixCompiler(importFileCb) - compiler.set('evmVersion', evmVersion) - compiler.set('optimize', optimize) - compiler.set('runs', runs) - compiler.loadVersion(usingWorker, currentCompilerUrl) - // @ts-ignore - compiler.event.register('compilerLoaded', this, (version, license) => { - next() - }) - } else { - compiler = UTRunner.compiler - next() - } - }, - (next) => { - const compilationFinishedCb = (success, data, source, input, version) => { - // data.error usually exists for exceptions like worker error etc. - if (!data.error) UTRunner.compiler = compiler - if (opts && opts.event) opts.event.emit('compilationFinished', success, data, source, input, version) - next(null, data) - } - compiler.event.unregister('compilationFinished', compilationFinishedCb) - // @ts-ignore - compiler.event.register('compilationFinished', compilationFinishedCb) - compiler.compile(sources, filepath) - } - ], function (err: Error | null | undefined, result: any) { - const error: Error[] = [] - if (result.error) error.push(result.error) - const errors = (result.errors || error).filter((e) => e.type === 'Error' || e.severity === 'error') - if (errors.length > 0) { + async.waterfall([ + (next) => { + if (!compiler || !deepequal(UTRunner.compilerConfig, newCompConfig)) { + UTRunner.compilerConfig = newCompConfig + const { currentCompilerUrl, evmVersion, optimize, runs, usingWorker } = newCompConfig + compiler = new RemixCompiler(importFileCb) + compiler.set('evmVersion', evmVersion) + compiler.set('optimize', optimize) + compiler.set('runs', runs) + compiler.loadVersion(usingWorker, currentCompilerUrl) + // @ts-ignore + compiler.event.register('compilerLoaded', this, (version, license) => { + next() + }) + } else { + compiler = UTRunner.compiler + next() + } + }, + (next) => { + const compilationFinishedCb = (success, data, source, input, version) => { + // data.error usually exists for exceptions like worker error etc. + if (!data.error) UTRunner.compiler = compiler + if (opts && opts.event) opts.event.emit('compilationFinished', success, data, source, input, version) + next(null, data) + } + compiler.event.unregister('compilationFinished', compilationFinishedCb) + // @ts-ignore + compiler.event.register('compilationFinished', compilationFinishedCb) + compiler.compile(sources, filepath) + } + ], function (err: Error | null | undefined, result: any) { + const error: Error[] = [] + if (result.error) error.push(result.error) + const errors = (result.errors || error).filter((e) => e.type === 'Error' || e.severity === 'error') + if (errors.length > 0) { if (!isBrowser) require('signale').fatal(errors) // eslint-disable-line - return cb(new CompilationErrors(errors)) - } - cb(err, result.contracts, result.sources) // return callback with contract details & ASTs - }) + return cb(new CompilationErrors(errors)) + } + cb(err, result.contracts, result.sources) // return callback with contract details & ASTs + }) } diff --git a/libs/remix-tests/src/deployer.ts b/libs/remix-tests/src/deployer.ts index d3164eb285..1b34331575 100644 --- a/libs/remix-tests/src/deployer.ts +++ b/libs/remix-tests/src/deployer.ts @@ -12,105 +12,105 @@ import { compilationInterface } from './types' */ export function deployAll (compileResult: compilationInterface, web3: Web3, testsAccounts, withDoubleGas: boolean, deployCb, callback) { - const compiledObject = {} - const contracts = {} - const accounts: string[] = testsAccounts + const compiledObject = {} + const contracts = {} + const accounts: string[] = testsAccounts - async.waterfall([ - function getContractData (next) { - for (const contractFile in compileResult) { - for (const contractName in compileResult[contractFile]) { - const contract = compileResult[contractFile][contractName] + async.waterfall([ + function getContractData (next) { + for (const contractFile in compileResult) { + for (const contractName in compileResult[contractFile]) { + const contract = compileResult[contractFile][contractName] - const className = contractName - const filename = contractFile + const className = contractName + const filename = contractFile - const abi = contract.abi - const code = contract.evm.bytecode.object + const abi = contract.abi + const code = contract.evm.bytecode.object - compiledObject[className] = {} - compiledObject[className].abi = abi - compiledObject[className].code = code - compiledObject[className].filename = filename - compiledObject[className].className = className - compiledObject[className].raw = contract + compiledObject[className] = {} + compiledObject[className].abi = abi + compiledObject[className].code = code + compiledObject[className].filename = filename + compiledObject[className].className = className + compiledObject[className].raw = contract - if (contractFile.endsWith('_test.sol')) { - compiledObject[className].isTest = true - } - } - } - next() - }, - function determineContractsToDeploy (next) { - const contractsToDeploy: string[] = ['Assert'] - const allContracts = Object.keys(compiledObject) - - for (const contractName of allContracts) { - if (contractName === 'Assert') { - continue - } - if (compiledObject[contractName].isTest) { - contractsToDeploy.push(contractName) - } - } - next(null, contractsToDeploy) - }, - function deployContracts (contractsToDeploy: string[], next) { - const deployRunner = (deployObject, contractObject, contractName, filename, callback) => { - deployObject.estimateGas().then((gasValue) => { - const gasBase = Math.ceil(gasValue * 1.2) - const gas = withDoubleGas ? gasBase * 2 : gasBase - deployObject.send({ - from: accounts[0], - gas: gas - }).on('receipt', async function (receipt) { - contractObject.options.address = receipt.contractAddress - contractObject.options.from = accounts[0] - contractObject.options.gas = 5000 * 1000 - compiledObject[contractName].deployedAddress = receipt.contractAddress + if (contractFile.endsWith('_test.sol')) { + compiledObject[className].isTest = true + } + } + } + next() + }, + function determineContractsToDeploy (next) { + const contractsToDeploy: string[] = ['Assert'] + const allContracts = Object.keys(compiledObject) - contracts[contractName] = contractObject - contracts[contractName].filename = filename + for (const contractName of allContracts) { + if (contractName === 'Assert') { + continue + } + if (compiledObject[contractName].isTest) { + contractsToDeploy.push(contractName) + } + } + next(null, contractsToDeploy) + }, + function deployContracts (contractsToDeploy: string[], next) { + const deployRunner = (deployObject, contractObject, contractName, filename, callback) => { + deployObject.estimateGas().then((gasValue) => { + const gasBase = Math.ceil(gasValue * 1.2) + const gas = withDoubleGas ? gasBase * 2 : gasBase + deployObject.send({ + from: accounts[0], + gas: gas + }).on('receipt', async function (receipt) { + contractObject.options.address = receipt.contractAddress + contractObject.options.from = accounts[0] + contractObject.options.gas = 5000 * 1000 + compiledObject[contractName].deployedAddress = receipt.contractAddress - if (deployCb) await deployCb(filename, receipt.contractAddress) - callback(null, { receipt: { contractAddress: receipt.contractAddress } }) // TODO this will only work with JavaScriptV VM - }).on('error', function (err) { - console.error(err) - callback(err) - }) - }).catch((err) => { - console.error(err) - callback(err) - }) - } + contracts[contractName] = contractObject + contracts[contractName].filename = filename - async.eachOfLimit(contractsToDeploy, 1, function (contractName, index, nextEach) { - const contract = compiledObject[contractName] - const encodeDataFinalCallback = (error, contractDeployData) => { - if (error) return nextEach(error) - const contractObject = new web3.eth.Contract(contract.abi) - const deployObject = contractObject.deploy({ arguments: [], data: '0x' + contractDeployData.dataHex }) - deployRunner(deployObject, contractObject, contractName, contract.filename, (error) => { nextEach(error) }) - } + if (deployCb) await deployCb(filename, receipt.contractAddress) + callback(null, { receipt: { contractAddress: receipt.contractAddress } }) // TODO this will only work with JavaScriptV VM + }).on('error', function (err) { + console.error(err) + callback(err) + }) + }).catch((err) => { + console.error(err) + callback(err) + }) + } - const encodeDataStepCallback = (msg) => { console.dir(msg) } + async.eachOfLimit(contractsToDeploy, 1, function (contractName, index, nextEach) { + const contract = compiledObject[contractName] + const encodeDataFinalCallback = (error, contractDeployData) => { + if (error) return nextEach(error) + const contractObject = new web3.eth.Contract(contract.abi) + const deployObject = contractObject.deploy({ arguments: [], data: '0x' + contractDeployData.dataHex }) + deployRunner(deployObject, contractObject, contractName, contract.filename, (error) => { nextEach(error) }) + } - const encodeDataDeployLibraryCallback = (libData, callback) => { - const abi = compiledObject[libData.data.contractName].abi - const code = compiledObject[libData.data.contractName].code - const libraryObject = new web3.eth.Contract(abi) - const deployObject = libraryObject.deploy({ arguments: [], data: '0x' + code }) - deployRunner(deployObject, libraryObject, libData.data.contractName, contract.filename, callback) - } + const encodeDataStepCallback = (msg) => { console.dir(msg) } - const funAbi = null // no need to set the abi for encoding the constructor - const params = '' // we suppose that the test contract does not have any param in the constructor - execution.txFormat.encodeConstructorCallAndDeployLibraries(contractName, contract.raw, compileResult, params, funAbi, encodeDataFinalCallback, encodeDataStepCallback, encodeDataDeployLibraryCallback) - }, function (err) { - if (err) return next(err) - next(null, contracts) - }) + const encodeDataDeployLibraryCallback = (libData, callback) => { + const abi = compiledObject[libData.data.contractName].abi + const code = compiledObject[libData.data.contractName].code + const libraryObject = new web3.eth.Contract(abi) + const deployObject = libraryObject.deploy({ arguments: [], data: '0x' + code }) + deployRunner(deployObject, libraryObject, libData.data.contractName, contract.filename, callback) } - ], callback) + + const funAbi = null // no need to set the abi for encoding the constructor + const params = '' // we suppose that the test contract does not have any param in the constructor + execution.txFormat.encodeConstructorCallAndDeployLibraries(contractName, contract.raw, compileResult, params, funAbi, encodeDataFinalCallback, encodeDataStepCallback, encodeDataDeployLibraryCallback) + }, function (err) { + if (err) return next(err) + next(null, contracts) + }) + } + ], callback) } diff --git a/libs/remix-tests/src/fileSystem.ts b/libs/remix-tests/src/fileSystem.ts index 10a9301b96..01ca0f27ad 100644 --- a/libs/remix-tests/src/fileSystem.ts +++ b/libs/remix-tests/src/fileSystem.ts @@ -4,17 +4,17 @@ const fs: any = require('fs') // eslint-disable-line // https://github.com/mikeal/node-utils/blob/master/file/lib/main.js fs.walkSync = function (start: string, callback) { - fs.readdirSync(start).forEach((name: string) => { - if (name === 'node_modules') { - return // hack - } - const abspath = path.join(start, name) - if (fs.statSync(abspath).isDirectory()) { - fs.walkSync(abspath, callback) - } else { - callback(abspath) - } - }) + fs.readdirSync(start).forEach((name: string) => { + if (name === 'node_modules') { + return // hack + } + const abspath = path.join(start, name) + if (fs.statSync(abspath).isDirectory()) { + fs.walkSync(abspath, callback) + } else { + callback(abspath) + } + }) } export default fs diff --git a/libs/remix-tests/src/logger.ts b/libs/remix-tests/src/logger.ts index e084ed00a2..8a637defef 100644 --- a/libs/remix-tests/src/logger.ts +++ b/libs/remix-tests/src/logger.ts @@ -4,55 +4,55 @@ import timestamp from 'time-stamp' import supportsColor from 'color-support' function hasFlag (flag: string) { - return ((typeof (process) !== 'undefined') && (process.argv.indexOf('--' + flag) !== -1)) + return ((typeof (process) !== 'undefined') && (process.argv.indexOf('--' + flag) !== -1)) } function addColor (str: string) { - if (hasFlag('no-color')) { - return str - } + if (hasFlag('no-color')) { + return str + } - if (hasFlag('color')) { - return colors.gray(str) - } + if (hasFlag('color')) { + return colors.gray(str) + } - if (supportsColor()) { - return colors.gray(str) - } + if (supportsColor()) { + return colors.gray(str) + } - return str + return str } function getTimestamp () { - return '[' + addColor(timestamp('HH:mm:ss')) + ']' + return '[' + addColor(timestamp('HH:mm:ss')) + ']' } // create winston logger format const logFmt = winston.format.printf((info) => { - return `${getTimestamp()} ${info.level}: ${info.message}` + return `${getTimestamp()} ${info.level}: ${info.message}` }) class Log { - logger: Logger; - constructor () { - this.logger = winston.createLogger({ - level: 'info', - transports: [new winston.transports.Console()], - format: winston.format.combine( - winston.format.colorize({ all: true }), - logFmt - ) - }) - } - - setVerbosity (v: LoggerOptions['level']): void { - this.logger.configure({ - level: v, - transports: [new winston.transports.Console()], - format: winston.format.combine( - winston.format.colorize({ all: true }), - logFmt - ) - }) - } + logger: Logger; + constructor () { + this.logger = winston.createLogger({ + level: 'info', + transports: [new winston.transports.Console()], + format: winston.format.combine( + winston.format.colorize({ all: true }), + logFmt + ) + }) + } + + setVerbosity (v: LoggerOptions['level']): void { + this.logger.configure({ + level: v, + transports: [new winston.transports.Console()], + format: winston.format.combine( + winston.format.colorize({ all: true }), + logFmt + ) + }) + } } export default Log diff --git a/libs/remix-tests/src/run.ts b/libs/remix-tests/src/run.ts index 116da1cb28..12d0b890ef 100644 --- a/libs/remix-tests/src/run.ts +++ b/libs/remix-tests/src/run.ts @@ -14,23 +14,23 @@ const log = logger.logger const commander = new Command(); // parse verbosity function mapVerbosity (v: string) { - const levels = { - '0': 'error', - '1': 'warn', - '2': 'info', - '3': 'verbose', - '4': 'debug', - '5': 'silly' - } - return levels[v] + const levels = { + '0': 'error', + '1': 'warn', + '2': 'info', + '3': 'verbose', + '4': 'debug', + '5': 'silly' + } + return levels[v] } function mapOptimize (v: string) { - const optimize = { - true: true, - false: false - } - return optimize[v] + const optimize = { + true: true, + false: false + } + return optimize[v] } const version = require('../package.json').version // eslint-disable-line @@ -38,108 +38,108 @@ const version = require('../package.json').version // eslint-disable-line commander.version(version) commander.command('version').description('output the version number').action(function () { - console.log(version) + console.log(version) }) commander.command('help').description('output usage information').action(function () { - commander.help() + commander.help() }) // get current version commander - .option('-c, --compiler ', 'set compiler version (e.g: 0.6.1, 0.7.1 etc)') - .option('-e, --evm ', 'set EVM version (e.g: petersburg, istanbul etc)') - .option('-o, --optimize ', 'enable/disable optimization', mapOptimize) - .option('-r, --runs ', 'set runs (e.g: 150, 250 etc)') - .option('-v, --verbose ', 'set verbosity level (0 to 5)', mapVerbosity) - .option('-f, --fork ', 'set hard fork (e.g: istanbul, berlin etc. See full list of hard forks here: https://github.com/ethereumjs/ethereumjs-monorepo/tree/master/packages/common/src/hardforks)') - .option('-n, --nodeUrl ', 'set node url (e.g: https://mainnet.infura.io/v3/your-api-key)') - .option('-b, --blockNumber ', 'set block number (e.g: 123456)') - .option('-k, --killProcess ', 'kill process when tests fail') - .argument('file_path', 'path to test file or directory') - .action(async (file_path) => { - const options = commander.opts(); - // Check if path exists - if (!fs.existsSync(file_path)) { - log.error(file_path + ' not found') - process.exit(1) - } - - // Check if path is for a directory - const isDirectory = fs.lstatSync(file_path).isDirectory() - - // If path is for a file, file name must have `_test.sol` suffix - if (!isDirectory && !file_path.endsWith('_test.sol')) { - log.error('Test filename should end with "_test.sol"') - process.exit(1) - } - - // Console message - console.log(colors.bold('\n\t👁\t:: Running tests using remix-tests ::\t👁\n')) - - // Set logger verbosity - if (options.verbose) { - logger.setVerbosity(options.verbose) - log.info('verbosity level set to ' + options.verbose.blue) - } - - const compilerConfig = {} as CompilerConfiguration - if (options.compiler) { - const compVersion = options.compiler - const baseURL = 'https://binaries.soliditylang.org/wasm/' - const response: AxiosResponse = await axios.get(baseURL + 'list.json') - const { releases, latestRelease } = response.data as { releases: string[], latestRelease: string } - const compString = releases ? releases[compVersion] : null - if (!compString) { - log.error(`No compiler found in releases with version ${compVersion}`) - process.exit(1) - } else { - compilerConfig.currentCompilerUrl = compString.replace('soljson-', '').replace('.js', '') - log.info(`Compiler version set to ${compVersion}. Latest version is ${latestRelease}`) - } - } - - if (options.evm) { - compilerConfig.evmVersion = options.evm - log.info(`EVM set to ${compilerConfig.evmVersion}`) - } - - if (options.optimize) { - compilerConfig.optimize = options.optimize - log.info(`Optimization is ${compilerConfig.optimize ? 'enabled' : 'disabled'}`) - } - - if (options.runs) { - if (!options.optimize) { - log.error('Optimization should be enabled for runs') - process.exit(1) - } - compilerConfig.runs = options.runs - log.info(`Runs set to ${compilerConfig.runs}`) - } - - if (options.fork && options.nodeUrl) { - log.info('Using hard fork ' + colors.green(options.fork) + ' and node url ' + colors.blue(options.nodeUrl)) - } - - const providerConfig = { - fork: options.fork || null, - nodeUrl: options.nodeUrl || null, - blockNumber: options.blockNumber || null - } - const web3 = new Web3() - const provider: any = new Provider(providerConfig) - await provider.init() - web3.setProvider(provider) - extend(web3) - runTestFiles(path.resolve(file_path), isDirectory, web3, compilerConfig, (error, totalPassing, totalFailing) => { - if (error) process.exit(1) - if (totalFailing > 0 && options.killProcess) process.exit(1) - }) + .option('-c, --compiler ', 'set compiler version (e.g: 0.6.1, 0.7.1 etc)') + .option('-e, --evm ', 'set EVM version (e.g: petersburg, istanbul etc)') + .option('-o, --optimize ', 'enable/disable optimization', mapOptimize) + .option('-r, --runs ', 'set runs (e.g: 150, 250 etc)') + .option('-v, --verbose ', 'set verbosity level (0 to 5)', mapVerbosity) + .option('-f, --fork ', 'set hard fork (e.g: istanbul, berlin etc. See full list of hard forks here: https://github.com/ethereumjs/ethereumjs-monorepo/tree/master/packages/common/src/hardforks)') + .option('-n, --nodeUrl ', 'set node url (e.g: https://mainnet.infura.io/v3/your-api-key)') + .option('-b, --blockNumber ', 'set block number (e.g: 123456)') + .option('-k, --killProcess ', 'kill process when tests fail') + .argument('file_path', 'path to test file or directory') + .action(async (file_path) => { + const options = commander.opts(); + // Check if path exists + if (!fs.existsSync(file_path)) { + log.error(file_path + ' not found') + process.exit(1) + } + + // Check if path is for a directory + const isDirectory = fs.lstatSync(file_path).isDirectory() + + // If path is for a file, file name must have `_test.sol` suffix + if (!isDirectory && !file_path.endsWith('_test.sol')) { + log.error('Test filename should end with "_test.sol"') + process.exit(1) + } + + // Console message + console.log(colors.bold('\n\t👁\t:: Running tests using remix-tests ::\t👁\n')) + + // Set logger verbosity + if (options.verbose) { + logger.setVerbosity(options.verbose) + log.info('verbosity level set to ' + options.verbose.blue) + } + + const compilerConfig = {} as CompilerConfiguration + if (options.compiler) { + const compVersion = options.compiler + const baseURL = 'https://binaries.soliditylang.org/wasm/' + const response: AxiosResponse = await axios.get(baseURL + 'list.json') + const { releases, latestRelease } = response.data as { releases: string[], latestRelease: string } + const compString = releases ? releases[compVersion] : null + if (!compString) { + log.error(`No compiler found in releases with version ${compVersion}`) + process.exit(1) + } else { + compilerConfig.currentCompilerUrl = compString.replace('soljson-', '').replace('.js', '') + log.info(`Compiler version set to ${compVersion}. Latest version is ${latestRelease}`) + } + } + + if (options.evm) { + compilerConfig.evmVersion = options.evm + log.info(`EVM set to ${compilerConfig.evmVersion}`) + } + + if (options.optimize) { + compilerConfig.optimize = options.optimize + log.info(`Optimization is ${compilerConfig.optimize ? 'enabled' : 'disabled'}`) + } + + if (options.runs) { + if (!options.optimize) { + log.error('Optimization should be enabled for runs') + process.exit(1) + } + compilerConfig.runs = options.runs + log.info(`Runs set to ${compilerConfig.runs}`) + } + + if (options.fork && options.nodeUrl) { + log.info('Using hard fork ' + colors.green(options.fork) + ' and node url ' + colors.blue(options.nodeUrl)) + } + + const providerConfig = { + fork: options.fork || null, + nodeUrl: options.nodeUrl || null, + blockNumber: options.blockNumber || null + } + const web3 = new Web3() + const provider: any = new Provider(providerConfig) + await provider.init() + web3.setProvider(provider) + extend(web3) + runTestFiles(path.resolve(file_path), isDirectory, web3, compilerConfig, (error, totalPassing, totalFailing) => { + if (error) process.exit(1) + if (totalFailing > 0 && options.killProcess) process.exit(1) }) + }) if (!process.argv.slice(2).length) { - log.error('Please specify a file or directory path') - process.exit(1) + log.error('Please specify a file or directory path') + process.exit(1) } commander.parse(process.argv) diff --git a/libs/remix-tests/src/runTestFiles.ts b/libs/remix-tests/src/runTestFiles.ts index a811f9b441..b2eb7c64f5 100644 --- a/libs/remix-tests/src/runTestFiles.ts +++ b/libs/remix-tests/src/runTestFiles.ts @@ -19,162 +19,162 @@ import { deployAll } from './deployer' // eslint-disable-next-line @typescript-eslint/no-empty-function export function runTestFiles (filepath: string, isDirectory: boolean, web3: Web3, compilerConfig: CompilerConfiguration, finalCallback: any = () => {}, opts?: Options) { - opts = opts || {} - compilerConfig = compilerConfig || {} as CompilerConfiguration - const sourceASTs: any = {} - const printLog = (log: string[]) => { - let formattedLog - if (typeof log[0] === 'string' && (log[0].includes('%s') || log[0].includes('%d'))) { - formattedLog = format(log[0], ...log.slice(1)) - } else { - formattedLog = log.join(' ') - } - signale.log(formattedLog) + opts = opts || {} + compilerConfig = compilerConfig || {} as CompilerConfiguration + const sourceASTs: any = {} + const printLog = (log: string[]) => { + let formattedLog + if (typeof log[0] === 'string' && (log[0].includes('%s') || log[0].includes('%d'))) { + formattedLog = format(log[0], ...log.slice(1)) + } else { + formattedLog = log.join(' ') } + signale.log(formattedLog) + } const { Signale } = require('signale') // eslint-disable-line - // signale configuration - const options = { - types: { - result: { - badge: '\t✓', - label: '', - color: 'greenBright' - }, - name: { - badge: '\n\t◼', - label: '', - color: 'whiteBright' - }, - log: { - badge: '\t', - label: '', - color: 'white' - }, - error: { - badge: '\t✘', - label: '', - color: 'redBright' - } - } + // signale configuration + const options = { + types: { + result: { + badge: '\t✓', + label: '', + color: 'greenBright' + }, + name: { + badge: '\n\t◼', + label: '', + color: 'whiteBright' + }, + log: { + badge: '\t', + label: '', + color: 'white' + }, + error: { + badge: '\t✘', + label: '', + color: 'redBright' + } } - const signale = new Signale(options) - let accounts = opts['accounts'] || null - async.waterfall([ - function getAccountList (next) { - if (accounts) return next(null) - web3.eth.getAccounts((_err: Error | null | undefined, _accounts) => { - accounts = _accounts - next(null) - }) - }, - function compile (next) { - compileFileOrFiles(filepath, isDirectory, { accounts }, compilerConfig, next) - }, - function deployAllContracts (compilationResult: compilationInterface, asts: ASTInterface, next) { - // Extract AST of test contract file source - for (const filename in asts) { - if (filename.endsWith('_test.sol')) { sourceASTs[filename] = asts[filename].ast } - } - deployAll(compilationResult, web3, accounts, false, null, (err, contracts) => { - if (err) { - // If contract deployment fails because of 'Out of Gas' error, try again with double gas - // This is temporary, should be removed when remix-tests will have a dedicated UI to - // accept deployment params from UI - if (err.message.includes('The contract code couldn\'t be stored, please check your gas limit')) { - deployAll(compilationResult, web3, accounts, true, null, (error, contracts) => { - if (error) next([{ message: 'contract deployment failed after trying twice: ' + error.message, severity: 'error' }]) // IDE expects errors in array - else next(null, compilationResult, contracts) - }) - } else { next([{ message: 'contract deployment failed: ' + err.message, severity: 'error' }]) } // IDE expects errors in array - } else { next(null, compilationResult, contracts) } + } + const signale = new Signale(options) + let accounts = opts['accounts'] || null + async.waterfall([ + function getAccountList (next) { + if (accounts) return next(null) + web3.eth.getAccounts((_err: Error | null | undefined, _accounts) => { + accounts = _accounts + next(null) + }) + }, + function compile (next) { + compileFileOrFiles(filepath, isDirectory, { accounts }, compilerConfig, next) + }, + function deployAllContracts (compilationResult: compilationInterface, asts: ASTInterface, next) { + // Extract AST of test contract file source + for (const filename in asts) { + if (filename.endsWith('_test.sol')) { sourceASTs[filename] = asts[filename].ast } + } + deployAll(compilationResult, web3, accounts, false, null, (err, contracts) => { + if (err) { + // If contract deployment fails because of 'Out of Gas' error, try again with double gas + // This is temporary, should be removed when remix-tests will have a dedicated UI to + // accept deployment params from UI + if (err.message.includes('The contract code couldn\'t be stored, please check your gas limit')) { + deployAll(compilationResult, web3, accounts, true, null, (error, contracts) => { + if (error) next([{ message: 'contract deployment failed after trying twice: ' + error.message, severity: 'error' }]) // IDE expects errors in array + else next(null, compilationResult, contracts) }) - }, - function determineTestContractsToRun (compilationResult: compilationInterface, contracts: any, next) { - const contractsToTest: string[] = [] - const contractsToTestDetails: any[] = [] - const gatherContractsFrom = function (filename: string) { - if (!filename.endsWith('_test.sol')) { - return - } - try { - Object.keys(compilationResult[filename]).forEach(contractName => { - contractsToTest.push(contractName) - contractsToTestDetails.push(compilationResult[filename][contractName]) - }) - } catch (e) { - console.error(e) - } - } - if (isDirectory) { - fs.walkSync(filepath, (foundpath: string) => { - gatherContractsFrom(foundpath) - }) - } else { - gatherContractsFrom(filepath) - } - next(null, contractsToTest, contractsToTestDetails, contracts) - }, - function runTests (contractsToTest: string[], contractsToTestDetails: any[], contracts: any, next) { - let totalPassing = 0 - let totalFailing = 0 - let totalTime = 0 + } else { next([{ message: 'contract deployment failed: ' + err.message, severity: 'error' }]) } // IDE expects errors in array + } else { next(null, compilationResult, contracts) } + }) + }, + function determineTestContractsToRun (compilationResult: compilationInterface, contracts: any, next) { + const contractsToTest: string[] = [] + const contractsToTestDetails: any[] = [] + const gatherContractsFrom = function (filename: string) { + if (!filename.endsWith('_test.sol')) { + return + } + try { + Object.keys(compilationResult[filename]).forEach(contractName => { + contractsToTest.push(contractName) + contractsToTestDetails.push(compilationResult[filename][contractName]) + }) + } catch (e) { + console.error(e) + } + } + if (isDirectory) { + fs.walkSync(filepath, (foundpath: string) => { + gatherContractsFrom(foundpath) + }) + } else { + gatherContractsFrom(filepath) + } + next(null, contractsToTest, contractsToTestDetails, contracts) + }, + function runTests (contractsToTest: string[], contractsToTestDetails: any[], contracts: any, next) { + let totalPassing = 0 + let totalFailing = 0 + let totalTime = 0 - const _testCallback = function (err: Error | null | undefined, result: TestResultInterface) { - if (err) throw err - if (result.type === 'contract') { - signale.name(result.value) - console.log('\n') - } else if (result.type === 'testPass') { - if (result?.hhLogs?.length) result.hhLogs.forEach(printLog) - signale.result(result.value.white) - } else if (result.type === 'testFailure') { - if (result?.hhLogs?.length) result.hhLogs.forEach(printLog) - signale.error(result.value.white) - if (result.assertMethod) { - console.log(colors.green('\t Expected value should be ' + result.assertMethod + ' to: ' + result.expected)) - console.log(colors.red('\t Received: ' + result.returned)) - } - console.log(colors.red('\t Message: ' + result.errMsg)) - console.log('\n') - } - } - const _resultsCallback = (_err: Error | null | undefined, result: ResultsInterface, cb) => { - totalPassing += result.passingNum - totalFailing += result.failureNum - totalTime += result.timePassed - cb() - } + const _testCallback = function (err: Error | null | undefined, result: TestResultInterface) { + if (err) throw err + if (result.type === 'contract') { + signale.name(result.value) + console.log('\n') + } else if (result.type === 'testPass') { + if (result?.hhLogs?.length) result.hhLogs.forEach(printLog) + signale.result(result.value.white) + } else if (result.type === 'testFailure') { + if (result?.hhLogs?.length) result.hhLogs.forEach(printLog) + signale.error(result.value.white) + if (result.assertMethod) { + console.log(colors.green('\t Expected value should be ' + result.assertMethod + ' to: ' + result.expected)) + console.log(colors.red('\t Received: ' + result.returned)) + } + console.log(colors.red('\t Message: ' + result.errMsg)) + console.log('\n') + } + } + const _resultsCallback = (_err: Error | null | undefined, result: ResultsInterface, cb) => { + totalPassing += result.passingNum + totalFailing += result.failureNum + totalTime += result.timePassed + cb() + } - async.eachOfLimit(contractsToTest, 1, (contractName: string, index, cb) => { - try { - const fileAST: AstNode = sourceASTs[contracts[contractName]['filename']] - runTest(contractName, contracts[contractName], contractsToTestDetails[index], fileAST, { accounts, web3 }, _testCallback, (err, result) => { - if (err) { - console.log(err) - return cb(err) - } - _resultsCallback(null, result, cb) - }) - } catch (e) { - console.error(e) - } - }, function (err) { - if (err) { - return next(err) - } - console.log('\n') - console.log(colors.bold.underline('Tests Summary: ')) + async.eachOfLimit(contractsToTest, 1, (contractName: string, index, cb) => { + try { + const fileAST: AstNode = sourceASTs[contracts[contractName]['filename']] + runTest(contractName, contracts[contractName], contractsToTestDetails[index], fileAST, { accounts, web3 }, _testCallback, (err, result) => { + if (err) { + console.log(err) + return cb(err) + } + _resultsCallback(null, result, cb) + }) + } catch (e) { + console.error(e) + } + }, function (err) { + if (err) { + return next(err) + } + console.log('\n') + console.log(colors.bold.underline('Tests Summary: ')) - if (totalPassing >= 0) { - console.log(colors.green('Passed: ' + totalPassing)) - } - if (totalFailing >= 0) { - console.log(colors.red('Failed: ' + totalFailing)) - } - console.log(colors.white('Time Taken: ' + totalTime + 's')) - console.log('') - next(null, totalPassing, totalFailing) - }) + if (totalPassing >= 0) { + console.log(colors.green('Passed: ' + totalPassing)) } - ], finalCallback) + if (totalFailing >= 0) { + console.log(colors.red('Failed: ' + totalFailing)) + } + console.log(colors.white('Time Taken: ' + totalTime + 's')) + console.log('') + next(null, totalPassing, totalFailing) + }) + } + ], finalCallback) } diff --git a/libs/remix-tests/src/runTestSources.ts b/libs/remix-tests/src/runTestSources.ts index f6cb8cbcf3..387f83675b 100644 --- a/libs/remix-tests/src/runTestSources.ts +++ b/libs/remix-tests/src/runTestSources.ts @@ -6,36 +6,36 @@ import Web3 from 'web3' import { EventEmitter } from 'events' import { Provider, extend } from '@remix-project/remix-simulator' import { - FinalResult, SrcIfc, compilationInterface, ASTInterface, Options, - TestResultInterface, AstNode, CompilerConfiguration + FinalResult, SrcIfc, compilationInterface, ASTInterface, Options, + TestResultInterface, AstNode, CompilerConfiguration } from './types' require('colors') export class UnitTestRunner { - event - accountsLibCode - testsAccounts: string[] | null - web3 - compiler - compilerConfig + event + accountsLibCode + testsAccounts: string[] | null + web3 + compiler + compilerConfig - constructor () { - this.event = new EventEmitter() - } + constructor () { + this.event = new EventEmitter() + } - async init (web3 = null, accounts = null) { - this.web3 = await this.createWeb3Provider(web3) - this.testsAccounts = accounts || (this.web3 && await this.web3.eth.getAccounts()) || [] - this.accountsLibCode = writeTestAccountsContract(this.testsAccounts) - } + async init (web3 = null, accounts = null) { + this.web3 = await this.createWeb3Provider(web3) + this.testsAccounts = accounts || (this.web3 && await this.web3.eth.getAccounts()) || [] + this.accountsLibCode = writeTestAccountsContract(this.testsAccounts) + } - async createWeb3Provider (optWeb3) { - const web3 = optWeb3 - if (web3) extend(web3) - return web3 - } + async createWeb3Provider (optWeb3) { + const web3 = optWeb3 + if (web3) extend(web3) + return web3 + } - /** + /** * @dev Run tests from source of a test contract file (used for IDE) * @param contractSources Sources of contract * @param compilerConfig current compiler configuration @@ -45,100 +45,100 @@ export class UnitTestRunner { * @param importFileCb Import file callback * @param opts Options */ - async runTestSources (contractSources: SrcIfc, newCompilerConfig: CompilerConfiguration, testCallback, resultCallback, deployCb:any, finalCallback: any, importFileCb, opts: Options) { - opts = opts || {} - const sourceASTs: any = {} - if (opts.web3 || opts.accounts) this.init(opts.web3, opts.accounts) - async.waterfall([ - (next) => { - compileContractSources(contractSources, newCompilerConfig, importFileCb, this, { accounts: this.testsAccounts, testFilePath: opts.testFilePath, event: this.event }, next) - }, - (compilationResult: compilationInterface, asts: ASTInterface, next) => { - for (const filename in asts) { - if (filename.endsWith('_test.sol')) { sourceASTs[filename] = asts[filename].ast } - } - deployAll(compilationResult, this.web3, this.testsAccounts, false, deployCb, (err, contracts) => { - if (err) { - // If contract deployment fails because of 'Out of Gas' error, try again with double gas - // This is temporary, should be removed when remix-tests will have a dedicated UI to - // accept deployment params from UI - if (err.message.includes('The contract code couldn\'t be stored, please check your gas limit')) { - deployAll(compilationResult, this.web3, this.testsAccounts, true, deployCb, (error, contracts) => { - if (error) next([{ message: 'contract deployment failed after trying twice: ' + error.message, severity: 'error' }]) // IDE expects errors in array - else next(null, compilationResult, contracts) - }) - } else { next([{ message: 'contract deployment failed: ' + err.message, severity: 'error' }]) } // IDE expects errors in array - } else { next(null, compilationResult, contracts) } - }) - }, - function determineTestContractsToRun (compilationResult: compilationInterface, contracts: any, next) { - const contractsToTest: string[] = [] - const contractsToTestDetails: any[] = [] + async runTestSources (contractSources: SrcIfc, newCompilerConfig: CompilerConfiguration, testCallback, resultCallback, deployCb:any, finalCallback: any, importFileCb, opts: Options) { + opts = opts || {} + const sourceASTs: any = {} + if (opts.web3 || opts.accounts) this.init(opts.web3, opts.accounts) + async.waterfall([ + (next) => { + compileContractSources(contractSources, newCompilerConfig, importFileCb, this, { accounts: this.testsAccounts, testFilePath: opts.testFilePath, event: this.event }, next) + }, + (compilationResult: compilationInterface, asts: ASTInterface, next) => { + for (const filename in asts) { + if (filename.endsWith('_test.sol')) { sourceASTs[filename] = asts[filename].ast } + } + deployAll(compilationResult, this.web3, this.testsAccounts, false, deployCb, (err, contracts) => { + if (err) { + // If contract deployment fails because of 'Out of Gas' error, try again with double gas + // This is temporary, should be removed when remix-tests will have a dedicated UI to + // accept deployment params from UI + if (err.message.includes('The contract code couldn\'t be stored, please check your gas limit')) { + deployAll(compilationResult, this.web3, this.testsAccounts, true, deployCb, (error, contracts) => { + if (error) next([{ message: 'contract deployment failed after trying twice: ' + error.message, severity: 'error' }]) // IDE expects errors in array + else next(null, compilationResult, contracts) + }) + } else { next([{ message: 'contract deployment failed: ' + err.message, severity: 'error' }]) } // IDE expects errors in array + } else { next(null, compilationResult, contracts) } + }) + }, + function determineTestContractsToRun (compilationResult: compilationInterface, contracts: any, next) { + const contractsToTest: string[] = [] + const contractsToTestDetails: any[] = [] - for (const filename in compilationResult) { - if (!filename.endsWith('_test.sol')) { - continue - } - Object.keys(compilationResult[filename]).forEach(contractName => { - contractsToTestDetails.push(compilationResult[filename][contractName]) - contractsToTest.push(contractName) - }) - } - next(null, contractsToTest, contractsToTestDetails, contracts) - }, - (contractsToTest: string[], contractsToTestDetails: any[], contracts: any, next) => { - let totalPassing = 0 - let totalFailing = 0 - let totalTime = 0 - const errors: any[] = [] - // eslint-disable-next-line handle-callback-err - const _testCallback = function (err: Error | null | undefined, result: TestResultInterface) { - if (result.type === 'testFailure') { - errors.push(result) - } - testCallback(result) - } + for (const filename in compilationResult) { + if (!filename.endsWith('_test.sol')) { + continue + } + Object.keys(compilationResult[filename]).forEach(contractName => { + contractsToTestDetails.push(compilationResult[filename][contractName]) + contractsToTest.push(contractName) + }) + } + next(null, contractsToTest, contractsToTestDetails, contracts) + }, + (contractsToTest: string[], contractsToTestDetails: any[], contracts: any, next) => { + let totalPassing = 0 + let totalFailing = 0 + let totalTime = 0 + const errors: any[] = [] + // eslint-disable-next-line handle-callback-err + const _testCallback = function (err: Error | null | undefined, result: TestResultInterface) { + if (result.type === 'testFailure') { + errors.push(result) + } + testCallback(result) + } - const _resultsCallback = function (_err, result, cb) { - resultCallback(_err, result, () => {}) // eslint-disable-line @typescript-eslint/no-empty-function - totalPassing += result.passingNum - totalFailing += result.failureNum - totalTime += result.timePassed - cb() - } + const _resultsCallback = function (_err, result, cb) { + resultCallback(_err, result, () => {}) // eslint-disable-line @typescript-eslint/no-empty-function + totalPassing += result.passingNum + totalFailing += result.failureNum + totalTime += result.timePassed + cb() + } - async.eachOfLimit(contractsToTest, 1, (contractName: string, index: string | number, cb: ErrorCallback) => { - const fileAST: AstNode = sourceASTs[contracts[contractName]['filename']] - runTest(contractName, contracts[contractName], contractsToTestDetails[index], fileAST, { accounts: this.testsAccounts, web3: this.web3 }, _testCallback, (err, result) => { - if (err) { - return cb(err) - } - _resultsCallback(null, result, cb) - }) - }, function (err) { - if (err) { - return next(err) - } + async.eachOfLimit(contractsToTest, 1, (contractName: string, index: string | number, cb: ErrorCallback) => { + const fileAST: AstNode = sourceASTs[contracts[contractName]['filename']] + runTest(contractName, contracts[contractName], contractsToTestDetails[index], fileAST, { accounts: this.testsAccounts, web3: this.web3 }, _testCallback, (err, result) => { + if (err) { + return cb(err) + } + _resultsCallback(null, result, cb) + }) + }, function (err) { + if (err) { + return next(err) + } - const finalResults: FinalResult = { - totalPassing: 0, - totalFailing: 0, - totalTime: 0, - errors: [] - } + const finalResults: FinalResult = { + totalPassing: 0, + totalFailing: 0, + totalTime: 0, + errors: [] + } - finalResults.totalPassing = totalPassing || 0 - finalResults.totalFailing = totalFailing || 0 - finalResults.totalTime = totalTime || 0 - finalResults.errors = [] + finalResults.totalPassing = totalPassing || 0 + finalResults.totalFailing = totalFailing || 0 + finalResults.totalTime = totalTime || 0 + finalResults.errors = [] - errors.forEach((error, _index) => { - finalResults.errors.push({ context: error.context, value: error.value, message: error.errMsg }) - }) + errors.forEach((error, _index) => { + finalResults.errors.push({ context: error.context, value: error.value, message: error.errMsg }) + }) - next(null, finalResults) - }) - } - ], finalCallback) - } + next(null, finalResults) + }) + } + ], finalCallback) + } } diff --git a/libs/remix-tests/src/testRunner.ts b/libs/remix-tests/src/testRunner.ts index 7731786068..f8192ed44d 100644 --- a/libs/remix-tests/src/testRunner.ts +++ b/libs/remix-tests/src/testRunner.ts @@ -3,8 +3,8 @@ import * as changeCase from 'change-case' import Web3 from 'web3' import assertionEvents from './assertionEvents' import { - RunListInterface, TestCbInterface, TestResultInterface, ResultCbInterface, - CompiledContract, AstNode, Options, FunctionDescription, UserDocumentation + RunListInterface, TestCbInterface, TestResultInterface, ResultCbInterface, + CompiledContract, AstNode, Options, FunctionDescription, UserDocumentation } from './types' /** @@ -14,12 +14,12 @@ import { */ function getFunctionFullName (signature: string, methodIdentifiers: Record ): string | null { - for (const method in methodIdentifiers) { - if (signature.replace('0x', '') === methodIdentifiers[method].replace('0x', '')) { - return method - } + for (const method in methodIdentifiers) { + if (signature.replace('0x', '') === methodIdentifiers[method].replace('0x', '')) { + return method } - return null + } + return null } /** @@ -28,7 +28,7 @@ function getFunctionFullName (signature: string, methodIdentifiers: Record ): string | null { - const fullName: string | null = getFunctionFullName(signature, methodIdentifiers) - const senderRegex = /#sender: account-+(\d)/g - const accountIndex: RegExpExecArray | null = fullName && userdoc.methods[fullName] ? senderRegex.exec(userdoc.methods[fullName].notice) : null - return fullName && accountIndex ? accountIndex[1] : null + const fullName: string | null = getFunctionFullName(signature, methodIdentifiers) + const senderRegex = /#sender: account-+(\d)/g + const accountIndex: RegExpExecArray | null = fullName && userdoc.methods[fullName] ? senderRegex.exec(userdoc.methods[fullName].notice) : null + return fullName && accountIndex ? accountIndex[1] : null } /** @@ -92,10 +92,10 @@ function getOverridedSender (userdoc: UserDocumentation, signature: string, meth */ function getProvidedValue (userdoc: UserDocumentation, signature: string, methodIdentifiers: Record ): string | null { - const fullName: string | null = getFunctionFullName(signature, methodIdentifiers) - const valueRegex = /#value: (\d+)/g - const value: RegExpExecArray | null = fullName && userdoc.methods[fullName] ? valueRegex.exec(userdoc.methods[fullName].notice) : null - return fullName && value ? value[1] : null + const fullName: string | null = getFunctionFullName(signature, methodIdentifiers) + const valueRegex = /#value: (\d+)/g + const value: RegExpExecArray | null = fullName && userdoc.methods[fullName] ? valueRegex.exec(userdoc.methods[fullName].notice) : null + return fullName && value ? value[1] : null } /** @@ -105,36 +105,36 @@ function getProvidedValue (userdoc: UserDocumentation, signature: string, method */ function getAvailableFunctions (fileAST: AstNode, testContractName: string): string[] { - let funcList: string[] = [] - if (fileAST.nodes && fileAST.nodes.length > 0) { - const contractAST: AstNode[] = fileAST.nodes.filter(node => isNodeName(node, testContractName) && isNodeType(node, 'ContractDefinition')) - if (contractAST.length > 0 && contractAST[0].nodes) { - const funcNodes: AstNode[] = contractAST[0].nodes.filter(node => ((node.kind === 'function' && isNodeType(node, 'FunctionDefinition')) || isNodeType(node, 'FunctionDefinition'))) - funcList = funcNodes.map(node => node.name) - } + let funcList: string[] = [] + if (fileAST.nodes && fileAST.nodes.length > 0) { + const contractAST: AstNode[] = fileAST.nodes.filter(node => isNodeName(node, testContractName) && isNodeType(node, 'ContractDefinition')) + if (contractAST.length > 0 && contractAST[0].nodes) { + const funcNodes: AstNode[] = contractAST[0].nodes.filter(node => ((node.kind === 'function' && isNodeType(node, 'FunctionDefinition')) || isNodeType(node, 'FunctionDefinition'))) + funcList = funcNodes.map(node => node.name) } - return funcList + } + return funcList } function getAssertMethodLocation (fileAST: AstNode, testContractName: string, functionName: string, assertMethod: string): string { - if (fileAST.nodes?.length) { - const contractAST: AstNode = fileAST.nodes.find(node => isNodeName(node, testContractName) && isNodeType(node, 'ContractDefinition')) - if (contractAST?.nodes?.length) { - const funcNode: AstNode = contractAST.nodes.find(node => isNodeName(node, functionName) && isNodeType(node, 'FunctionDefinition')) - // Check if statement nodeType is 'ExpressionStatement' or 'Return', for examples: - // Assert.equal(foo.get(), 100, "initial value is not correct"); - // return Assert.equal(foo.get(), 100, "initial value is not correct"); - const expressions = funcNode.body.statements.filter(s => - isNodeTypeIn(s, ['ExpressionStatement', 'Return']) && + if (fileAST.nodes?.length) { + const contractAST: AstNode = fileAST.nodes.find(node => isNodeName(node, testContractName) && isNodeType(node, 'ContractDefinition')) + if (contractAST?.nodes?.length) { + const funcNode: AstNode = contractAST.nodes.find(node => isNodeName(node, functionName) && isNodeType(node, 'FunctionDefinition')) + // Check if statement nodeType is 'ExpressionStatement' or 'Return', for examples: + // Assert.equal(foo.get(), 100, "initial value is not correct"); + // return Assert.equal(foo.get(), 100, "initial value is not correct"); + const expressions = funcNode.body.statements.filter(s => + isNodeTypeIn(s, ['ExpressionStatement', 'Return']) && isNodeType(s.expression, 'FunctionCall')) - const assetExpression = expressions.find(e => e.expression.expression && + const assetExpression = expressions.find(e => e.expression.expression && isNodeType(e.expression.expression, 'MemberAccess') && e.expression.expression.memberName === assertMethod && isNodeName(e.expression.expression.expression, 'Assert') - ) - return assetExpression?.expression?.src - } + ) + return assetExpression?.expression?.src } + } } /** @@ -144,15 +144,15 @@ function getAssertMethodLocation (fileAST: AstNode, testContractName: string, fu */ function getTestFunctionsInterface (jsonInterface: FunctionDescription[], funcList: string[]): FunctionDescription[] { - const functionsInterface: FunctionDescription[] = [] - const specialFunctions: string[] = ['beforeAll', 'beforeEach', 'afterAll', 'afterEach'] - for (const func of funcList) { - if (!specialFunctions.includes(func)) { - const funcInterface: FunctionDescription | undefined = jsonInterface.find(node => node.type === 'function' && node.name === func) - if (funcInterface) functionsInterface.push(funcInterface) - } + const functionsInterface: FunctionDescription[] = [] + const specialFunctions: string[] = ['beforeAll', 'beforeEach', 'afterAll', 'afterEach'] + for (const func of funcList) { + if (!specialFunctions.includes(func)) { + const funcInterface: FunctionDescription | undefined = jsonInterface.find(node => node.type === 'function' && node.name === func) + if (funcInterface) functionsInterface.push(funcInterface) } - return functionsInterface + } + return functionsInterface } /** @@ -161,15 +161,15 @@ function getTestFunctionsInterface (jsonInterface: FunctionDescription[], funcLi */ function getSpecialFunctionsInterface (jsonInterface: FunctionDescription[]): Record { - const specialFunctionsInterface: Record = {} - const funcList: string[] = ['beforeAll', 'beforeEach', 'afterAll', 'afterEach'] - for (const func of funcList) { - const funcInterface: FunctionDescription | undefined = jsonInterface.find(node => node.type === 'function' && node.name === func) - if (funcInterface) { - specialFunctionsInterface[func] = funcInterface - } + const specialFunctionsInterface: Record = {} + const funcList: string[] = ['beforeAll', 'beforeEach', 'afterAll', 'afterEach'] + for (const func of funcList) { + const funcInterface: FunctionDescription | undefined = jsonInterface.find(node => node.type === 'function' && node.name === func) + if (funcInterface) { + specialFunctionsInterface[func] = funcInterface } - return specialFunctionsInterface + } + return specialFunctionsInterface } /** @@ -180,230 +180,230 @@ function getSpecialFunctionsInterface (jsonInterface: FunctionDescription[]): Re */ function createRunList (jsonInterface: FunctionDescription[], fileAST: AstNode, testContractName: string): RunListInterface[] { - const availableFunctions: string[] = getAvailableFunctions(fileAST, testContractName) - const testFunctionsInterface: FunctionDescription[] = getTestFunctionsInterface(jsonInterface, availableFunctions) - const specialFunctionsInterface: Record = getSpecialFunctionsInterface(jsonInterface) - const runList: RunListInterface[] = [] - - if (availableFunctions.includes('beforeAll')) { - const func = specialFunctionsInterface['beforeAll'] - runList.push({ name: 'beforeAll', inputs: func.inputs, signature: func.signature, type: 'internal', constant: isConstant(func), payable: isPayable(func) }) + const availableFunctions: string[] = getAvailableFunctions(fileAST, testContractName) + const testFunctionsInterface: FunctionDescription[] = getTestFunctionsInterface(jsonInterface, availableFunctions) + const specialFunctionsInterface: Record = getSpecialFunctionsInterface(jsonInterface) + const runList: RunListInterface[] = [] + + if (availableFunctions.includes('beforeAll')) { + const func = specialFunctionsInterface['beforeAll'] + runList.push({ name: 'beforeAll', inputs: func.inputs, signature: func.signature, type: 'internal', constant: isConstant(func), payable: isPayable(func) }) + } + + for (const func of testFunctionsInterface) { + if (availableFunctions.includes('beforeEach')) { + const func = specialFunctionsInterface['beforeEach'] + runList.push({ name: 'beforeEach', inputs: func.inputs, signature: func.signature, type: 'internal', constant: isConstant(func), payable: isPayable(func) }) } - - for (const func of testFunctionsInterface) { - if (availableFunctions.includes('beforeEach')) { - const func = specialFunctionsInterface['beforeEach'] - runList.push({ name: 'beforeEach', inputs: func.inputs, signature: func.signature, type: 'internal', constant: isConstant(func), payable: isPayable(func) }) - } - if (func.name && func.inputs) runList.push({ name: func.name, inputs: func.inputs, signature: func.signature, type: 'test', constant: isConstant(func), payable: isPayable(func) }) - if (availableFunctions.indexOf('afterEach') >= 0) { - const func = specialFunctionsInterface['afterEach'] - runList.push({ name: 'afterEach', inputs: func.inputs, signature: func.signature, type: 'internal', constant: isConstant(func), payable: isPayable(func) }) - } + if (func.name && func.inputs) runList.push({ name: func.name, inputs: func.inputs, signature: func.signature, type: 'test', constant: isConstant(func), payable: isPayable(func) }) + if (availableFunctions.indexOf('afterEach') >= 0) { + const func = specialFunctionsInterface['afterEach'] + runList.push({ name: 'afterEach', inputs: func.inputs, signature: func.signature, type: 'internal', constant: isConstant(func), payable: isPayable(func) }) } + } - if (availableFunctions.indexOf('afterAll') >= 0) { - const func = specialFunctionsInterface['afterAll'] - runList.push({ name: 'afterAll', inputs: func.inputs, signature: func.signature, type: 'internal', constant: isConstant(func), payable: isPayable(func) }) - } + if (availableFunctions.indexOf('afterAll') >= 0) { + const func = specialFunctionsInterface['afterAll'] + runList.push({ name: 'afterAll', inputs: func.inputs, signature: func.signature, type: 'internal', constant: isConstant(func), payable: isPayable(func) }) + } - return runList + return runList } export function runTest (testName: string, testObject: any, contractDetails: CompiledContract, fileAST: AstNode, opts: Options, testCallback: TestCbInterface, resultsCallback: ResultCbInterface): void { - let passingNum = 0 - let failureNum = 0 - let timePassed = 0 - const isJSONInterfaceAvailable = testObject && testObject.options && testObject.options.jsonInterface - if (!isJSONInterfaceAvailable) { return resultsCallback(new Error('Contract interface not available'), { passingNum, failureNum, timePassed }) } - const runList: RunListInterface[] = createRunList(testObject.options.jsonInterface, fileAST, testName) - const web3 = opts.web3 || new Web3() - web3.eth.handleRevert = true // enables returning error reason on revert - const accts: TestResultInterface = { - type: 'accountList', - value: opts.accounts - } - testCallback(undefined, accts) - - const resp: TestResultInterface = { - type: 'contract', - value: testName, - filename: testObject.filename + let passingNum = 0 + let failureNum = 0 + let timePassed = 0 + const isJSONInterfaceAvailable = testObject && testObject.options && testObject.options.jsonInterface + if (!isJSONInterfaceAvailable) { return resultsCallback(new Error('Contract interface not available'), { passingNum, failureNum, timePassed }) } + const runList: RunListInterface[] = createRunList(testObject.options.jsonInterface, fileAST, testName) + const web3 = opts.web3 || new Web3() + web3.eth.handleRevert = true // enables returning error reason on revert + const accts: TestResultInterface = { + type: 'accountList', + value: opts.accounts + } + testCallback(undefined, accts) + + const resp: TestResultInterface = { + type: 'contract', + value: testName, + filename: testObject.filename + } + testCallback(undefined, resp) + async.eachOfLimit(runList, 1, function (func, index, next) { + let sender: string | null = null + let hhLogs + if (func.signature) { + sender = getOverridedSender(contractDetails.userdoc, func.signature, contractDetails.evm.methodIdentifiers) + if (opts.accounts && sender) { + sender = opts.accounts[sender] + } } - testCallback(undefined, resp) - async.eachOfLimit(runList, 1, function (func, index, next) { - let sender: string | null = null - let hhLogs - if (func.signature) { - sender = getOverridedSender(contractDetails.userdoc, func.signature, contractDetails.evm.methodIdentifiers) - if (opts.accounts && sender) { - sender = opts.accounts[sender] - } - } - let sendParams: Record | null = null - if (sender) sendParams = { from: sender } - if (func.inputs && func.inputs.length > 0) { return resultsCallback(new Error(`Method '${func.name}' can not have parameters inside a test contract`), { passingNum, failureNum, timePassed }) } - const method = testObject.methods[func.name].apply(testObject.methods[func.name], []) - const startTime = Date.now() - let debugTxHash:string - if (func.constant) { - sendParams = {} - const tagTimestamp = 'remix_tests_tag' + Date.now() - sendParams.timestamp = tagTimestamp - method.call(sendParams).then(async (result) => { - const time = (Date.now() - startTime) / 1000.0 - let tagTxHash - if (web3.eth && web3.eth.getHashFromTagBySimulator) tagTxHash = await web3.eth.getHashFromTagBySimulator(tagTimestamp) - if (web3.eth && web3.eth.getHHLogsForTx) hhLogs = await web3.eth.getHHLogsForTx(tagTxHash) - debugTxHash = tagTxHash - if (result) { - const resp: TestResultInterface = { - type: 'testPass', - value: changeCase.sentenceCase(func.name), - filename: testObject.filename, - time: time, - context: testName, - web3, - debugTxHash - } - if (hhLogs && hhLogs.length) resp.hhLogs = hhLogs - testCallback(undefined, resp) - passingNum += 1 - timePassed += time - } else { - const resp: TestResultInterface = { - type: 'testFailure', - value: changeCase.sentenceCase(func.name), - filename: testObject.filename, - time: time, - errMsg: 'function returned false', - context: testName, - web3, - debugTxHash - } - if (hhLogs && hhLogs.length) resp.hhLogs = hhLogs - testCallback(undefined, resp) - failureNum += 1 - timePassed += time - } - next() - }) + let sendParams: Record | null = null + if (sender) sendParams = { from: sender } + if (func.inputs && func.inputs.length > 0) { return resultsCallback(new Error(`Method '${func.name}' can not have parameters inside a test contract`), { passingNum, failureNum, timePassed }) } + const method = testObject.methods[func.name].apply(testObject.methods[func.name], []) + const startTime = Date.now() + let debugTxHash:string + if (func.constant) { + sendParams = {} + const tagTimestamp = 'remix_tests_tag' + Date.now() + sendParams.timestamp = tagTimestamp + method.call(sendParams).then(async (result) => { + const time = (Date.now() - startTime) / 1000.0 + let tagTxHash + if (web3.eth && web3.eth.getHashFromTagBySimulator) tagTxHash = await web3.eth.getHashFromTagBySimulator(tagTimestamp) + if (web3.eth && web3.eth.getHHLogsForTx) hhLogs = await web3.eth.getHHLogsForTx(tagTxHash) + debugTxHash = tagTxHash + if (result) { + const resp: TestResultInterface = { + type: 'testPass', + value: changeCase.sentenceCase(func.name), + filename: testObject.filename, + time: time, + context: testName, + web3, + debugTxHash + } + if (hhLogs && hhLogs.length) resp.hhLogs = hhLogs + testCallback(undefined, resp) + passingNum += 1 + timePassed += time } else { - if (func.payable) { - const value = getProvidedValue(contractDetails.userdoc, func.signature, contractDetails.evm.methodIdentifiers) - if (value) { - if (sendParams) sendParams.value = value - else sendParams = { value } - } - } - if (!sendParams) sendParams = {} - sendParams.gas = 10000000 * 8 - method.send(sendParams).on('receipt', async (receipt) => { - try { - debugTxHash = receipt.transactionHash - if (web3.eth && web3.eth.getHHLogsForTx) hhLogs = await web3.eth.getHHLogsForTx(receipt.transactionHash) - const time: number = (Date.now() - startTime) / 1000.0 - const assertionEventHashes = assertionEvents.map(e => Web3.utils.sha3(e.name + '(' + e.params.join() + ')')) - let testPassed = false - for (const i in receipt.events) { - let events = receipt.events[i] - if (!Array.isArray(events)) events = [events] - for (const event of events) { - const eIndex = assertionEventHashes.indexOf(event.raw.topics[0]) // event name topic will always be at index 0 - if (eIndex >= 0) { - const testEvent = web3.eth.abi.decodeParameters(assertionEvents[eIndex].params, event.raw.data) - if (!testEvent[0]) { - const assertMethod = testEvent[2] - if (assertMethod === 'ok') { // for 'Assert.ok' method - testEvent[3] = 'false' - testEvent[4] = 'true' - } - const location = getAssertMethodLocation(fileAST, testName, func.name, assertMethod) - const resp: TestResultInterface = { - type: 'testFailure', - value: changeCase.sentenceCase(func.name), - filename: testObject.filename, - time: time, - errMsg: testEvent[1], - context: testName, - assertMethod, - returned: testEvent[3], - expected: testEvent[4], - location, - web3, - debugTxHash - } - if (hhLogs && hhLogs.length) resp.hhLogs = hhLogs - testCallback(undefined, resp) - failureNum += 1 - timePassed += time - return next() - } - testPassed = true - } - } - } - - if (testPassed) { - const resp: TestResultInterface = { - type: 'testPass', - value: changeCase.sentenceCase(func.name), - filename: testObject.filename, - time: time, - context: testName, - web3, - debugTxHash - } - if (hhLogs && hhLogs.length) resp.hhLogs = hhLogs - testCallback(undefined, resp) - passingNum += 1 - timePassed += time - } else if (hhLogs && hhLogs.length) { - const resp: TestResultInterface = { - type: 'logOnly', - value: changeCase.sentenceCase(func.name), - filename: testObject.filename, - time: time, - context: testName, - hhLogs - } - testCallback(undefined, resp) - timePassed += time - } - - return next() - } catch (err) { - console.error(err) - return next(err) - } - }).on('error', async (err) => { - const time: number = (Date.now() - startTime) / 1000.0 - let errMsg = err.message - let txHash - if (err.reason) errMsg = `transaction reverted with the reason: ${err.reason}` - const resp: TestResultInterface = { + const resp: TestResultInterface = { + type: 'testFailure', + value: changeCase.sentenceCase(func.name), + filename: testObject.filename, + time: time, + errMsg: 'function returned false', + context: testName, + web3, + debugTxHash + } + if (hhLogs && hhLogs.length) resp.hhLogs = hhLogs + testCallback(undefined, resp) + failureNum += 1 + timePassed += time + } + next() + }) + } else { + if (func.payable) { + const value = getProvidedValue(contractDetails.userdoc, func.signature, contractDetails.evm.methodIdentifiers) + if (value) { + if (sendParams) sendParams.value = value + else sendParams = { value } + } + } + if (!sendParams) sendParams = {} + sendParams.gas = 10000000 * 8 + method.send(sendParams).on('receipt', async (receipt) => { + try { + debugTxHash = receipt.transactionHash + if (web3.eth && web3.eth.getHHLogsForTx) hhLogs = await web3.eth.getHHLogsForTx(receipt.transactionHash) + const time: number = (Date.now() - startTime) / 1000.0 + const assertionEventHashes = assertionEvents.map(e => Web3.utils.sha3(e.name + '(' + e.params.join() + ')')) + let testPassed = false + for (const i in receipt.events) { + let events = receipt.events[i] + if (!Array.isArray(events)) events = [events] + for (const event of events) { + const eIndex = assertionEventHashes.indexOf(event.raw.topics[0]) // event name topic will always be at index 0 + if (eIndex >= 0) { + const testEvent = web3.eth.abi.decodeParameters(assertionEvents[eIndex].params, event.raw.data) + if (!testEvent[0]) { + const assertMethod = testEvent[2] + if (assertMethod === 'ok') { // for 'Assert.ok' method + testEvent[3] = 'false' + testEvent[4] = 'true' + } + const location = getAssertMethodLocation(fileAST, testName, func.name, assertMethod) + const resp: TestResultInterface = { type: 'testFailure', value: changeCase.sentenceCase(func.name), filename: testObject.filename, time: time, - errMsg, + errMsg: testEvent[1], context: testName, - web3 - } - if (err.receipt) txHash = err.receipt.transactionHash - else if (err.message.includes('Transaction has been reverted by the EVM')) { - txHash = JSON.parse(err.message.replace('Transaction has been reverted by the EVM:', '')).transactionHash + assertMethod, + returned: testEvent[3], + expected: testEvent[4], + location, + web3, + debugTxHash + } + if (hhLogs && hhLogs.length) resp.hhLogs = hhLogs + testCallback(undefined, resp) + failureNum += 1 + timePassed += time + return next() } - if (web3.eth && web3.eth.getHHLogsForTx && txHash) hhLogs = await web3.eth.getHHLogsForTx(txHash) - if (hhLogs && hhLogs.length) resp.hhLogs = hhLogs - resp.debugTxHash = txHash - testCallback(undefined, resp) - failureNum += 1 - timePassed += time - return next() - }) + testPassed = true + } + } + } + + if (testPassed) { + const resp: TestResultInterface = { + type: 'testPass', + value: changeCase.sentenceCase(func.name), + filename: testObject.filename, + time: time, + context: testName, + web3, + debugTxHash + } + if (hhLogs && hhLogs.length) resp.hhLogs = hhLogs + testCallback(undefined, resp) + passingNum += 1 + timePassed += time + } else if (hhLogs && hhLogs.length) { + const resp: TestResultInterface = { + type: 'logOnly', + value: changeCase.sentenceCase(func.name), + filename: testObject.filename, + time: time, + context: testName, + hhLogs + } + testCallback(undefined, resp) + timePassed += time + } + + return next() + } catch (err) { + console.error(err) + return next(err) } - }, function (error) { - resultsCallback(error, { passingNum, failureNum, timePassed }) - }) + }).on('error', async (err) => { + const time: number = (Date.now() - startTime) / 1000.0 + let errMsg = err.message + let txHash + if (err.reason) errMsg = `transaction reverted with the reason: ${err.reason}` + const resp: TestResultInterface = { + type: 'testFailure', + value: changeCase.sentenceCase(func.name), + filename: testObject.filename, + time: time, + errMsg, + context: testName, + web3 + } + if (err.receipt) txHash = err.receipt.transactionHash + else if (err.message.includes('Transaction has been reverted by the EVM')) { + txHash = JSON.parse(err.message.replace('Transaction has been reverted by the EVM:', '')).transactionHash + } + if (web3.eth && web3.eth.getHHLogsForTx && txHash) hhLogs = await web3.eth.getHHLogsForTx(txHash) + if (hhLogs && hhLogs.length) resp.hhLogs = hhLogs + resp.debugTxHash = txHash + testCallback(undefined, resp) + failureNum += 1 + timePassed += time + return next() + }) + } + }, function (error) { + resultsCallback(error, { passingNum, failureNum, timePassed }) + }) } diff --git a/libs/remix-tests/src/types.ts b/libs/remix-tests/src/types.ts index fb03e94887..2dd50aed00 100644 --- a/libs/remix-tests/src/types.ts +++ b/libs/remix-tests/src/types.ts @@ -67,12 +67,12 @@ export interface CompilationErrors { } // eslint-disable-next-line no-redeclare export class CompilationErrors extends Error { - constructor (errors: Array) { - const mapError = errors.map((e) => { return e.formattedMessage || e.message }) - super(mapError.join('\n')) - this.errors = errors - this.name = 'CompilationErrors' - } + constructor (errors: Array) { + const mapError = errors.map((e) => { return e.formattedMessage || e.message }) + super(mapError.join('\n')) + this.errors = errors + this.name = 'CompilationErrors' + } } /** sources object with name of the file and content **/ diff --git a/libs/remix-tests/tests/testRunner.cli.spec.ts b/libs/remix-tests/tests/testRunner.cli.spec.ts index 8ac5505f10..3bdd68fa60 100644 --- a/libs/remix-tests/tests/testRunner.cli.spec.ts +++ b/libs/remix-tests/tests/testRunner.cli.spec.ts @@ -3,35 +3,35 @@ import { resolve } from 'path' import { expect } from 'chai'; describe('testRunner: remix-tests CLI', function(){ - this.timeout(120000) - // remix-tests binary, after build, is used as executable + this.timeout(120000) + // remix-tests binary, after build, is used as executable - const executablePath = resolve(__dirname + '/../../../dist/libs/remix-tests/bin/remix-tests') + const executablePath = resolve(__dirname + '/../../../dist/libs/remix-tests/bin/remix-tests') - const result = spawnSync('ls', { cwd: resolve(__dirname + '/../../../dist/libs/remix-tests') }) - if(result) { - const dirContent = result.stdout.toString() - // Install dependencies if 'node_modules' is not already present - if(!dirContent.includes('node_modules')) { - execSync('yarn add @remix-project/remix-lib ../../libs/remix-lib', { cwd: resolve(__dirname + '/../../../dist/libs/remix-tests') }) - execSync('yarn add @remix-project/remix-url-resolver ../../libs/remix-url-resolver', { cwd: resolve(__dirname + '/../../../dist/libs/remix-tests') }) - execSync('yarn add @remix-project/remix-solidity ../../libs/remix-solidity', { cwd: resolve(__dirname + '/../../../dist/libs/remix-tests') }) - execSync('yarn add @remix-project/remix-simulator ../../libs/remix-simulator', { cwd: resolve(__dirname + '/../../../dist/libs/remix-tests') }) - execSync('yarn install', { cwd: resolve(__dirname + '/../../../dist/libs/remix-tests') }) - } + const result = spawnSync('ls', { cwd: resolve(__dirname + '/../../../dist/libs/remix-tests') }) + if(result) { + const dirContent = result.stdout.toString() + // Install dependencies if 'node_modules' is not already present + if(!dirContent.includes('node_modules')) { + execSync('yarn add @remix-project/remix-lib ../../libs/remix-lib', { cwd: resolve(__dirname + '/../../../dist/libs/remix-tests') }) + execSync('yarn add @remix-project/remix-url-resolver ../../libs/remix-url-resolver', { cwd: resolve(__dirname + '/../../../dist/libs/remix-tests') }) + execSync('yarn add @remix-project/remix-solidity ../../libs/remix-solidity', { cwd: resolve(__dirname + '/../../../dist/libs/remix-tests') }) + execSync('yarn add @remix-project/remix-simulator ../../libs/remix-simulator', { cwd: resolve(__dirname + '/../../../dist/libs/remix-tests') }) + execSync('yarn install', { cwd: resolve(__dirname + '/../../../dist/libs/remix-tests') }) } + } - describe('test various CLI options', function() { - it('remix-tests version', () => { - const res = spawnSync(executablePath, ['-V']) - // eslint-disable-next-line @typescript-eslint/no-var-requires - expect(res.stdout.toString().trim()).to.equal(require('../package.json').version) - }) + describe('test various CLI options', function() { + it('remix-tests version', () => { + const res = spawnSync(executablePath, ['-V']) + // eslint-disable-next-line @typescript-eslint/no-var-requires + expect(res.stdout.toString().trim()).to.equal(require('../package.json').version) + }) - it('remix-tests help', () => { - const res = spawnSync(executablePath, ['-h']) - const expectedHelp = `Usage: remix-tests [options] [command] + it('remix-tests help', () => { + const res = spawnSync(executablePath, ['-h']) + const expectedHelp = `Usage: remix-tests [options] [command] Arguments: file_path path to test file or directory @@ -55,112 +55,112 @@ Options: Commands: version output the version number help output usage information` - expect(res.stdout.toString().trim()).to.equal(expectedHelp) - }) + expect(res.stdout.toString().trim()).to.equal(expectedHelp) + }) - it('remix-tests running a test file', function() { - const res = spawnSync(executablePath, [resolve(__dirname + '/examples_0/assert_ok_test.sol')]) - //console.log(res.stdout.toString()) - // match initial lines - expect(res.stdout.toString().trim()).to.match(/:: Running tests using remix-tests ::/) - expect(res.stdout.toString().trim()).to.match(/creation of library remix_tests.sol:Assert pending.../) - // match test result - expect(res.stdout.toString().trim()).to.match(/AssertOkTest/) - expect(res.stdout.toString().trim()).to.match(/AssertOkTest okPassTest/) // check if console.log is printed - expect(res.stdout.toString().trim()).to.match(/Ok pass test/) - expect(res.stdout.toString().trim()).to.match(/AssertOkTest okFailTest/) // check if console.log is printed - expect(res.stdout.toString().trim()).to.match(/Ok fail test/) - // match fail test details - expect(res.stdout.toString().trim()).to.match(/Expected value should be ok to: true/) - expect(res.stdout.toString().trim()).to.match(/Received: false/) - expect(res.stdout.toString().trim()).to.match(/Message: okFailTest fails/) + it('remix-tests running a test file', function() { + const res = spawnSync(executablePath, [resolve(__dirname + '/examples_0/assert_ok_test.sol')]) + //console.log(res.stdout.toString()) + // match initial lines + expect(res.stdout.toString().trim()).to.match(/:: Running tests using remix-tests ::/) + expect(res.stdout.toString().trim()).to.match(/creation of library remix_tests.sol:Assert pending.../) + // match test result + expect(res.stdout.toString().trim()).to.match(/AssertOkTest/) + expect(res.stdout.toString().trim()).to.match(/AssertOkTest okPassTest/) // check if console.log is printed + expect(res.stdout.toString().trim()).to.match(/Ok pass test/) + expect(res.stdout.toString().trim()).to.match(/AssertOkTest okFailTest/) // check if console.log is printed + expect(res.stdout.toString().trim()).to.match(/Ok fail test/) + // match fail test details + expect(res.stdout.toString().trim()).to.match(/Expected value should be ok to: true/) + expect(res.stdout.toString().trim()).to.match(/Received: false/) + expect(res.stdout.toString().trim()).to.match(/Message: okFailTest fails/) - }) + }) - it('remix-tests running a test file with custom compiler version', () => { - const res = spawnSync(executablePath, ['--compiler', '0.7.4', resolve(__dirname + '/examples_0/assert_ok_test.sol')]) - // match initial lines - expect(res.stdout.toString().trim()).to.contain('Compiler version set to 0.7.4. Latest version is') - expect(res.stdout.toString().trim()).to.contain('Loading remote solc version v0.7.4+commit.3f05b770 ...') - expect(res.stdout.toString().trim()).to.match(/:: Running tests using remix-tests ::/) - expect(res.stdout.toString().trim()).to.match(/creation of library remix_tests.sol:Assert pending.../) - // match test result - expect(res.stdout.toString().trim()).to.match(/Ok pass test/) - expect(res.stdout.toString().trim()).to.match(/Ok fail test/) - // match fail test details - expect(res.stdout.toString().trim()).to.match(/Message: okFailTest fails/) - }) + it('remix-tests running a test file with custom compiler version', () => { + const res = spawnSync(executablePath, ['--compiler', '0.7.4', resolve(__dirname + '/examples_0/assert_ok_test.sol')]) + // match initial lines + expect(res.stdout.toString().trim()).to.contain('Compiler version set to 0.7.4. Latest version is') + expect(res.stdout.toString().trim()).to.contain('Loading remote solc version v0.7.4+commit.3f05b770 ...') + expect(res.stdout.toString().trim()).to.match(/:: Running tests using remix-tests ::/) + expect(res.stdout.toString().trim()).to.match(/creation of library remix_tests.sol:Assert pending.../) + // match test result + expect(res.stdout.toString().trim()).to.match(/Ok pass test/) + expect(res.stdout.toString().trim()).to.match(/Ok fail test/) + // match fail test details + expect(res.stdout.toString().trim()).to.match(/Message: okFailTest fails/) + }) - it('remix-tests running a test file with unavailable custom compiler version (should fail)', () => { - const res = spawnSync(executablePath, ['--compiler', '1.10.4', resolve(__dirname + '/examples_0/assert_ok_test.sol')]) - // match initial lines - expect(res.stdout.toString().trim()).to.contain('No compiler found in releases with version 1.10.4') - }) + it('remix-tests running a test file with unavailable custom compiler version (should fail)', () => { + const res = spawnSync(executablePath, ['--compiler', '1.10.4', resolve(__dirname + '/examples_0/assert_ok_test.sol')]) + // match initial lines + expect(res.stdout.toString().trim()).to.contain('No compiler found in releases with version 1.10.4') + }) - it('remix-tests running a test file with custom EVM', () => { - const res = spawnSync(executablePath, ['--evm', 'petersburg', resolve(__dirname + '/examples_0/assert_ok_test.sol')]) - // match initial lines - expect(res.stdout.toString().trim()).to.contain('EVM set to petersburg') - expect(res.stdout.toString().trim()).to.match(/:: Running tests using remix-tests ::/) - expect(res.stdout.toString().trim()).to.match(/creation of library remix_tests.sol:Assert pending.../) - // match test result - expect(res.stdout.toString().trim()).to.match(/Ok pass test/) - expect(res.stdout.toString().trim()).to.match(/Ok fail test/) - // match fail test details - expect(res.stdout.toString().trim()).to.match(/Message: okFailTest fails/) - }) + it('remix-tests running a test file with custom EVM', () => { + const res = spawnSync(executablePath, ['--evm', 'petersburg', resolve(__dirname + '/examples_0/assert_ok_test.sol')]) + // match initial lines + expect(res.stdout.toString().trim()).to.contain('EVM set to petersburg') + expect(res.stdout.toString().trim()).to.match(/:: Running tests using remix-tests ::/) + expect(res.stdout.toString().trim()).to.match(/creation of library remix_tests.sol:Assert pending.../) + // match test result + expect(res.stdout.toString().trim()).to.match(/Ok pass test/) + expect(res.stdout.toString().trim()).to.match(/Ok fail test/) + // match fail test details + expect(res.stdout.toString().trim()).to.match(/Message: okFailTest fails/) + }) - it('remix-tests running a test file by enabling optimization', () => { - const res = spawnSync(executablePath, ['--optimize', 'true', resolve(__dirname + '/examples_0/assert_ok_test.sol')]) - // match initial lines - expect(res.stdout.toString().trim().includes('Optimization is enabled')) - expect(res.stdout.toString().trim()).to.match(/:: Running tests using remix-tests ::/) - expect(res.stdout.toString().trim()).to.match(/creation of library remix_tests.sol:Assert pending.../) - // match test result - expect(res.stdout.toString().trim()).to.match(/Ok pass test/) - expect(res.stdout.toString().trim()).to.match(/Ok fail test/) - // match fail test details - expect(res.stdout.toString().trim()).to.match(/Message: okFailTest fails/) - }) + it('remix-tests running a test file by enabling optimization', () => { + const res = spawnSync(executablePath, ['--optimize', 'true', resolve(__dirname + '/examples_0/assert_ok_test.sol')]) + // match initial lines + expect(res.stdout.toString().trim().includes('Optimization is enabled')) + expect(res.stdout.toString().trim()).to.match(/:: Running tests using remix-tests ::/) + expect(res.stdout.toString().trim()).to.match(/creation of library remix_tests.sol:Assert pending.../) + // match test result + expect(res.stdout.toString().trim()).to.match(/Ok pass test/) + expect(res.stdout.toString().trim()).to.match(/Ok fail test/) + // match fail test details + expect(res.stdout.toString().trim()).to.match(/Message: okFailTest fails/) + }) - it('remix-tests running a test file by enabling optimization and setting runs', () => { - const res = spawnSync(executablePath, ['--optimize', 'true', '--runs', '300', resolve(__dirname + '/examples_0/assert_ok_test.sol')]) - // match initial lines - expect(res.stdout.toString().trim()).to.contain('Optimization is enabled') - expect(res.stdout.toString().trim()).to.contain('Runs set to 300') - expect(res.stdout.toString().trim()).to.match(/:: Running tests using remix-tests ::/) - expect(res.stdout.toString().trim()).to.match(/creation of library remix_tests.sol:Assert pending.../) - // match test result - expect(res.stdout.toString().trim()).to.match(/Ok pass test/) - expect(res.stdout.toString().trim()).to.match(/Ok fail test/) - // match fail test details - expect(res.stdout.toString().trim()).to.match(/Message: okFailTest fails/) - }) + it('remix-tests running a test file by enabling optimization and setting runs', () => { + const res = spawnSync(executablePath, ['--optimize', 'true', '--runs', '300', resolve(__dirname + '/examples_0/assert_ok_test.sol')]) + // match initial lines + expect(res.stdout.toString().trim()).to.contain('Optimization is enabled') + expect(res.stdout.toString().trim()).to.contain('Runs set to 300') + expect(res.stdout.toString().trim()).to.match(/:: Running tests using remix-tests ::/) + expect(res.stdout.toString().trim()).to.match(/creation of library remix_tests.sol:Assert pending.../) + // match test result + expect(res.stdout.toString().trim()).to.match(/Ok pass test/) + expect(res.stdout.toString().trim()).to.match(/Ok fail test/) + // match fail test details + expect(res.stdout.toString().trim()).to.match(/Message: okFailTest fails/) + }) - it('remix-tests running a test file without enabling optimization and setting runs (should fail)', () => { - const res = spawnSync(executablePath, ['--runs', '300', resolve(__dirname + '/examples_0/assert_ok_test.sol')]) - // match initial lines - expect(res.stdout.toString().trim()).to.contain('Optimization should be enabled for runs') - }) + it('remix-tests running a test file without enabling optimization and setting runs (should fail)', () => { + const res = spawnSync(executablePath, ['--runs', '300', resolve(__dirname + '/examples_0/assert_ok_test.sol')]) + // match initial lines + expect(res.stdout.toString().trim()).to.contain('Optimization should be enabled for runs') + }) - it('remix-tests running a test file with all options', () => { - const res = spawnSync(executablePath, ['--compiler', '0.7.5', '--evm', 'istanbul', '--optimize', 'true', '--runs', '250', resolve(__dirname + '/examples_0/assert_ok_test.sol')]) - // match initial lines - expect(res.stdout.toString().trim()).to.contain('Compiler version set to 0.7.5. Latest version is') - expect(res.stdout.toString().trim()).to.contain('Loading remote solc version v0.7.5+commit.eb77ed08 ...') - expect(res.stdout.toString().trim()).to.contain('EVM set to istanbul') - expect(res.stdout.toString().trim()).to.contain('Optimization is enabled') - expect(res.stdout.toString().trim()).to.contain('Runs set to 250') - expect(res.stdout.toString().trim()).to.match(/:: Running tests using remix-tests ::/) - expect(res.stdout.toString().trim()).to.match(/creation of library remix_tests.sol:Assert pending.../) - // match test result - expect(res.stdout.toString().trim()).to.match(/Ok pass test/) - expect(res.stdout.toString().trim()).to.match(/Ok fail test/) - // match fail test details - expect(res.stdout.toString().trim()).to.match(/Message: okFailTest fails/) - }) - + it('remix-tests running a test file with all options', () => { + const res = spawnSync(executablePath, ['--compiler', '0.7.5', '--evm', 'istanbul', '--optimize', 'true', '--runs', '250', resolve(__dirname + '/examples_0/assert_ok_test.sol')]) + // match initial lines + expect(res.stdout.toString().trim()).to.contain('Compiler version set to 0.7.5. Latest version is') + expect(res.stdout.toString().trim()).to.contain('Loading remote solc version v0.7.5+commit.eb77ed08 ...') + expect(res.stdout.toString().trim()).to.contain('EVM set to istanbul') + expect(res.stdout.toString().trim()).to.contain('Optimization is enabled') + expect(res.stdout.toString().trim()).to.contain('Runs set to 250') + expect(res.stdout.toString().trim()).to.match(/:: Running tests using remix-tests ::/) + expect(res.stdout.toString().trim()).to.match(/creation of library remix_tests.sol:Assert pending.../) + // match test result + expect(res.stdout.toString().trim()).to.match(/Ok pass test/) + expect(res.stdout.toString().trim()).to.match(/Ok fail test/) + // match fail test details + expect(res.stdout.toString().trim()).to.match(/Message: okFailTest fails/) }) + + }) }) \ No newline at end of file diff --git a/libs/remix-tests/tests/testRunner.spec.ts b/libs/remix-tests/tests/testRunner.spec.ts index cbdc1c4aa1..add8a330c9 100644 --- a/libs/remix-tests/tests/testRunner.spec.ts +++ b/libs/remix-tests/tests/testRunner.spec.ts @@ -14,476 +14,476 @@ import { ResultsInterface, TestCbInterface, ResultCbInterface } from '../src/ind // In this specific test, we'll use this helper to exclude `time` keys. // Assertions for the existance of these will be made at the correct places. function deepEqualExcluding(a: any, b: any, excludedKeys: string[]) { - function removeKeysFromObject(obj: any, excludedKeys: string[]) { - if (obj !== Object(obj)) { - return obj - } - - if (Object.prototype.toString.call(obj) !== '[object Array]') { - obj = Object.assign({}, obj) - for (const key of excludedKeys) { - delete obj[key] - } + function removeKeysFromObject(obj: any, excludedKeys: string[]) { + if (obj !== Object(obj)) { + return obj + } - return obj - } + if (Object.prototype.toString.call(obj) !== '[object Array]') { + obj = Object.assign({}, obj) + for (const key of excludedKeys) { + delete obj[key] + } - const newObj = [] - for (const idx in obj) { - newObj[idx] = removeKeysFromObject(obj[idx], excludedKeys); - } + return obj + } - return newObj + const newObj = [] + for (const idx in obj) { + newObj[idx] = removeKeysFromObject(obj[idx], excludedKeys); } - const aStripped: any = removeKeysFromObject(a, excludedKeys); - const bStripped: any = removeKeysFromObject(b, excludedKeys); - assert.deepEqual(aStripped, bStripped) + return newObj + } + + const aStripped: any = removeKeysFromObject(a, excludedKeys); + const bStripped: any = removeKeysFromObject(b, excludedKeys); + assert.deepEqual(aStripped, bStripped) } let accounts: string[] const provider: any = new Provider() async function compileAndDeploy(filename: string, callback: any) { - const web3: Web3 = new Web3() - const sourceASTs: any = {} - await provider.init() - web3.setProvider(provider) - extend(web3) - let compilationData: any - async.waterfall([ - function getAccountList(next: any): void { - web3.eth.getAccounts((_err: Error | null | undefined, _accounts: string[]) => { - accounts = _accounts - web3.eth.defaultAccount = accounts[0] - next(_err) - }) - }, - function compile(next: any): void { - compileFileOrFiles(filename, false, { accounts }, null, next) - }, - function deployAllContracts(compilationResult: compilationInterface, asts, next: any): void { - for (const filename in asts) { - if (filename.endsWith('_test.sol')) - sourceASTs[filename] = asts[filename].ast - } - // eslint-disable-next-line no-useless-catch - try { - compilationData = compilationResult - deployAll(compilationResult, web3, accounts, false, null, next) - } catch (e) { - throw e - } - } - ], function (_err: Error | null | undefined, contracts: any): void { - callback(null, compilationData, contracts, sourceASTs, accounts, web3) - }) + const web3: Web3 = new Web3() + const sourceASTs: any = {} + await provider.init() + web3.setProvider(provider) + extend(web3) + let compilationData: any + async.waterfall([ + function getAccountList(next: any): void { + web3.eth.getAccounts((_err: Error | null | undefined, _accounts: string[]) => { + accounts = _accounts + web3.eth.defaultAccount = accounts[0] + next(_err) + }) + }, + function compile(next: any): void { + compileFileOrFiles(filename, false, { accounts }, null, next) + }, + function deployAllContracts(compilationResult: compilationInterface, asts, next: any): void { + for (const filename in asts) { + if (filename.endsWith('_test.sol')) + sourceASTs[filename] = asts[filename].ast + } + // eslint-disable-next-line no-useless-catch + try { + compilationData = compilationResult + deployAll(compilationResult, web3, accounts, false, null, next) + } catch (e) { + throw e + } + } + ], function (_err: Error | null | undefined, contracts: any): void { + callback(null, compilationData, contracts, sourceASTs, accounts, web3) + }) } describe('testRunner', function () { - let tests: any[] = [], results: ResultsInterface; + let tests: any[] = [], results: ResultsInterface; - const testCallback: TestCbInterface = (err, test) => { - if (err) { throw err } + const testCallback: TestCbInterface = (err, test) => { + if (err) { throw err } - if (test.type === 'testPass' || test.type === 'testFailure') { - assert.ok(test.time, 'test time not reported') - assert.ok(!Number.isInteger(test.time || 0), 'test time should not be an integer') - } - - tests.push(test) + if (test.type === 'testPass' || test.type === 'testFailure') { + assert.ok(test.time, 'test time not reported') + assert.ok(!Number.isInteger(test.time || 0), 'test time should not be an integer') } - const resultsCallback = (done) => { - return (err, _results) => { - if (err) { throw err } - results = _results - done() - } + tests.push(test) + } + + const resultsCallback = (done) => { + return (err, _results) => { + if (err) { throw err } + results = _results + done() } + } - describe('#runTest', function () { - this.timeout(10000) - describe('assert library OK method tests', () => { - const filename: string = __dirname + '/examples_0/assert_ok_test.sol' - - before((done) => { - compileAndDeploy(filename, (_err: Error | null | undefined, compilationData: any, contracts: any, asts: any, accounts: string[], web3: any) => { - runTest('AssertOkTest', contracts.AssertOkTest, compilationData[filename]['AssertOkTest'], asts[filename], { accounts, web3 }, testCallback, resultsCallback(done)) - }) - }) - - after(() => { tests = [] }) - - it('should have 1 passing test', () => { - assert.equal(results.passingNum, 1) - }) - - it('should have 1 failing test', () => { - assert.equal(results.failureNum, 1) - }) - - const hhLogs1 = [["AssertOkTest", "okPassTest"]] - const hhLogs2 = [["AssertOkTest", "okFailTest"]] - it('should return', () => { - deepEqualExcluding(tests, [ - { type: 'accountList', value: accounts }, - { type: 'contract', value: 'AssertOkTest', filename: __dirname + '/examples_0/assert_ok_test.sol' }, - { type: 'testPass', debugTxHash: '0x5b665752a4faf83229259b9b2811d3295be0af633b0051d4b90042283ef55707', value: 'Ok pass test', filename: __dirname + '/examples_0/assert_ok_test.sol', context: 'AssertOkTest', hhLogs: hhLogs1 }, - { type: 'testFailure', debugTxHash: '0xa0a30ad042a7fc3495f72be7ba788d705888ffbbec7173f60bb27e07721510f2', value: 'Ok fail test', filename: __dirname + '/examples_0/assert_ok_test.sol', errMsg: 'okFailTest fails', context: 'AssertOkTest', hhLogs: hhLogs2, assertMethod: 'ok', location: '366:36:0', expected: 'true', returned: 'false' }, - - ], ['time', 'web3']) - }) - }) + describe('#runTest', function () { + this.timeout(10000) + describe('assert library OK method tests', () => { + const filename: string = __dirname + '/examples_0/assert_ok_test.sol' - describe('assert library EQUAL method tests', function () { - const filename: string = __dirname + '/examples_0/assert_equal_test.sol' - - before((done) => { - compileAndDeploy(filename, (_err: Error | null | undefined, compilationData: any, contracts: any, asts: any, accounts: string[], web3: any) => { - runTest('AssertEqualTest', contracts.AssertEqualTest, compilationData[filename]['AssertEqualTest'], asts[filename], { accounts }, testCallback, resultsCallback(done)) - }) - }) - - after(() => { tests = [] }) - - it('should have 6 passing test', () => { - assert.equal(results.passingNum, 6) - }) - - it('should have 6 failing test', () => { - assert.equal(results.failureNum, 6) - }) - - it('should return', () => { - deepEqualExcluding(tests, [ - { type: 'accountList', value: accounts }, - { type: 'contract', value: 'AssertEqualTest', filename: __dirname + '/examples_0/assert_equal_test.sol' }, - { type: 'testPass', debugTxHash: '0x233b8d91f0fa068b1a4deae1141178bc3eb79c3d2a6786160595a358363a157c', value: 'Equal uint pass test', filename: __dirname + '/examples_0/assert_equal_test.sol', context: 'AssertEqualTest' }, - { type: 'testFailure', debugTxHash: '0xa5e39c78663c2e5071c08467047ba5b2650d16081b50369700d46d7f90c4d94b', value: 'Equal uint fail test', filename: __dirname + '/examples_0/assert_equal_test.sol', errMsg: 'equalUintFailTest fails', context: 'AssertEqualTest', assertMethod: 'equal', location: '273:57:0', expected: '2', returned: '1' }, - { type: 'testPass', debugTxHash: '0x57af51c2c19db390a4ccf72fa3d32347fb3d998e70820909c7876bd8ccebf8a3', value: 'Equal int pass test', filename: __dirname + '/examples_0/assert_equal_test.sol', context: 'AssertEqualTest' }, - { type: 'testFailure', debugTxHash: '0x710f3a54a561c009fcf0277273b8fe337b2c493e9e83e0ae02786d487339ca7b', value: 'Equal int fail test', filename: __dirname + '/examples_0/assert_equal_test.sol', errMsg: 'equalIntFailTest fails', context: 'AssertEqualTest', assertMethod: 'equal', location: '493:45:0', expected: '2', returned: '-1' }, - { type: 'testPass', debugTxHash: '0x10c1ed8651110ad5de6adcad8e1284aa5c1fd3a998a1e863bbecc0ec855fcd7b', value: 'Equal bool pass test', filename: __dirname + '/examples_0/assert_equal_test.sol', context: 'AssertEqualTest' }, - { type: 'testFailure', debugTxHash: '0x004871a82968f43e02278eab9dd3d7eb0bbe88b64d459efa50065e5996fe5fad', value: 'Equal bool fail test', filename: __dirname + '/examples_0/assert_equal_test.sol', errMsg: 'equalBoolFailTest fails', context: 'AssertEqualTest', assertMethod: 'equal', location: '708:52:0', expected: false, returned: true }, - { type: 'testPass', debugTxHash: '0x64a4d4853ab7907712912cf2120ac2bfd2e08b4767b375250f0e907757546454', value: 'Equal address pass test', filename: __dirname + '/examples_0/assert_equal_test.sol', context: 'AssertEqualTest' }, - { type: 'testFailure', debugTxHash: '0xcf62fb76e3b2eb95d92aa2671a9e81e30fefb944f55e2fb8b97096c45fc74a38', value: 'Equal address fail test', filename: __dirname + '/examples_0/assert_equal_test.sol', errMsg: 'equalAddressFailTest fails', context: 'AssertEqualTest', assertMethod: 'equal', location: '1015:130:0', expected: '0x1c6637567229159d1eFD45f95A6675e77727E013', returned: '0x7994f14563F39875a2F934Ce42cAbF48a93FdDA9' }, - { type: 'testPass', debugTxHash: '0x18ef613acc128a21282e09cf920b32ef3be648bb35c0299471ddbbbeeb0faf8c', value: 'Equal bytes32 pass test', filename: __dirname + '/examples_0/assert_equal_test.sol', context: 'AssertEqualTest' }, - { type: 'testFailure', debugTxHash: '0x86fbf2f14e13d228f80a87a947841270d8c55073adddf78e8d4e2ba05d724ec6', value: 'Equal bytes32 fail test', filename: __dirname + '/examples_0/assert_equal_test.sol', errMsg: 'equalBytes32FailTest fails', context: 'AssertEqualTest', assertMethod: 'equal', location: '1670:48:0', expected: '0x72656d6978000000000000000000000000000000000000000000000000000000', returned: '0x72656d6979000000000000000000000000000000000000000000000000000000' }, - { type: 'testPass', debugTxHash: '0x80b3465f2504b74359790baa009237ba066685b24afa65a31814f1ad1bc4f99f', value: 'Equal string pass test', filename: __dirname + '/examples_0/assert_equal_test.sol', context: 'AssertEqualTest' }, - { type: 'testFailure', debugTxHash: '0x88b035a85c5f87f54a805334817f3e4599b4190d98f25947fe14d7804facd8b7', value: 'Equal string fail test', filename: __dirname + '/examples_0/assert_equal_test.sol', errMsg: 'equalStringFailTest fails', context: 'AssertEqualTest', assertMethod: 'equal', location: '1916:81:0', expected: 'remix-tests', returned: 'remix' } - ], ['time', 'web3']) - }) + before((done) => { + compileAndDeploy(filename, (_err: Error | null | undefined, compilationData: any, contracts: any, asts: any, accounts: string[], web3: any) => { + runTest('AssertOkTest', contracts.AssertOkTest, compilationData[filename]['AssertOkTest'], asts[filename], { accounts, web3 }, testCallback, resultsCallback(done)) }) + }) + + after(() => { tests = [] }) + + it('should have 1 passing test', () => { + assert.equal(results.passingNum, 1) + }) + it('should have 1 failing test', () => { + assert.equal(results.failureNum, 1) + }) + const hhLogs1 = [["AssertOkTest", "okPassTest"]] + const hhLogs2 = [["AssertOkTest", "okFailTest"]] + it('should return', () => { + deepEqualExcluding(tests, [ + { type: 'accountList', value: accounts }, + { type: 'contract', value: 'AssertOkTest', filename: __dirname + '/examples_0/assert_ok_test.sol' }, + { type: 'testPass', debugTxHash: '0x5b665752a4faf83229259b9b2811d3295be0af633b0051d4b90042283ef55707', value: 'Ok pass test', filename: __dirname + '/examples_0/assert_ok_test.sol', context: 'AssertOkTest', hhLogs: hhLogs1 }, + { type: 'testFailure', debugTxHash: '0xa0a30ad042a7fc3495f72be7ba788d705888ffbbec7173f60bb27e07721510f2', value: 'Ok fail test', filename: __dirname + '/examples_0/assert_ok_test.sol', errMsg: 'okFailTest fails', context: 'AssertOkTest', hhLogs: hhLogs2, assertMethod: 'ok', location: '366:36:0', expected: 'true', returned: 'false' }, - describe('assert library NOTEQUAL method tests', function () { - const filename: string = __dirname + '/examples_0/assert_notEqual_test.sol' - - before((done) => { - compileAndDeploy(filename, (_err: Error | null | undefined, compilationData: any, contracts: any, asts: any, accounts: string[], web3: any) => { - runTest('AssertNotEqualTest', contracts.AssertNotEqualTest, compilationData[filename]['AssertNotEqualTest'], asts[filename], { accounts }, testCallback, resultsCallback(done)) - }) - }) - - after(() => { tests = [] }) - - it('should have 6 passing test', () => { - assert.equal(results.passingNum, 6) - }) - - it('should have 6 failing test', () => { - assert.equal(results.failureNum, 6) - }) - - it('should return', () => { - deepEqualExcluding(tests, [ - { type: 'accountList', value: accounts }, - { type: 'contract', value: 'AssertNotEqualTest', filename: __dirname + '/examples_0/assert_notEqual_test.sol' }, - { type: 'testPass', debugTxHash: '0xdef34ec7fbc6a3e6c6ef619b424bf8ebf16db16ed3f74500d56d8170d3aeca66', value: 'Not equal uint pass test', filename: __dirname + '/examples_0/assert_notEqual_test.sol', context: 'AssertNotEqualTest' }, - { type: 'testFailure', debugTxHash: '0xfcbd35bc5f460e22e885951d560171d687cf90ccdffc41fb5de1beb7075fe4e9', value: 'Not equal uint fail test', filename: __dirname + '/examples_0/assert_notEqual_test.sol', errMsg: 'notEqualUintFailTest fails', context: 'AssertNotEqualTest', assertMethod: 'notEqual', location: '288:63:0', expected: '1', returned: '1' }, - { type: 'testPass', debugTxHash: '0x7f269855c3fc5c677eca416eb85665b8f10df00d3b7ec5dcc00cbf8e6364cba4', value: 'Not equal int pass test', filename: __dirname + '/examples_0/assert_notEqual_test.sol', context: 'AssertNotEqualTest' }, - { type: 'testFailure', debugTxHash: '0x76555e218571d4ad69496d7d10ae46d30149c4bfd8c6e15ff2a58668ab6fba62', value: 'Not equal int fail test', filename: __dirname + '/examples_0/assert_notEqual_test.sol', errMsg: 'notEqualIntFailTest fails', context: 'AssertNotEqualTest', assertMethod: 'notEqual', location: '525:52:0', expected: '-2', returned: '-2' }, - { type: 'testPass', debugTxHash: '0x5fe790b3f32b9580c1d5f9a2dbb0e10ddcb62846037d3f5800d47a51bb67cc91', value: 'Not equal bool pass test', filename: __dirname + '/examples_0/assert_notEqual_test.sol', context: 'AssertNotEqualTest' }, - { type: 'testFailure', debugTxHash: '0x660d0a73395e6855aea8f6d3450e63640437dc15071842b417c39f40e1d7ae61', value: 'Not equal bool fail test', filename: __dirname + '/examples_0/assert_notEqual_test.sol', errMsg: 'notEqualBoolFailTest fails', context: 'AssertNotEqualTest', assertMethod: 'notEqual', location: '760:57:0', expected: true, returned: true }, - { type: 'testPass', debugTxHash: '0x6fddce5573bd6723acf5a3e4137d698ff78f695873a228939276c4323ddfb132', value: 'Not equal address pass test', filename: __dirname + '/examples_0/assert_notEqual_test.sol', context: 'AssertNotEqualTest' }, - // eslint-disable-next-line @typescript-eslint/no-loss-of-precision - { type: 'testFailure', debugTxHash: '0x51479e46db802fb598c61ca0dd630345b9d70cc58667b5a80aa79e8119fa7787', value: 'Not equal address fail test', filename: __dirname + '/examples_0/assert_notEqual_test.sol', errMsg: 'notEqualAddressFailTest fails', context: 'AssertNotEqualTest', assertMethod: 'notEqual', location: '1084:136:0', expected: 0x7994f14563F39875a2F934Ce42cAbF48a93FdDA9, returned: 0x7994f14563F39875a2F934Ce42cAbF48a93FdDA9 }, - { type: 'testPass', debugTxHash: '0xbcaf6d8977b655fdedb280e0e9221d728706d41e85e0973d00c8da1d128022c7', value: 'Not equal bytes32 pass test', filename: __dirname + '/examples_0/assert_notEqual_test.sol', context: 'AssertNotEqualTest' }, - { type: 'testFailure', debugTxHash: '0x34008ef0ea908fedbf80471424d801f5069e6e46221f8ee4a2ee16776a6eeef6', value: 'Not equal bytes32 fail test', filename: __dirname + '/examples_0/assert_notEqual_test.sol', errMsg: 'notEqualBytes32FailTest fails', context: 'AssertNotEqualTest', assertMethod: 'notEqual', location: '1756:54:0', expected: '0x72656d6978000000000000000000000000000000000000000000000000000000', returned: '0x72656d6978000000000000000000000000000000000000000000000000000000' }, - { type: 'testPass', debugTxHash: '0x8e0bc9dedea6e088ca7bd82b1e9fab516be5a52f7716a26ccca8197236aae105', value: 'Not equal string pass test', filename: __dirname + '/examples_0/assert_notEqual_test.sol', context: 'AssertNotEqualTest' }, - { type: 'testFailure', debugTxHash: '0x13c6d270c3609ef858dd6d0c79433ca0b43e47b485b2e40ffe363f18f2868ea8', value: 'Not equal string fail test', filename: __dirname + '/examples_0/assert_notEqual_test.sol', errMsg: 'notEqualStringFailTest fails', context: 'AssertNotEqualTest', assertMethod: 'notEqual', location: '2026:81:0', expected: 'remix', returned: 'remix' }, - ], ['time', 'web3']) - }) + ], ['time', 'web3']) + }) + }) + + describe('assert library EQUAL method tests', function () { + const filename: string = __dirname + '/examples_0/assert_equal_test.sol' + + before((done) => { + compileAndDeploy(filename, (_err: Error | null | undefined, compilationData: any, contracts: any, asts: any, accounts: string[], web3: any) => { + runTest('AssertEqualTest', contracts.AssertEqualTest, compilationData[filename]['AssertEqualTest'], asts[filename], { accounts }, testCallback, resultsCallback(done)) }) + }) + + after(() => { tests = [] }) + + it('should have 6 passing test', () => { + assert.equal(results.passingNum, 6) + }) + + it('should have 6 failing test', () => { + assert.equal(results.failureNum, 6) + }) + + it('should return', () => { + deepEqualExcluding(tests, [ + { type: 'accountList', value: accounts }, + { type: 'contract', value: 'AssertEqualTest', filename: __dirname + '/examples_0/assert_equal_test.sol' }, + { type: 'testPass', debugTxHash: '0x233b8d91f0fa068b1a4deae1141178bc3eb79c3d2a6786160595a358363a157c', value: 'Equal uint pass test', filename: __dirname + '/examples_0/assert_equal_test.sol', context: 'AssertEqualTest' }, + { type: 'testFailure', debugTxHash: '0xa5e39c78663c2e5071c08467047ba5b2650d16081b50369700d46d7f90c4d94b', value: 'Equal uint fail test', filename: __dirname + '/examples_0/assert_equal_test.sol', errMsg: 'equalUintFailTest fails', context: 'AssertEqualTest', assertMethod: 'equal', location: '273:57:0', expected: '2', returned: '1' }, + { type: 'testPass', debugTxHash: '0x57af51c2c19db390a4ccf72fa3d32347fb3d998e70820909c7876bd8ccebf8a3', value: 'Equal int pass test', filename: __dirname + '/examples_0/assert_equal_test.sol', context: 'AssertEqualTest' }, + { type: 'testFailure', debugTxHash: '0x710f3a54a561c009fcf0277273b8fe337b2c493e9e83e0ae02786d487339ca7b', value: 'Equal int fail test', filename: __dirname + '/examples_0/assert_equal_test.sol', errMsg: 'equalIntFailTest fails', context: 'AssertEqualTest', assertMethod: 'equal', location: '493:45:0', expected: '2', returned: '-1' }, + { type: 'testPass', debugTxHash: '0x10c1ed8651110ad5de6adcad8e1284aa5c1fd3a998a1e863bbecc0ec855fcd7b', value: 'Equal bool pass test', filename: __dirname + '/examples_0/assert_equal_test.sol', context: 'AssertEqualTest' }, + { type: 'testFailure', debugTxHash: '0x004871a82968f43e02278eab9dd3d7eb0bbe88b64d459efa50065e5996fe5fad', value: 'Equal bool fail test', filename: __dirname + '/examples_0/assert_equal_test.sol', errMsg: 'equalBoolFailTest fails', context: 'AssertEqualTest', assertMethod: 'equal', location: '708:52:0', expected: false, returned: true }, + { type: 'testPass', debugTxHash: '0x64a4d4853ab7907712912cf2120ac2bfd2e08b4767b375250f0e907757546454', value: 'Equal address pass test', filename: __dirname + '/examples_0/assert_equal_test.sol', context: 'AssertEqualTest' }, + { type: 'testFailure', debugTxHash: '0xcf62fb76e3b2eb95d92aa2671a9e81e30fefb944f55e2fb8b97096c45fc74a38', value: 'Equal address fail test', filename: __dirname + '/examples_0/assert_equal_test.sol', errMsg: 'equalAddressFailTest fails', context: 'AssertEqualTest', assertMethod: 'equal', location: '1015:130:0', expected: '0x1c6637567229159d1eFD45f95A6675e77727E013', returned: '0x7994f14563F39875a2F934Ce42cAbF48a93FdDA9' }, + { type: 'testPass', debugTxHash: '0x18ef613acc128a21282e09cf920b32ef3be648bb35c0299471ddbbbeeb0faf8c', value: 'Equal bytes32 pass test', filename: __dirname + '/examples_0/assert_equal_test.sol', context: 'AssertEqualTest' }, + { type: 'testFailure', debugTxHash: '0x86fbf2f14e13d228f80a87a947841270d8c55073adddf78e8d4e2ba05d724ec6', value: 'Equal bytes32 fail test', filename: __dirname + '/examples_0/assert_equal_test.sol', errMsg: 'equalBytes32FailTest fails', context: 'AssertEqualTest', assertMethod: 'equal', location: '1670:48:0', expected: '0x72656d6978000000000000000000000000000000000000000000000000000000', returned: '0x72656d6979000000000000000000000000000000000000000000000000000000' }, + { type: 'testPass', debugTxHash: '0x80b3465f2504b74359790baa009237ba066685b24afa65a31814f1ad1bc4f99f', value: 'Equal string pass test', filename: __dirname + '/examples_0/assert_equal_test.sol', context: 'AssertEqualTest' }, + { type: 'testFailure', debugTxHash: '0x88b035a85c5f87f54a805334817f3e4599b4190d98f25947fe14d7804facd8b7', value: 'Equal string fail test', filename: __dirname + '/examples_0/assert_equal_test.sol', errMsg: 'equalStringFailTest fails', context: 'AssertEqualTest', assertMethod: 'equal', location: '1916:81:0', expected: 'remix-tests', returned: 'remix' } + ], ['time', 'web3']) + }) + }) + + - describe('assert library GREATERTHAN method tests', function () { - const filename: string = __dirname + '/examples_0/assert_greaterThan_test.sol' - - before((done) => { - compileAndDeploy(filename, (_err: Error | null | undefined, compilationData: any, contracts: any, asts: any, accounts: string[], web3: any) => { - runTest('AssertGreaterThanTest', contracts.AssertGreaterThanTest, compilationData[filename]['AssertGreaterThanTest'], asts[filename], { accounts }, testCallback, resultsCallback(done)) - }) - }) - - after(() => { tests = [] }) - - it('should have 4 passing test', () => { - assert.equal(results.passingNum, 4) - }) - - it('should have 4 failing test', () => { - assert.equal(results.failureNum, 4) - }) - it('should return', () => { - deepEqualExcluding(tests, [ - { type: 'accountList', value: accounts }, - { type: 'contract', value: 'AssertGreaterThanTest', filename: __dirname + '/examples_0/assert_greaterThan_test.sol' }, - { type: 'testPass', debugTxHash: '0xdc325916fd93227b76231131e52e67f8913d395098c5ac767032db9bd757a91c', value: 'Greater than uint pass test', filename: __dirname + '/examples_0/assert_greaterThan_test.sol', context: 'AssertGreaterThanTest' }, - { type: 'testFailure', debugTxHash: '0xf98eea22bb86f13e0bb4072df22b540289a46b332bdb203a1e488d7e14a1dcd4', value: 'Greater than uint fail test', filename: __dirname + '/examples_0/assert_greaterThan_test.sol', errMsg: 'greaterThanUintFailTest fails', context: 'AssertGreaterThanTest', assertMethod: 'greaterThan', location: '303:69:0', expected: '4', returned: '1' }, - { type: 'testPass', debugTxHash: '0xef5ef38329ba6aac2f868d53d803053c52b1895a2c25b704260435c141a63bfc', value: 'Greater than int pass test', filename: __dirname + '/examples_0/assert_greaterThan_test.sol', context: 'AssertGreaterThanTest' }, - { type: 'testFailure', debugTxHash: '0x6b9430f3f12c12fb11e5a8d32fef849ab34614e644be20c6b41a25e510453440', value: 'Greater than int fail test', filename: __dirname + '/examples_0/assert_greaterThan_test.sol', errMsg: 'greaterThanIntFailTest fails', context: 'AssertGreaterThanTest', assertMethod: 'greaterThan', location: '569:67:0', expected: '1', returned: '-1' }, - { type: 'testPass', debugTxHash: '0x4c6e10815a5e82bf2c60950606dc886317f680028a9229ba2dda17b5ea36325a', value: 'Greater than uint int pass test', filename: __dirname + '/examples_0/assert_greaterThan_test.sol', context: 'AssertGreaterThanTest' }, - { type: 'testFailure', debugTxHash: '0x989c405c32c8e270a5dea69e6250a514c05dacd6fcf018365a241abc28c2497b', value: 'Greater than uint int fail test', filename: __dirname + '/examples_0/assert_greaterThan_test.sol', errMsg: 'greaterThanUintIntFailTest fails', context: 'AssertGreaterThanTest', assertMethod: 'greaterThan', location: '845:71:0', expected: '2', returned: '1' }, - { type: 'testPass', debugTxHash: '0x9fed670ae2061929f71780835b7ea3eb7da6d4fb553cd2d5f62950c353165861', value: 'Greater than int uint pass test', filename: __dirname + '/examples_0/assert_greaterThan_test.sol', context: 'AssertGreaterThanTest' }, - { type: 'testFailure', debugTxHash: '0xcf394fd279293cdcf58efc42f3a443595fdb171769a45df01b0c84cd76b3a9a2', value: 'Greater than int uint fail test', filename: __dirname + '/examples_0/assert_greaterThan_test.sol', errMsg: 'greaterThanIntUintFailTest fails', context: 'AssertGreaterThanTest', assertMethod: 'greaterThan', location: '1125:76:0', expected: '115792089237316195423570985008687907853269984665640564039457584007913129639836', returned: '100' } - ], ['time', 'web3']) - }) + describe('assert library NOTEQUAL method tests', function () { + const filename: string = __dirname + '/examples_0/assert_notEqual_test.sol' + + before((done) => { + compileAndDeploy(filename, (_err: Error | null | undefined, compilationData: any, contracts: any, asts: any, accounts: string[], web3: any) => { + runTest('AssertNotEqualTest', contracts.AssertNotEqualTest, compilationData[filename]['AssertNotEqualTest'], asts[filename], { accounts }, testCallback, resultsCallback(done)) }) + }) + + after(() => { tests = [] }) + + it('should have 6 passing test', () => { + assert.equal(results.passingNum, 6) + }) + + it('should have 6 failing test', () => { + assert.equal(results.failureNum, 6) + }) + + it('should return', () => { + deepEqualExcluding(tests, [ + { type: 'accountList', value: accounts }, + { type: 'contract', value: 'AssertNotEqualTest', filename: __dirname + '/examples_0/assert_notEqual_test.sol' }, + { type: 'testPass', debugTxHash: '0xdef34ec7fbc6a3e6c6ef619b424bf8ebf16db16ed3f74500d56d8170d3aeca66', value: 'Not equal uint pass test', filename: __dirname + '/examples_0/assert_notEqual_test.sol', context: 'AssertNotEqualTest' }, + { type: 'testFailure', debugTxHash: '0xfcbd35bc5f460e22e885951d560171d687cf90ccdffc41fb5de1beb7075fe4e9', value: 'Not equal uint fail test', filename: __dirname + '/examples_0/assert_notEqual_test.sol', errMsg: 'notEqualUintFailTest fails', context: 'AssertNotEqualTest', assertMethod: 'notEqual', location: '288:63:0', expected: '1', returned: '1' }, + { type: 'testPass', debugTxHash: '0x7f269855c3fc5c677eca416eb85665b8f10df00d3b7ec5dcc00cbf8e6364cba4', value: 'Not equal int pass test', filename: __dirname + '/examples_0/assert_notEqual_test.sol', context: 'AssertNotEqualTest' }, + { type: 'testFailure', debugTxHash: '0x76555e218571d4ad69496d7d10ae46d30149c4bfd8c6e15ff2a58668ab6fba62', value: 'Not equal int fail test', filename: __dirname + '/examples_0/assert_notEqual_test.sol', errMsg: 'notEqualIntFailTest fails', context: 'AssertNotEqualTest', assertMethod: 'notEqual', location: '525:52:0', expected: '-2', returned: '-2' }, + { type: 'testPass', debugTxHash: '0x5fe790b3f32b9580c1d5f9a2dbb0e10ddcb62846037d3f5800d47a51bb67cc91', value: 'Not equal bool pass test', filename: __dirname + '/examples_0/assert_notEqual_test.sol', context: 'AssertNotEqualTest' }, + { type: 'testFailure', debugTxHash: '0x660d0a73395e6855aea8f6d3450e63640437dc15071842b417c39f40e1d7ae61', value: 'Not equal bool fail test', filename: __dirname + '/examples_0/assert_notEqual_test.sol', errMsg: 'notEqualBoolFailTest fails', context: 'AssertNotEqualTest', assertMethod: 'notEqual', location: '760:57:0', expected: true, returned: true }, + { type: 'testPass', debugTxHash: '0x6fddce5573bd6723acf5a3e4137d698ff78f695873a228939276c4323ddfb132', value: 'Not equal address pass test', filename: __dirname + '/examples_0/assert_notEqual_test.sol', context: 'AssertNotEqualTest' }, + // eslint-disable-next-line @typescript-eslint/no-loss-of-precision + { type: 'testFailure', debugTxHash: '0x51479e46db802fb598c61ca0dd630345b9d70cc58667b5a80aa79e8119fa7787', value: 'Not equal address fail test', filename: __dirname + '/examples_0/assert_notEqual_test.sol', errMsg: 'notEqualAddressFailTest fails', context: 'AssertNotEqualTest', assertMethod: 'notEqual', location: '1084:136:0', expected: 0x7994f14563F39875a2F934Ce42cAbF48a93FdDA9, returned: 0x7994f14563F39875a2F934Ce42cAbF48a93FdDA9 }, + { type: 'testPass', debugTxHash: '0xbcaf6d8977b655fdedb280e0e9221d728706d41e85e0973d00c8da1d128022c7', value: 'Not equal bytes32 pass test', filename: __dirname + '/examples_0/assert_notEqual_test.sol', context: 'AssertNotEqualTest' }, + { type: 'testFailure', debugTxHash: '0x34008ef0ea908fedbf80471424d801f5069e6e46221f8ee4a2ee16776a6eeef6', value: 'Not equal bytes32 fail test', filename: __dirname + '/examples_0/assert_notEqual_test.sol', errMsg: 'notEqualBytes32FailTest fails', context: 'AssertNotEqualTest', assertMethod: 'notEqual', location: '1756:54:0', expected: '0x72656d6978000000000000000000000000000000000000000000000000000000', returned: '0x72656d6978000000000000000000000000000000000000000000000000000000' }, + { type: 'testPass', debugTxHash: '0x8e0bc9dedea6e088ca7bd82b1e9fab516be5a52f7716a26ccca8197236aae105', value: 'Not equal string pass test', filename: __dirname + '/examples_0/assert_notEqual_test.sol', context: 'AssertNotEqualTest' }, + { type: 'testFailure', debugTxHash: '0x13c6d270c3609ef858dd6d0c79433ca0b43e47b485b2e40ffe363f18f2868ea8', value: 'Not equal string fail test', filename: __dirname + '/examples_0/assert_notEqual_test.sol', errMsg: 'notEqualStringFailTest fails', context: 'AssertNotEqualTest', assertMethod: 'notEqual', location: '2026:81:0', expected: 'remix', returned: 'remix' }, + ], ['time', 'web3']) + }) + }) - describe('assert library LESSERTHAN method tests', function () { - const filename: string = __dirname + '/examples_0/assert_lesserThan_test.sol' - - before((done) => { - compileAndDeploy(filename, (_err: Error | null | undefined, compilationData: any, contracts: any, asts: any, accounts: string[], web3: any) => { - runTest('AssertLesserThanTest', contracts.AssertLesserThanTest, compilationData[filename]['AssertLesserThanTest'], asts[filename], { accounts }, testCallback, resultsCallback(done)) - }) - }) - - after(() => { tests = [] }) - - it('should have 4 passing test', () => { - assert.equal(results.passingNum, 4) - }) - - it('should have 4 failing test', () => { - assert.equal(results.failureNum, 4) - }) - - it('should return', () => { - deepEqualExcluding(tests, [ - { type: 'accountList', value: accounts }, - { type: 'contract', value: 'AssertLesserThanTest', filename: __dirname + '/examples_0/assert_lesserThan_test.sol' }, - { type: 'testPass', debugTxHash: '0x524fb46aa0e8a78bc11a99432908d422450c2933d837f858aeacba9b84706d5c', value: 'Lesser than uint pass test', filename: __dirname + '/examples_0/assert_lesserThan_test.sol', context: 'AssertLesserThanTest' }, - { type: 'testFailure', debugTxHash: '0x0551a67b10b9e13182e8bdb4e530ed92466d5054ae959f999f2c558da2c39d22', value: 'Lesser than uint fail test', filename: __dirname + '/examples_0/assert_lesserThan_test.sol', errMsg: 'lesserThanUintFailTest fails', context: 'AssertLesserThanTest', assertMethod: 'lesserThan', location: '298:67:0', expected: '2', returned: '4' }, - { type: 'testPass', debugTxHash: '0x6d63958d8c3230e837d0ca8335e57262c6e0c6b2c07a5b481842b9ad7329ac28', value: 'Lesser than int pass test', filename: __dirname + '/examples_0/assert_lesserThan_test.sol', context: 'AssertLesserThanTest' }, - { type: 'testFailure', debugTxHash: '0x38e96ef44f4e785db4d40a95862a9797e8cef6de0ce1d059da72ff42e2f3ca62', value: 'Lesser than int fail test', filename: __dirname + '/examples_0/assert_lesserThan_test.sol', errMsg: 'lesserThanIntFailTest fails', context: 'AssertLesserThanTest', assertMethod: 'lesserThan', location: '557:65:0', expected: '-1', returned: '1' }, - { type: 'testPass', debugTxHash: '0x699f9fc2bf7a14134e89b94cd9dc1c537b5d4581a1c26a34a0c3343ddede9608', value: 'Lesser than uint int pass test', filename: __dirname + '/examples_0/assert_lesserThan_test.sol', context: 'AssertLesserThanTest' }, - { type: 'testFailure', debugTxHash: '0xce1391dcfbfdc6c611e357e6c1c9f6cd9f257153ee400cb80bd36af6d239c342', value: 'Lesser than uint int fail test', filename: __dirname + '/examples_0/assert_lesserThan_test.sol', errMsg: 'lesserThanUintIntFailTest fails', context: 'AssertLesserThanTest', assertMethod: 'lesserThan', location: '826:71:0', expected: '-1', returned: '115792089237316195423570985008687907853269984665640564039457584007913129639935' }, - { type: 'testPass', debugTxHash: '0x7040e6664c13e6b35ef1daaef93a8cae36a62150d818183892096a98b921800c', value: 'Lesser than int uint pass test', filename: __dirname + '/examples_0/assert_lesserThan_test.sol', context: 'AssertLesserThanTest' }, - { type: 'testFailure', debugTxHash: '0x8c58bb433ea41760dcf11114232407d703e8ebf7d5e9637e2923282eae5caee6', value: 'Lesser than int uint fail test', filename: __dirname + '/examples_0/assert_lesserThan_test.sol', errMsg: 'lesserThanIntUintFailTest fails', context: 'AssertLesserThanTest', assertMethod: 'lesserThan', location: '1105:69:0', expected: '1', returned: '1' }, - ], ['time', 'web3']) - }) + describe('assert library GREATERTHAN method tests', function () { + const filename: string = __dirname + '/examples_0/assert_greaterThan_test.sol' + + before((done) => { + compileAndDeploy(filename, (_err: Error | null | undefined, compilationData: any, contracts: any, asts: any, accounts: string[], web3: any) => { + runTest('AssertGreaterThanTest', contracts.AssertGreaterThanTest, compilationData[filename]['AssertGreaterThanTest'], asts[filename], { accounts }, testCallback, resultsCallback(done)) }) + }) + + after(() => { tests = [] }) + + it('should have 4 passing test', () => { + assert.equal(results.passingNum, 4) + }) + + it('should have 4 failing test', () => { + assert.equal(results.failureNum, 4) + }) + it('should return', () => { + deepEqualExcluding(tests, [ + { type: 'accountList', value: accounts }, + { type: 'contract', value: 'AssertGreaterThanTest', filename: __dirname + '/examples_0/assert_greaterThan_test.sol' }, + { type: 'testPass', debugTxHash: '0xdc325916fd93227b76231131e52e67f8913d395098c5ac767032db9bd757a91c', value: 'Greater than uint pass test', filename: __dirname + '/examples_0/assert_greaterThan_test.sol', context: 'AssertGreaterThanTest' }, + { type: 'testFailure', debugTxHash: '0xf98eea22bb86f13e0bb4072df22b540289a46b332bdb203a1e488d7e14a1dcd4', value: 'Greater than uint fail test', filename: __dirname + '/examples_0/assert_greaterThan_test.sol', errMsg: 'greaterThanUintFailTest fails', context: 'AssertGreaterThanTest', assertMethod: 'greaterThan', location: '303:69:0', expected: '4', returned: '1' }, + { type: 'testPass', debugTxHash: '0xef5ef38329ba6aac2f868d53d803053c52b1895a2c25b704260435c141a63bfc', value: 'Greater than int pass test', filename: __dirname + '/examples_0/assert_greaterThan_test.sol', context: 'AssertGreaterThanTest' }, + { type: 'testFailure', debugTxHash: '0x6b9430f3f12c12fb11e5a8d32fef849ab34614e644be20c6b41a25e510453440', value: 'Greater than int fail test', filename: __dirname + '/examples_0/assert_greaterThan_test.sol', errMsg: 'greaterThanIntFailTest fails', context: 'AssertGreaterThanTest', assertMethod: 'greaterThan', location: '569:67:0', expected: '1', returned: '-1' }, + { type: 'testPass', debugTxHash: '0x4c6e10815a5e82bf2c60950606dc886317f680028a9229ba2dda17b5ea36325a', value: 'Greater than uint int pass test', filename: __dirname + '/examples_0/assert_greaterThan_test.sol', context: 'AssertGreaterThanTest' }, + { type: 'testFailure', debugTxHash: '0x989c405c32c8e270a5dea69e6250a514c05dacd6fcf018365a241abc28c2497b', value: 'Greater than uint int fail test', filename: __dirname + '/examples_0/assert_greaterThan_test.sol', errMsg: 'greaterThanUintIntFailTest fails', context: 'AssertGreaterThanTest', assertMethod: 'greaterThan', location: '845:71:0', expected: '2', returned: '1' }, + { type: 'testPass', debugTxHash: '0x9fed670ae2061929f71780835b7ea3eb7da6d4fb553cd2d5f62950c353165861', value: 'Greater than int uint pass test', filename: __dirname + '/examples_0/assert_greaterThan_test.sol', context: 'AssertGreaterThanTest' }, + { type: 'testFailure', debugTxHash: '0xcf394fd279293cdcf58efc42f3a443595fdb171769a45df01b0c84cd76b3a9a2', value: 'Greater than int uint fail test', filename: __dirname + '/examples_0/assert_greaterThan_test.sol', errMsg: 'greaterThanIntUintFailTest fails', context: 'AssertGreaterThanTest', assertMethod: 'greaterThan', location: '1125:76:0', expected: '115792089237316195423570985008687907853269984665640564039457584007913129639836', returned: '100' } + ], ['time', 'web3']) + }) + }) - describe('test with before', function () { - const filename: string = __dirname + '/examples_1/simple_storage_test.sol' - - before((done) => { - compileAndDeploy(filename, (_err: Error | null | undefined, compilationData: any, contracts: any, asts: any, accounts: string[], web3: any) => { - runTest('MyTest', contracts.MyTest, compilationData[filename]['MyTest'], asts[filename], { accounts }, testCallback, resultsCallback(done)) - }) - }) - - after(() => { tests = [] }) - - it('should have 3 passing test', () => { - assert.equal(results.passingNum, 3) - }) - - it('should have 1 failing test', () => { - assert.equal(results.failureNum, 1) - }) - - it('should return 6 messages', () => { - deepEqualExcluding(tests, [ - { type: 'accountList', value: accounts }, - { type: 'contract', value: 'MyTest', filename: __dirname + '/examples_1/simple_storage_test.sol' }, - { type: 'testPass', debugTxHash: '0xed5b6898331119c6e3d1185b9de65d87ad7329cc629a8f2d43b966cf180a5dc1', value: 'Initial value should be100', filename: __dirname + '/examples_1/simple_storage_test.sol', context: 'MyTest' }, - { type: 'testPass', debugTxHash: '0x79cae5c4f44edfd7ae3490e01c75df5741b107672cef5e69800e4d30d380a721', value: 'Initial value should not be200', filename: __dirname + '/examples_1/simple_storage_test.sol', context: 'MyTest' }, - { type: 'testFailure', debugTxHash: '0x24a20f7643e88f891e469ef495911ab0b75f99e2b09b9b091e688674910d1506', value: 'Should trigger one fail', filename: __dirname + '/examples_1/simple_storage_test.sol', errMsg: 'uint test 1 fails', context: 'MyTest', assertMethod: 'equal', location: '532:51:1', expected: '2', returned: '1' }, - { type: 'testPass', debugTxHash: '0x08b1f60c908b7e6cf2dd24fc166c755f0fe5336aebfb325cae4ce00ea9bbf932', value: 'Should trigger one pass', filename: __dirname + '/examples_1/simple_storage_test.sol', context: 'MyTest' } - ], ['time', 'web3']) - }) + describe('assert library LESSERTHAN method tests', function () { + const filename: string = __dirname + '/examples_0/assert_lesserThan_test.sol' + + before((done) => { + compileAndDeploy(filename, (_err: Error | null | undefined, compilationData: any, contracts: any, asts: any, accounts: string[], web3: any) => { + runTest('AssertLesserThanTest', contracts.AssertLesserThanTest, compilationData[filename]['AssertLesserThanTest'], asts[filename], { accounts }, testCallback, resultsCallback(done)) }) + }) + + after(() => { tests = [] }) + + it('should have 4 passing test', () => { + assert.equal(results.passingNum, 4) + }) + + it('should have 4 failing test', () => { + assert.equal(results.failureNum, 4) + }) + + it('should return', () => { + deepEqualExcluding(tests, [ + { type: 'accountList', value: accounts }, + { type: 'contract', value: 'AssertLesserThanTest', filename: __dirname + '/examples_0/assert_lesserThan_test.sol' }, + { type: 'testPass', debugTxHash: '0x524fb46aa0e8a78bc11a99432908d422450c2933d837f858aeacba9b84706d5c', value: 'Lesser than uint pass test', filename: __dirname + '/examples_0/assert_lesserThan_test.sol', context: 'AssertLesserThanTest' }, + { type: 'testFailure', debugTxHash: '0x0551a67b10b9e13182e8bdb4e530ed92466d5054ae959f999f2c558da2c39d22', value: 'Lesser than uint fail test', filename: __dirname + '/examples_0/assert_lesserThan_test.sol', errMsg: 'lesserThanUintFailTest fails', context: 'AssertLesserThanTest', assertMethod: 'lesserThan', location: '298:67:0', expected: '2', returned: '4' }, + { type: 'testPass', debugTxHash: '0x6d63958d8c3230e837d0ca8335e57262c6e0c6b2c07a5b481842b9ad7329ac28', value: 'Lesser than int pass test', filename: __dirname + '/examples_0/assert_lesserThan_test.sol', context: 'AssertLesserThanTest' }, + { type: 'testFailure', debugTxHash: '0x38e96ef44f4e785db4d40a95862a9797e8cef6de0ce1d059da72ff42e2f3ca62', value: 'Lesser than int fail test', filename: __dirname + '/examples_0/assert_lesserThan_test.sol', errMsg: 'lesserThanIntFailTest fails', context: 'AssertLesserThanTest', assertMethod: 'lesserThan', location: '557:65:0', expected: '-1', returned: '1' }, + { type: 'testPass', debugTxHash: '0x699f9fc2bf7a14134e89b94cd9dc1c537b5d4581a1c26a34a0c3343ddede9608', value: 'Lesser than uint int pass test', filename: __dirname + '/examples_0/assert_lesserThan_test.sol', context: 'AssertLesserThanTest' }, + { type: 'testFailure', debugTxHash: '0xce1391dcfbfdc6c611e357e6c1c9f6cd9f257153ee400cb80bd36af6d239c342', value: 'Lesser than uint int fail test', filename: __dirname + '/examples_0/assert_lesserThan_test.sol', errMsg: 'lesserThanUintIntFailTest fails', context: 'AssertLesserThanTest', assertMethod: 'lesserThan', location: '826:71:0', expected: '-1', returned: '115792089237316195423570985008687907853269984665640564039457584007913129639935' }, + { type: 'testPass', debugTxHash: '0x7040e6664c13e6b35ef1daaef93a8cae36a62150d818183892096a98b921800c', value: 'Lesser than int uint pass test', filename: __dirname + '/examples_0/assert_lesserThan_test.sol', context: 'AssertLesserThanTest' }, + { type: 'testFailure', debugTxHash: '0x8c58bb433ea41760dcf11114232407d703e8ebf7d5e9637e2923282eae5caee6', value: 'Lesser than int uint fail test', filename: __dirname + '/examples_0/assert_lesserThan_test.sol', errMsg: 'lesserThanIntUintFailTest fails', context: 'AssertLesserThanTest', assertMethod: 'lesserThan', location: '1105:69:0', expected: '1', returned: '1' }, + ], ['time', 'web3']) + }) + }) + + describe('test with before', function () { + const filename: string = __dirname + '/examples_1/simple_storage_test.sol' - describe('test with beforeEach', function () { - const filename: string = __dirname + '/examples_2/simple_storage_test.sol' - - before(done => { - compileAndDeploy(filename, function (_err: Error | null | undefined, compilationData: any, contracts: any, asts: any, accounts: string[], web3: any) { - runTest('MyTest', contracts.MyTest, compilationData[filename]['MyTest'], asts[filename], { accounts }, testCallback, resultsCallback(done)) - }) - }) - - after(() => { tests = [] }) - - it('should have 2 passing tests', () => { - assert.equal(results.passingNum, 2) - }) - - it('should 0 failing tests', () => { - assert.equal(results.failureNum, 0) - }) - - it('should return 4 messages', () => { - deepEqualExcluding(tests, [ - { type: 'accountList', value: accounts }, - { type: 'contract', value: 'MyTest', filename: __dirname + '/examples_2/simple_storage_test.sol' }, - { type: 'testPass', debugTxHash: '0xed5b6898331119c6e3d1185b9de65d87ad7329cc629a8f2d43b966cf180a5dc1', value: 'Initial value should be100', filename: __dirname + '/examples_2/simple_storage_test.sol', context: 'MyTest' }, - { type: 'testPass', debugTxHash: '0x8ed5b4858405b43ad4052f5690b4b711c0f6cdeb67a64f54084417d43bc54308', value: 'Value is set200', filename: __dirname + '/examples_2/simple_storage_test.sol', context: 'MyTest' } - ], ['time', 'web3']) - }) + before((done) => { + compileAndDeploy(filename, (_err: Error | null | undefined, compilationData: any, contracts: any, asts: any, accounts: string[], web3: any) => { + runTest('MyTest', contracts.MyTest, compilationData[filename]['MyTest'], asts[filename], { accounts }, testCallback, resultsCallback(done)) }) + }) + + after(() => { tests = [] }) + + it('should have 3 passing test', () => { + assert.equal(results.passingNum, 3) + }) + + it('should have 1 failing test', () => { + assert.equal(results.failureNum, 1) + }) + + it('should return 6 messages', () => { + deepEqualExcluding(tests, [ + { type: 'accountList', value: accounts }, + { type: 'contract', value: 'MyTest', filename: __dirname + '/examples_1/simple_storage_test.sol' }, + { type: 'testPass', debugTxHash: '0xed5b6898331119c6e3d1185b9de65d87ad7329cc629a8f2d43b966cf180a5dc1', value: 'Initial value should be100', filename: __dirname + '/examples_1/simple_storage_test.sol', context: 'MyTest' }, + { type: 'testPass', debugTxHash: '0x79cae5c4f44edfd7ae3490e01c75df5741b107672cef5e69800e4d30d380a721', value: 'Initial value should not be200', filename: __dirname + '/examples_1/simple_storage_test.sol', context: 'MyTest' }, + { type: 'testFailure', debugTxHash: '0x24a20f7643e88f891e469ef495911ab0b75f99e2b09b9b091e688674910d1506', value: 'Should trigger one fail', filename: __dirname + '/examples_1/simple_storage_test.sol', errMsg: 'uint test 1 fails', context: 'MyTest', assertMethod: 'equal', location: '532:51:1', expected: '2', returned: '1' }, + { type: 'testPass', debugTxHash: '0x08b1f60c908b7e6cf2dd24fc166c755f0fe5336aebfb325cae4ce00ea9bbf932', value: 'Should trigger one pass', filename: __dirname + '/examples_1/simple_storage_test.sol', context: 'MyTest' } + ], ['time', 'web3']) + }) + }) + + describe('test with beforeEach', function () { + const filename: string = __dirname + '/examples_2/simple_storage_test.sol' - // Test string equality - describe('test string equality', function () { - const filename: string = __dirname + '/examples_3/simple_string_test.sol' - - before(done => { - compileAndDeploy(filename, function (_err: Error | null | undefined, compilationData: any, contracts: any, asts: any, accounts: string[], web3: any) { - runTest('StringTest', contracts.StringTest, compilationData[filename]['StringTest'], asts[filename], { accounts }, testCallback, resultsCallback(done)) - }) - }) - - after(() => { tests = [] }) - - it('should 2 passing tests', () => { - assert.equal(results.passingNum, 2) - }) - - it('should return 4 messages', () => { - deepEqualExcluding(tests, [ - { type: 'accountList', value: accounts }, - { type: 'contract', value: 'StringTest', filename: __dirname + '/examples_3/simple_string_test.sol' }, - { type: 'testPass', debugTxHash: '0x3567da76ffbec37e3b43a41987a7ff3e61b41b4c544f35c010d2d4b39568d6d4', value: 'Initial value should be hello world', filename: __dirname + '/examples_3/simple_string_test.sol', context: 'StringTest' }, - { type: 'testPass', debugTxHash: '0x8619b743ccc99be7d5347a064732474b2d1b69844be65b0e7754c6ac1340d275', value: 'Value should not be hello wordl', filename: __dirname + '/examples_3/simple_string_test.sol', context: 'StringTest' } - ], ['time', 'web3']) - }) + before(done => { + compileAndDeploy(filename, function (_err: Error | null | undefined, compilationData: any, contracts: any, asts: any, accounts: string[], web3: any) { + runTest('MyTest', contracts.MyTest, compilationData[filename]['MyTest'], asts[filename], { accounts }, testCallback, resultsCallback(done)) }) + }) + + after(() => { tests = [] }) + + it('should have 2 passing tests', () => { + assert.equal(results.passingNum, 2) + }) + + it('should 0 failing tests', () => { + assert.equal(results.failureNum, 0) + }) + + it('should return 4 messages', () => { + deepEqualExcluding(tests, [ + { type: 'accountList', value: accounts }, + { type: 'contract', value: 'MyTest', filename: __dirname + '/examples_2/simple_storage_test.sol' }, + { type: 'testPass', debugTxHash: '0xed5b6898331119c6e3d1185b9de65d87ad7329cc629a8f2d43b966cf180a5dc1', value: 'Initial value should be100', filename: __dirname + '/examples_2/simple_storage_test.sol', context: 'MyTest' }, + { type: 'testPass', debugTxHash: '0x8ed5b4858405b43ad4052f5690b4b711c0f6cdeb67a64f54084417d43bc54308', value: 'Value is set200', filename: __dirname + '/examples_2/simple_storage_test.sol', context: 'MyTest' } + ], ['time', 'web3']) + }) + }) - // Test multiple directory import in test contract - describe('test multiple directory import in test contract', function () { - const filename: string = __dirname + '/examples_5/test/simple_storage_test.sol' - - before(done => { - compileAndDeploy(filename, function (_err: Error | null | undefined, compilationData: any, contracts: any, asts: any, accounts: string[], web3: any) { - runTest('StorageResolveTest', contracts.StorageResolveTest, compilationData[filename]['StorageResolveTest'], asts[filename], { accounts }, testCallback, resultsCallback(done)) - }) - }) - - after(() => { tests = [] }) - - it('should 3 passing tests', () => { - assert.equal(results.passingNum, 3) - }) - - it('should return 4 messages', () => { - deepEqualExcluding(tests, [ - { type: 'accountList', value: accounts }, - { type: 'contract', value: 'StorageResolveTest', filename: __dirname + '/examples_5/test/simple_storage_test.sol' }, - { type: 'testPass', debugTxHash: '0xed5b6898331119c6e3d1185b9de65d87ad7329cc629a8f2d43b966cf180a5dc1', value: 'Initial value should be100', filename: __dirname + '/examples_5/test/simple_storage_test.sol', context: 'StorageResolveTest' }, - { type: 'testPass', debugTxHash: '0x6893fe4f5a83cc51f03c9237ab93b93ffd826236167d58e20666be4c1b3128a4', value: 'Check if even', filename: __dirname + '/examples_5/test/simple_storage_test.sol', context: 'StorageResolveTest' }, - { type: 'testPass', debugTxHash: '0x64e600b32be681b68926660042ddd96f22d07949b424959811b8acb56e72f719', value: 'Check if odd', filename: __dirname + '/examples_5/test/simple_storage_test.sol', context: 'StorageResolveTest' } - ], ['time', 'web3']) - }) + // Test string equality + describe('test string equality', function () { + const filename: string = __dirname + '/examples_3/simple_string_test.sol' + + before(done => { + compileAndDeploy(filename, function (_err: Error | null | undefined, compilationData: any, contracts: any, asts: any, accounts: string[], web3: any) { + runTest('StringTest', contracts.StringTest, compilationData[filename]['StringTest'], asts[filename], { accounts }, testCallback, resultsCallback(done)) }) + }) + + after(() => { tests = [] }) + + it('should 2 passing tests', () => { + assert.equal(results.passingNum, 2) + }) + + it('should return 4 messages', () => { + deepEqualExcluding(tests, [ + { type: 'accountList', value: accounts }, + { type: 'contract', value: 'StringTest', filename: __dirname + '/examples_3/simple_string_test.sol' }, + { type: 'testPass', debugTxHash: '0x3567da76ffbec37e3b43a41987a7ff3e61b41b4c544f35c010d2d4b39568d6d4', value: 'Initial value should be hello world', filename: __dirname + '/examples_3/simple_string_test.sol', context: 'StringTest' }, + { type: 'testPass', debugTxHash: '0x8619b743ccc99be7d5347a064732474b2d1b69844be65b0e7754c6ac1340d275', value: 'Value should not be hello wordl', filename: __dirname + '/examples_3/simple_string_test.sol', context: 'StringTest' } + ], ['time', 'web3']) + }) + }) - //Test SafeMath library methods - describe('test SafeMath library', function () { - const filename: string = __dirname + '/examples_4/SafeMath_test.sol' + // Test multiple directory import in test contract + describe('test multiple directory import in test contract', function () { + const filename: string = __dirname + '/examples_5/test/simple_storage_test.sol' - before(done => { - compileAndDeploy(filename, function (_err: Error | null | undefined, compilationData: any, contracts: any, asts: any, accounts: string[], web3: any) { - runTest('SafeMathTest', contracts.SafeMathTest, compilationData[filename]['SafeMathTest'], asts[filename], { accounts }, testCallback, resultsCallback(done)) - }) - }) + before(done => { + compileAndDeploy(filename, function (_err: Error | null | undefined, compilationData: any, contracts: any, asts: any, accounts: string[], web3: any) { + runTest('StorageResolveTest', contracts.StorageResolveTest, compilationData[filename]['StorageResolveTest'], asts[filename], { accounts }, testCallback, resultsCallback(done)) + }) + }) + + after(() => { tests = [] }) + + it('should 3 passing tests', () => { + assert.equal(results.passingNum, 3) + }) + + it('should return 4 messages', () => { + deepEqualExcluding(tests, [ + { type: 'accountList', value: accounts }, + { type: 'contract', value: 'StorageResolveTest', filename: __dirname + '/examples_5/test/simple_storage_test.sol' }, + { type: 'testPass', debugTxHash: '0xed5b6898331119c6e3d1185b9de65d87ad7329cc629a8f2d43b966cf180a5dc1', value: 'Initial value should be100', filename: __dirname + '/examples_5/test/simple_storage_test.sol', context: 'StorageResolveTest' }, + { type: 'testPass', debugTxHash: '0x6893fe4f5a83cc51f03c9237ab93b93ffd826236167d58e20666be4c1b3128a4', value: 'Check if even', filename: __dirname + '/examples_5/test/simple_storage_test.sol', context: 'StorageResolveTest' }, + { type: 'testPass', debugTxHash: '0x64e600b32be681b68926660042ddd96f22d07949b424959811b8acb56e72f719', value: 'Check if odd', filename: __dirname + '/examples_5/test/simple_storage_test.sol', context: 'StorageResolveTest' } + ], ['time', 'web3']) + }) + }) - after(() => { tests = [] }) + //Test SafeMath library methods + describe('test SafeMath library', function () { + const filename: string = __dirname + '/examples_4/SafeMath_test.sol' - it('should have 10 passing tests', () => { - assert.equal(results.passingNum, 10) - }) - it('should have 0 failing tests', () => { - assert.equal(results.failureNum, 0) - }) + before(done => { + compileAndDeploy(filename, function (_err: Error | null | undefined, compilationData: any, contracts: any, asts: any, accounts: string[], web3: any) { + runTest('SafeMathTest', contracts.SafeMathTest, compilationData[filename]['SafeMathTest'], asts[filename], { accounts }, testCallback, resultsCallback(done)) }) + }) - //Test signed/unsigned integer weight - describe('test number weight', function () { - const filename: string = __dirname + '/number/number_test.sol' + after(() => { tests = [] }) - before(done => { - compileAndDeploy(filename, function (_err: Error | null | undefined, compilationData: any, contracts: any, asts: any, accounts: string[], web3: any) { - runTest('IntegerTest', contracts.IntegerTest, compilationData[filename]['IntegerTest'], asts[filename], { accounts }, testCallback, resultsCallback(done)) - }) - }) + it('should have 10 passing tests', () => { + assert.equal(results.passingNum, 10) + }) + it('should have 0 failing tests', () => { + assert.equal(results.failureNum, 0) + }) + }) - after(() => { tests = [] }) + //Test signed/unsigned integer weight + describe('test number weight', function () { + const filename: string = __dirname + '/number/number_test.sol' - it('should have 6 passing tests', () => { - assert.equal(results.passingNum, 6) - }) - it('should have 2 failing tests', () => { - assert.equal(results.failureNum, 2) - }) + before(done => { + compileAndDeploy(filename, function (_err: Error | null | undefined, compilationData: any, contracts: any, asts: any, accounts: string[], web3: any) { + runTest('IntegerTest', contracts.IntegerTest, compilationData[filename]['IntegerTest'], asts[filename], { accounts }, testCallback, resultsCallback(done)) }) + }) - // Test Transaction with custom sender & value - describe('various sender', function () { - const filename: string = __dirname + '/various_sender/sender_and_value_test.sol' + after(() => { tests = [] }) - before(done => { - compileAndDeploy(filename, function (_err: Error | null | undefined, compilationData: any, contracts: any, asts: any, accounts: string[], web3: any) { - runTest('SenderAndValueTest', contracts.SenderAndValueTest, compilationData[filename]['SenderAndValueTest'], asts[filename], { accounts }, testCallback, resultsCallback(done)) - }) - }) + it('should have 6 passing tests', () => { + assert.equal(results.passingNum, 6) + }) + it('should have 2 failing tests', () => { + assert.equal(results.failureNum, 2) + }) + }) - after(() => { tests = [] }) + // Test Transaction with custom sender & value + describe('various sender', function () { + const filename: string = __dirname + '/various_sender/sender_and_value_test.sol' - it('should have 17 passing tests', () => { - assert.equal(results.passingNum, 17) - }) - it('should have 0 failing tests', () => { - assert.equal(results.failureNum, 0) - }) + before(done => { + compileAndDeploy(filename, function (_err: Error | null | undefined, compilationData: any, contracts: any, asts: any, accounts: string[], web3: any) { + runTest('SenderAndValueTest', contracts.SenderAndValueTest, compilationData[filename]['SenderAndValueTest'], asts[filename], { accounts }, testCallback, resultsCallback(done)) }) + }) - // Test `runTest` method without sending contract object (should throw error) - describe('runTest method without contract json interface', function () { - const filename: string = __dirname + '/various_sender/sender_and_value_test.sol' - const errorCallback: any = (done) => { - return (err, _results) => { - if (err && err.message.includes('Contract interface not available')) { - results = _results - done() - } - else throw err - } - } - before(done => { - compileAndDeploy(filename, function (_err: Error | null | undefined, compilationData: any, contracts: any, asts: any, accounts: string[], web3: any) { - runTest('SenderAndValueTest', undefined, compilationData[filename]['SenderAndValueTest'], asts[filename], { accounts }, testCallback, errorCallback(done)) - }) - }) - - it('should have 0 passing tests', () => { - assert.equal(results.passingNum, 0) - }) - it('should have 0 failing tests', () => { - assert.equal(results.failureNum, 0) - }) - }) + after(() => { tests = [] }) + + it('should have 17 passing tests', () => { + assert.equal(results.passingNum, 17) + }) + it('should have 0 failing tests', () => { + assert.equal(results.failureNum, 0) + }) + }) + // Test `runTest` method without sending contract object (should throw error) + describe('runTest method without contract json interface', function () { + const filename: string = __dirname + '/various_sender/sender_and_value_test.sol' + const errorCallback: any = (done) => { + return (err, _results) => { + if (err && err.message.includes('Contract interface not available')) { + results = _results + done() + } + else throw err + } + } + before(done => { + compileAndDeploy(filename, function (_err: Error | null | undefined, compilationData: any, contracts: any, asts: any, accounts: string[], web3: any) { + runTest('SenderAndValueTest', undefined, compilationData[filename]['SenderAndValueTest'], asts[filename], { accounts }, testCallback, errorCallback(done)) + }) + }) + + it('should have 0 passing tests', () => { + assert.equal(results.passingNum, 0) + }) + it('should have 0 failing tests', () => { + assert.equal(results.failureNum, 0) + }) }) + + }) }) \ No newline at end of file diff --git a/libs/remix-ui/app/src/lib/remix-app/components/dragbar/dragbar.tsx b/libs/remix-ui/app/src/lib/remix-app/components/dragbar/dragbar.tsx index 1671e1bcc8..0ba814b345 100644 --- a/libs/remix-ui/app/src/lib/remix-app/components/dragbar/dragbar.tsx +++ b/libs/remix-ui/app/src/lib/remix-app/components/dragbar/dragbar.tsx @@ -12,75 +12,75 @@ interface IRemixDragBarUi { } const DragBar = (props: IRemixDragBarUi) => { - const [dragState, setDragState] = useState(false) - const [dragBarPosX, setDragBarPosX] = useState(0) - const [offset, setOffSet] = useState(0) - const initialWidth = useRef(props.minWidth) - const nodeRef = React.useRef(null) // fix for strictmode + const [dragState, setDragState] = useState(false) + const [dragBarPosX, setDragBarPosX] = useState(0) + const [offset, setOffSet] = useState(0) + const initialWidth = useRef(props.minWidth) + const nodeRef = React.useRef(null) // fix for strictmode - useEffect(() => { - setDragBarPosX(offset + (props.hidden ? 0 : props.refObject.current.offsetWidth)) - }, [props.hidden, offset]) + useEffect(() => { + setDragBarPosX(offset + (props.hidden ? 0 : props.refObject.current.offsetWidth)) + }, [props.hidden, offset]) - useEffect(() => { - initialWidth.current = props.refObject.current.clientWidth - if (props.maximiseTrigger > 0) { - const width = 0.4 * window.innerWidth - if (width > props.refObject.current.offsetWidth) { - props.refObject.current.style.width = width + 'px' - setTimeout(() => { - setDragBarPosX(offset + width) - }, 300) - } - } - }, [props.maximiseTrigger]) - - useEffect(() => { - if (props.maximiseTrigger > 0) { - props.refObject.current.style.width = initialWidth.current + 'px' - setTimeout(() => { - setDragBarPosX(offset + initialWidth.current) - }, 300) - } - }, [props.resetTrigger]) + useEffect(() => { + initialWidth.current = props.refObject.current.clientWidth + if (props.maximiseTrigger > 0) { + const width = 0.4 * window.innerWidth + if (width > props.refObject.current.offsetWidth) { + props.refObject.current.style.width = width + 'px' + setTimeout(() => { + setDragBarPosX(offset + width) + }, 300) + } + } + }, [props.maximiseTrigger]) - const handleResize = () => { - if (!props.refObject.current) return - setOffSet(props.refObject.current.offsetLeft) - setDragBarPosX(props.refObject.current.offsetLeft + props.refObject.current.offsetWidth) + useEffect(() => { + if (props.maximiseTrigger > 0) { + props.refObject.current.style.width = initialWidth.current + 'px' + setTimeout(() => { + setDragBarPosX(offset + initialWidth.current) + }, 300) } + }, [props.resetTrigger]) - useEffect(() => { - window.addEventListener('resize', handleResize) - // TODO: not a good way to wait on the ref doms element to be rendered of course - setTimeout(() => - handleResize(), 2000) - return () => window.removeEventListener('resize', handleResize) - }, []) + const handleResize = () => { + if (!props.refObject.current) return + setOffSet(props.refObject.current.offsetLeft) + setDragBarPosX(props.refObject.current.offsetLeft + props.refObject.current.offsetWidth) + } - function stopDrag(data: any) { - setDragState(false) - if (data.x < props.minWidth + offset) { - setDragBarPosX(offset) - props.setHideStatus(true) - } else { - props.refObject.current.style.width = (data.x - offset) + 'px' - setTimeout(() => { - props.setHideStatus(false) - setDragBarPosX(offset + props.refObject.current.offsetWidth) - }, 300) - } - } + useEffect(() => { + window.addEventListener('resize', handleResize) + // TODO: not a good way to wait on the ref doms element to be rendered of course + setTimeout(() => + handleResize(), 2000) + return () => window.removeEventListener('resize', handleResize) + }, []) - function startDrag() { - setDragState(true) + function stopDrag(data: any) { + setDragState(false) + if (data.x < props.minWidth + offset) { + setDragBarPosX(offset) + props.setHideStatus(true) + } else { + props.refObject.current.style.width = (data.x - offset) + 'px' + setTimeout(() => { + props.setHideStatus(false) + setDragBarPosX(offset + props.refObject.current.offsetWidth) + }, 300) } - return <> -
- -
-
- + } + + function startDrag() { + setDragState(true) + } + return <> +
+ +
+
+ } export default DragBar diff --git a/libs/remix-ui/app/src/lib/remix-app/components/modals/dialogViewPlugin.tsx b/libs/remix-ui/app/src/lib/remix-app/components/modals/dialogViewPlugin.tsx index d5610de56c..6c76c0283d 100644 --- a/libs/remix-ui/app/src/lib/remix-app/components/modals/dialogViewPlugin.tsx +++ b/libs/remix-ui/app/src/lib/remix-app/components/modals/dialogViewPlugin.tsx @@ -3,13 +3,13 @@ import { AppContext } from '../../context/context' import { useDialogDispatchers } from '../../context/provider' const DialogViewPlugin = () => { - const { modal, alert, toast } = useDialogDispatchers() - const app = useContext(AppContext) + const { modal, alert, toast } = useDialogDispatchers() + const app = useContext(AppContext) - useEffect(() => { - app.modal.setDispatcher({ modal, alert, toast }) - }, []) - return <> + useEffect(() => { + app.modal.setDispatcher({ modal, alert, toast }) + }, []) + return <> } export default DialogViewPlugin diff --git a/libs/remix-ui/app/src/lib/remix-app/components/modals/dialogs.tsx b/libs/remix-ui/app/src/lib/remix-app/components/modals/dialogs.tsx index 90bd71a05d..b9776c1be6 100644 --- a/libs/remix-ui/app/src/lib/remix-app/components/modals/dialogs.tsx +++ b/libs/remix-ui/app/src/lib/remix-app/components/modals/dialogs.tsx @@ -4,13 +4,13 @@ import { Toaster } from '@remix-ui/toaster' import ModalWrapper from './modal-wrapper' const AppDialogs = () => { - const { handleHideModal, handleToaster } = useDialogDispatchers() - const { focusModal, focusToaster } = useDialogs() + const { handleHideModal, handleToaster } = useDialogDispatchers() + const { focusModal, focusToaster } = useDialogs() - return ( - <> - - - ) + return ( + <> + + + ) } export default AppDialogs diff --git a/libs/remix-ui/app/src/lib/remix-app/components/modals/matomo.tsx b/libs/remix-ui/app/src/lib/remix-app/components/modals/matomo.tsx index 276a3ee47b..e23db9c4df 100644 --- a/libs/remix-ui/app/src/lib/remix-app/components/modals/matomo.tsx +++ b/libs/remix-ui/app/src/lib/remix-app/components/modals/matomo.tsx @@ -9,42 +9,42 @@ declare global { const _paq = window._paq = window._paq || [] const MatomoDialog = (props) => { - const { settings, showMatamo, appManager } = useContext(AppContext) - const { modal } = useDialogDispatchers() - const [visible, setVisible] = useState(props.hide) + const { settings, showMatamo, appManager } = useContext(AppContext) + const { modal } = useDialogDispatchers() + const [visible, setVisible] = useState(props.hide) - const message = () => { - return (<>

An Opt-in version of Matomo, an open source data analytics platform is being used to improve Remix IDE.

-

We realize that our users have sensitive information in their code and that their privacy - your privacy - must be protected.

-

All data collected through Matomo is stored on our own server - no data is ever given to third parties. Our analytics reports are public: take a look.

-

We do not collect nor store any personally identifiable information (PII).

-

For more info, see: Matomo Analyitcs on Remix iDE.

-

You can change your choice in the Settings panel anytime.

) - } - - useEffect(() => { - if (visible && showMatamo) { - modal({ id: 'matomoModal', title: 'Help us to improve Remix IDE', message: message(), okLabel: 'Accept', okFn: handleModalOkClick, cancelLabel: 'Decline', cancelFn: declineModal }) - } - }, [visible]) + const message = () => { + return (<>

An Opt-in version of Matomo, an open source data analytics platform is being used to improve Remix IDE.

+

We realize that our users have sensitive information in their code and that their privacy - your privacy - must be protected.

+

All data collected through Matomo is stored on our own server - no data is ever given to third parties. Our analytics reports are public: take a look.

+

We do not collect nor store any personally identifiable information (PII).

+

For more info, see: Matomo Analyitcs on Remix iDE.

+

You can change your choice in the Settings panel anytime.

) + } - const declineModal = async () => { - settings.updateMatomoAnalyticsChoice(false) - _paq.push(['optUserOut']) - appManager.call('walkthrough', 'start') - setVisible(false) + useEffect(() => { + if (visible && showMatamo) { + modal({ id: 'matomoModal', title: 'Help us to improve Remix IDE', message: message(), okLabel: 'Accept', okFn: handleModalOkClick, cancelLabel: 'Decline', cancelFn: declineModal }) } + }, [visible]) - const handleModalOkClick = async () => { - _paq.push(['forgetUserOptOut']) - // @TODO remove next line when https://github.com/matomo-org/matomo/commit/9e10a150585522ca30ecdd275007a882a70c6df5 is used - document.cookie = 'mtm_consent_removed=; expires=Thu, 01 Jan 1970 00:00:01 GMT;' - settings.updateMatomoAnalyticsChoice(true) - appManager.call('walkthrough', 'start') - setVisible(false) - } + const declineModal = async () => { + settings.updateMatomoAnalyticsChoice(false) + _paq.push(['optUserOut']) + appManager.call('walkthrough', 'start') + setVisible(false) + } + + const handleModalOkClick = async () => { + _paq.push(['forgetUserOptOut']) + // @TODO remove next line when https://github.com/matomo-org/matomo/commit/9e10a150585522ca30ecdd275007a882a70c6df5 is used + document.cookie = 'mtm_consent_removed=; expires=Thu, 01 Jan 1970 00:00:01 GMT;' + settings.updateMatomoAnalyticsChoice(true) + appManager.call('walkthrough', 'start') + setVisible(false) + } - return (<>) + return (<>) } export default MatomoDialog diff --git a/libs/remix-ui/app/src/lib/remix-app/components/modals/modal-wrapper.tsx b/libs/remix-ui/app/src/lib/remix-app/components/modals/modal-wrapper.tsx index 38c8d8579e..fed19e6a45 100644 --- a/libs/remix-ui/app/src/lib/remix-app/components/modals/modal-wrapper.tsx +++ b/libs/remix-ui/app/src/lib/remix-app/components/modals/modal-wrapper.tsx @@ -8,128 +8,128 @@ interface ModalWrapperProps extends ModalDialogProps { } const ModalWrapper = (props: ModalWrapperProps) => { - const [state, setState] = useState() - const ref = useRef() - const formRef = useRef() - const data = useRef() + const [state, setState] = useState() + const ref = useRef() + const formRef = useRef() + const data = useRef() - const getFormData = () => { - if (formRef.current) { - const formData = new FormData(formRef.current) - const data = {} - for (const pair of formData.entries()) { - data[pair[0]] = pair[1] - } - return data - } + const getFormData = () => { + if (formRef.current) { + const formData = new FormData(formRef.current) + const data = {} + for (const pair of formData.entries()) { + data[pair[0]] = pair[1] + } + return data } + } - const onFinishPrompt = async () => { - if (ref.current === undefined && formRef.current === undefined) { - onOkFn() - } else if (formRef.current) { - (props.okFn) ? props.okFn(getFormData()) : props.resolve(getFormData()) - } else if(ref.current) { - // @ts-ignore: Object is possibly 'null'. - (props.okFn) ? props.okFn(ref.current.value) : props.resolve(ref.current.value) - } + const onFinishPrompt = async () => { + if (ref.current === undefined && formRef.current === undefined) { + onOkFn() + } else if (formRef.current) { + (props.okFn) ? props.okFn(getFormData()) : props.resolve(getFormData()) + } else if(ref.current) { + // @ts-ignore: Object is possibly 'null'. + (props.okFn) ? props.okFn(ref.current.value) : props.resolve(ref.current.value) } + } - const onOkFn = async () => { - (props.okFn) ? props.okFn(data.current) : props.resolve(data.current || true) - } - - const onCancelFn = async () => { - (props.cancelFn) ? props.cancelFn() : props.resolve(false) - } + const onOkFn = async () => { + (props.okFn) ? props.okFn(data.current) : props.resolve(data.current || true) + } - const onInputChanged = (event) => { - if (props.validationFn) { - const validation = props.validationFn(event.target.value) - setState(prevState => { - return { ...prevState, message: createModalMessage(props.defaultValue, validation), validation } - }) - } - } + const onCancelFn = async () => { + (props.cancelFn) ? props.cancelFn() : props.resolve(false) + } - const createModalMessage = (defaultValue: string, validation: ValidationResult) => { - return ( - <> - {props.message} - - {validation && !validation.valid && {validation.message}} - - ) + const onInputChanged = (event) => { + if (props.validationFn) { + const validation = props.validationFn(event.target.value) + setState(prevState => { + return { ...prevState, message: createModalMessage(props.defaultValue, validation), validation } + }) } + } - const onFormChanged = () => { - if (props.validationFn) { - const validation = props.validationFn(getFormData()) - setState(prevState => { - return { ...prevState, message: createForm(validation), validation } - }) - } - } + const createModalMessage = (defaultValue: string, validation: ValidationResult) => { + return ( + <> + {props.message} + + {validation && !validation.valid && {validation.message}} + + ) + } - const createForm = (validation: ValidationResult) => { - return ( - <> -
- {props.message} -
- {validation && !validation.valid && {validation.message}} - - ) + const onFormChanged = () => { + if (props.validationFn) { + const validation = props.validationFn(getFormData()) + setState(prevState => { + return { ...prevState, message: createForm(validation), validation } + }) } + } - useEffect(() => { - data.current = props.data - if (props.modalType) { - switch (props.modalType) { - case ModalTypes.prompt: - case ModalTypes.password: - setState({ - ...props, - okFn: onFinishPrompt, - cancelFn: onCancelFn, - message: createModalMessage(props.defaultValue, { valid: true }) - }) - break - case ModalTypes.form: - setState({ - ...props, - okFn: onFinishPrompt, - cancelFn: onCancelFn, - message: createForm({ valid: true }) - }) - break - default: - setState({ - ...props, - okFn: onOkFn, - cancelFn: onCancelFn - }) - break - } - } else { - setState({ - ...props, - okFn: onOkFn, - cancelFn: onCancelFn - }) - } - }, [props]) + const createForm = (validation: ValidationResult) => { + return ( + <> +
+ {props.message} +
+ {validation && !validation.valid && {validation.message}} + + ) + } - // reset the message and input if any, so when the modal is shown again it doesn't show the previous value. - const handleHide = () => { - setState(prevState => { - return { ...prevState, message: '' } + useEffect(() => { + data.current = props.data + if (props.modalType) { + switch (props.modalType) { + case ModalTypes.prompt: + case ModalTypes.password: + setState({ + ...props, + okFn: onFinishPrompt, + cancelFn: onCancelFn, + message: createModalMessage(props.defaultValue, { valid: true }) + }) + break + case ModalTypes.form: + setState({ + ...props, + okFn: onFinishPrompt, + cancelFn: onCancelFn, + message: createForm({ valid: true }) + }) + break + default: + setState({ + ...props, + okFn: onOkFn, + cancelFn: onCancelFn }) - props.handleHide() + break + } + } else { + setState({ + ...props, + okFn: onOkFn, + cancelFn: onCancelFn + }) } + }, [props]) - return ( - - ) + // reset the message and input if any, so when the modal is shown again it doesn't show the previous value. + const handleHide = () => { + setState(prevState => { + return { ...prevState, message: '' } + }) + props.handleHide() + } + + return ( + + ) } export default ModalWrapper diff --git a/libs/remix-ui/app/src/lib/remix-app/components/modals/origin-warning.tsx b/libs/remix-ui/app/src/lib/remix-app/components/modals/origin-warning.tsx index 61d25db2f6..02eb667011 100644 --- a/libs/remix-ui/app/src/lib/remix-app/components/modals/origin-warning.tsx +++ b/libs/remix-ui/app/src/lib/remix-app/components/modals/origin-warning.tsx @@ -3,33 +3,33 @@ import { ModalDialog } from '@remix-ui/modal-dialog' import { useDialogDispatchers } from '../../context/provider' const OriginWarning = () => { - const { alert } = useDialogDispatchers() - const [content, setContent] = useState(null) + const { alert } = useDialogDispatchers() + const [content, setContent] = useState(null) - useEffect(() => { - // check the origin and warn message - if (window.location.hostname === 'yann300.github.io') { - setContent('This UNSTABLE ALPHA branch of Remix has been moved to http://ethereum.github.io/remix-live-alpha.') - } else if (window.location.hostname === 'remix-alpha.ethereum.org' || + useEffect(() => { + // check the origin and warn message + if (window.location.hostname === 'yann300.github.io') { + setContent('This UNSTABLE ALPHA branch of Remix has been moved to http://ethereum.github.io/remix-live-alpha.') + } else if (window.location.hostname === 'remix-alpha.ethereum.org' || (window.location.hostname === 'ethereum.github.io' && window.location.pathname.indexOf('/remix-live-alpha') === 0)) { - setContent('Welcome to the Remix alpha instance. Please use it to try out latest features. But use preferably https://remix.ethereum.org for any production work.') - } else if (window.location.protocol.indexOf('http') === 0 && + setContent('Welcome to the Remix alpha instance. Please use it to try out latest features. But use preferably https://remix.ethereum.org for any production work.') + } else if (window.location.protocol.indexOf('http') === 0 && window.location.hostname !== 'remix.ethereum.org' && window.location.hostname !== 'localhost' && window.location.hostname !== '127.0.0.1') { - setContent(`The Remix IDE has moved to http://remix.ethereum.org.\n + setContent(`The Remix IDE has moved to http://remix.ethereum.org.\n This instance of Remix you are visiting WILL NOT BE UPDATED.\n Please make a backup of your contracts and start using http://remix.ethereum.org`) - } - }, []) + } + }, []) - useEffect(() => { - if (content) { - alert({ id: 'warningOriging', title: null, message: content }) - } - }, [content]) + useEffect(() => { + if (content) { + alert({ id: 'warningOriging', title: null, message: content }) + } + }, [content]) - return (<>) + return (<>) } export default OriginWarning diff --git a/libs/remix-ui/app/src/lib/remix-app/components/splashscreen.tsx b/libs/remix-ui/app/src/lib/remix-app/components/splashscreen.tsx index 44e55a4db4..766f0890ac 100644 --- a/libs/remix-ui/app/src/lib/remix-app/components/splashscreen.tsx +++ b/libs/remix-ui/app/src/lib/remix-app/components/splashscreen.tsx @@ -1,16 +1,16 @@ import React from 'react' const RemixSplashScreen = (props) => { - return (<>
- - - - - -
+ return (<>
+ + + + + +
REMIX IDE -
-
) +
+
) } export default RemixSplashScreen diff --git a/libs/remix-ui/app/src/lib/remix-app/context/context.tsx b/libs/remix-ui/app/src/lib/remix-app/context/context.tsx index ca69771f80..a914b98c58 100644 --- a/libs/remix-ui/app/src/lib/remix-app/context/context.tsx +++ b/libs/remix-ui/app/src/lib/remix-app/context/context.tsx @@ -13,11 +13,11 @@ export interface dispatchModalInterface { } export const dispatchModalContext = React.createContext({ - modal: (data: AppModal) => { }, - toast: (message: string | JSX.Element) => {}, - alert: (data: AlertModal) => {}, - handleHideModal: () => {}, - handleToaster: () => {} + modal: (data: AppModal) => { }, + toast: (message: string | JSX.Element) => {}, + alert: (data: AlertModal) => {}, + handleHideModal: () => {}, + handleToaster: () => {} }) export const modalContext = React.createContext(ModalInitialState) diff --git a/libs/remix-ui/app/src/lib/remix-app/context/provider.tsx b/libs/remix-ui/app/src/lib/remix-app/context/provider.tsx index 635500a8b3..8803e491c9 100644 --- a/libs/remix-ui/app/src/lib/remix-app/context/provider.tsx +++ b/libs/remix-ui/app/src/lib/remix-app/context/provider.tsx @@ -7,66 +7,66 @@ import { ModalTypes } from '../types' import { AppContext, dispatchModalContext, modalContext } from './context' export const ModalProvider = ({ children = [], reducer = modalReducer, initialState = ModalInitialState } = {}) => { - const [{ modals, toasters, focusModal, focusToaster }, dispatch] = useReducer(reducer, initialState) + const [{ modals, toasters, focusModal, focusToaster }, dispatch] = useReducer(reducer, initialState) - const onNextFn = async () => { - dispatch({ - type: modalActionTypes.processQueue - }) - } + const onNextFn = async () => { + dispatch({ + type: modalActionTypes.processQueue + }) + } - const modal = (modalData: AppModal) => { - const { id, title, message, validationFn, okLabel, okFn, cancelLabel, cancelFn, modalType, defaultValue, hideFn, data } = modalData - return new Promise((resolve, reject) => { - dispatch({ - type: modalActionTypes.setModal, - payload: { id, title, message, okLabel, validationFn, okFn, cancelLabel, cancelFn, modalType: modalType || ModalTypes.default, defaultValue: defaultValue, hideFn, resolve, next: onNextFn, data } - }) - }) - } + const modal = (modalData: AppModal) => { + const { id, title, message, validationFn, okLabel, okFn, cancelLabel, cancelFn, modalType, defaultValue, hideFn, data } = modalData + return new Promise((resolve, reject) => { + dispatch({ + type: modalActionTypes.setModal, + payload: { id, title, message, okLabel, validationFn, okFn, cancelLabel, cancelFn, modalType: modalType || ModalTypes.default, defaultValue: defaultValue, hideFn, resolve, next: onNextFn, data } + }) + }) + } - const alert = (modalData: AlertModal) => { - return modal({ id: modalData.id, title: modalData.title || 'Alert', message: modalData.message || modalData.title, okLabel: 'OK', okFn: (value?:any) => {}, cancelLabel: '', cancelFn: () => {} }) - } + const alert = (modalData: AlertModal) => { + return modal({ id: modalData.id, title: modalData.title || 'Alert', message: modalData.message || modalData.title, okLabel: 'OK', okFn: (value?:any) => {}, cancelLabel: '', cancelFn: () => {} }) + } - const handleHideModal = () => { - dispatch({ - type: modalActionTypes.handleHideModal, - payload: null - }) - } + const handleHideModal = () => { + dispatch({ + type: modalActionTypes.handleHideModal, + payload: null + }) + } - const toast = (message: string | JSX.Element) => { - dispatch({ - type: modalActionTypes.setToast, - payload: { message, timestamp: Date.now() } - }) - } + const toast = (message: string | JSX.Element) => { + dispatch({ + type: modalActionTypes.setToast, + payload: { message, timestamp: Date.now() } + }) + } - const handleToaster = () => { - dispatch({ - type: modalActionTypes.handleToaster, - payload: null - }) - } + const handleToaster = () => { + dispatch({ + type: modalActionTypes.handleToaster, + payload: null + }) + } - return ( - - {children} - - ) + return ( + + {children} + + ) } export const AppProvider = ({ children = [], value = {} } = {}) => { - return - {children} - + return + {children} + } export const useDialogs = () => { - return React.useContext(modalContext) + return React.useContext(modalContext) } export const useDialogDispatchers = () => { - return React.useContext(dispatchModalContext) + return React.useContext(dispatchModalContext) } diff --git a/libs/remix-ui/app/src/lib/remix-app/reducer/modals.ts b/libs/remix-ui/app/src/lib/remix-app/reducer/modals.ts index 4330d967a6..505d407068 100644 --- a/libs/remix-ui/app/src/lib/remix-app/reducer/modals.ts +++ b/libs/remix-ui/app/src/lib/remix-app/reducer/modals.ts @@ -3,81 +3,81 @@ import { ModalInitialState } from '../state/modals' import { AppModal, ModalState } from '../interface' export const modalReducer = (state: ModalState = ModalInitialState, action: ModalAction) => { - switch (action.type) { - case modalActionTypes.setModal: { - const timestamp = Date.now() - const focusModal: AppModal = { - timestamp, - id: action.payload.id || timestamp.toString(), - hide: false, - title: action.payload.title, - validationFn: action.payload.validationFn, - message: action.payload.message, - okLabel: action.payload.okLabel, - okFn: action.payload.okFn, - cancelLabel: action.payload.cancelLabel, - cancelFn: action.payload.cancelFn, - modalType: action.payload.modalType, - defaultValue: action.payload.defaultValue, - hideFn: action.payload.hideFn, - resolve: action.payload.resolve, - next: action.payload.next, - data: action.payload.data - } + switch (action.type) { + case modalActionTypes.setModal: { + const timestamp = Date.now() + const focusModal: AppModal = { + timestamp, + id: action.payload.id || timestamp.toString(), + hide: false, + title: action.payload.title, + validationFn: action.payload.validationFn, + message: action.payload.message, + okLabel: action.payload.okLabel, + okFn: action.payload.okFn, + cancelLabel: action.payload.cancelLabel, + cancelFn: action.payload.cancelFn, + modalType: action.payload.modalType, + defaultValue: action.payload.defaultValue, + hideFn: action.payload.hideFn, + resolve: action.payload.resolve, + next: action.payload.next, + data: action.payload.data + } - const modalList: AppModal[] = state.modals.slice() - modalList.push(focusModal) + const modalList: AppModal[] = state.modals.slice() + modalList.push(focusModal) - if (modalList.length === 1) { - return { ...state, modals: modalList, focusModal } - } else { - return { ...state, modals: modalList } - } - } - case modalActionTypes.handleHideModal: { - setTimeout(() => { - if (state.focusModal.hideFn) { - state.focusModal.hideFn() - } - if (state.focusModal.resolve) { - state.focusModal.resolve(undefined) - } - if (state.focusModal.next) { - state.focusModal.next() - } - }, 250) - const modalList: AppModal[] = state.modals.slice() - modalList.shift() // remove the current modal from the list - state.focusModal = { ...state.focusModal, hide: true, message: null } - return { ...state, modals: modalList } - } - case modalActionTypes.processQueue: { - const modalList: AppModal[] = state.modals.slice() - if (modalList.length) { - const focusModal = modalList[0] // extract the next modal from the list - return { ...state, modals: modalList, focusModal } - } else { - return { ...state, modals: modalList } - } + if (modalList.length === 1) { + return { ...state, modals: modalList, focusModal } + } else { + return { ...state, modals: modalList } } - case modalActionTypes.setToast: { - const toasterList = state.toasters.slice() - toasterList.push(action.payload) - if (toasterList.length === 1) { - return { ...state, toasters: toasterList, focusToaster: action.payload } - } else { - return { ...state, toasters: toasterList } - } + } + case modalActionTypes.handleHideModal: { + setTimeout(() => { + if (state.focusModal.hideFn) { + state.focusModal.hideFn() + } + if (state.focusModal.resolve) { + state.focusModal.resolve(undefined) + } + if (state.focusModal.next) { + state.focusModal.next() + } + }, 250) + const modalList: AppModal[] = state.modals.slice() + modalList.shift() // remove the current modal from the list + state.focusModal = { ...state.focusModal, hide: true, message: null } + return { ...state, modals: modalList } + } + case modalActionTypes.processQueue: { + const modalList: AppModal[] = state.modals.slice() + if (modalList.length) { + const focusModal = modalList[0] // extract the next modal from the list + return { ...state, modals: modalList, focusModal } + } else { + return { ...state, modals: modalList } } - case modalActionTypes.handleToaster: { - const toasterList = state.toasters.slice() - toasterList.shift() - if (toasterList.length) { - const toaster = toasterList[0] - return { ...state, toasters: toasterList, focusToaster: toaster } - } else { - return { ...state, toasters: [] } - } + } + case modalActionTypes.setToast: { + const toasterList = state.toasters.slice() + toasterList.push(action.payload) + if (toasterList.length === 1) { + return { ...state, toasters: toasterList, focusToaster: action.payload } + } else { + return { ...state, toasters: toasterList } } + } + case modalActionTypes.handleToaster: { + const toasterList = state.toasters.slice() + toasterList.shift() + if (toasterList.length) { + const toaster = toasterList[0] + return { ...state, toasters: toasterList, focusToaster: toaster } + } else { + return { ...state, toasters: [] } } + } + } } diff --git a/libs/remix-ui/app/src/lib/remix-app/remix-app.tsx b/libs/remix-ui/app/src/lib/remix-app/remix-app.tsx index 31cbe3dc79..f804244a91 100644 --- a/libs/remix-ui/app/src/lib/remix-app/remix-app.tsx +++ b/libs/remix-ui/app/src/lib/remix-app/remix-app.tsx @@ -16,95 +16,95 @@ interface IRemixAppUi { } const RemixApp = (props: IRemixAppUi) => { - const [appReady, setAppReady] = useState(false) - const [hideSidePanel, setHideSidePanel] = useState(false) - const [maximiseTrigger, setMaximiseTrigger] = useState(0) - const [resetTrigger, setResetTrigger] = useState(0) - const [locale, setLocale] = useState<{ code:string; messages:any }>({ code:'en', messages:{} }); - const sidePanelRef = useRef(null) + const [appReady, setAppReady] = useState(false) + const [hideSidePanel, setHideSidePanel] = useState(false) + const [maximiseTrigger, setMaximiseTrigger] = useState(0) + const [resetTrigger, setResetTrigger] = useState(0) + const [locale, setLocale] = useState<{ code:string; messages:any }>({ code:'en', messages:{} }); + const sidePanelRef = useRef(null) - useEffect(() => { - async function activateApp () { - props.app.themeModule.initTheme(() => { - setAppReady(true) - props.app.activate() - setListeners() - }) - setLocale(props.app.localeModule.currentLocale()) - } - if (props.app) { - activateApp() - } - }, []) + useEffect(() => { + async function activateApp () { + props.app.themeModule.initTheme(() => { + setAppReady(true) + props.app.activate() + setListeners() + }) + setLocale(props.app.localeModule.currentLocale()) + } + if (props.app) { + activateApp() + } + }, []) - function setListeners () { - props.app.sidePanel.events.on('toggle', () => { - setHideSidePanel(prev => { - return !prev - }) - }) - props.app.sidePanel.events.on('showing', () => { - setHideSidePanel(false) - }) + function setListeners () { + props.app.sidePanel.events.on('toggle', () => { + setHideSidePanel(prev => { + return !prev + }) + }) + props.app.sidePanel.events.on('showing', () => { + setHideSidePanel(false) + }) - props.app.layout.event.on('minimizesidepanel', () => { - // the 'showing' event always fires from sidepanel, so delay this a bit - setTimeout(() => { - setHideSidePanel(true) - }, 1000) - }) + props.app.layout.event.on('minimizesidepanel', () => { + // the 'showing' event always fires from sidepanel, so delay this a bit + setTimeout(() => { + setHideSidePanel(true) + }, 1000) + }) - props.app.layout.event.on('maximisesidepanel', () => { - setMaximiseTrigger(prev => { - return prev + 1 - }) - }) + props.app.layout.event.on('maximisesidepanel', () => { + setMaximiseTrigger(prev => { + return prev + 1 + }) + }) - props.app.layout.event.on('resetsidepanel', () => { - setResetTrigger(prev => { - return prev + 1 - }) - }) - props.app.localeModule.events.on('localeChanged', (nextLocale) => { - setLocale(nextLocale) - }) - } + props.app.layout.event.on('resetsidepanel', () => { + setResetTrigger(prev => { + return prev + 1 + }) + }) + props.app.localeModule.events.on('localeChanged', (nextLocale) => { + setLocale(nextLocale) + }) + } - const value = { - settings: props.app.settings, - showMatamo: props.app.showMatamo, - appManager: props.app.appManager, - modal: props.app.notification, - layout: props.app.layout - } + const value = { + settings: props.app.settings, + showMatamo: props.app.showMatamo, + appManager: props.app.appManager, + modal: props.app.notification, + layout: props.app.layout + } - return ( - //@ts-ignore - - - - -
-
{props.app.menuicons.render()}
-
{props.app.sidePanel.render()}
- -
- - -
-
-
-
-
{props.app.hiddenPanel.render()}
- - -
-
- ) + return ( + //@ts-ignore + + + + +
+
{props.app.menuicons.render()}
+
{props.app.sidePanel.render()}
+ +
+ + +
+
+
+
+
{props.app.hiddenPanel.render()}
+ + +
+
+ ) } export default RemixApp diff --git a/libs/remix-ui/app/src/lib/remix-app/state/modals.ts b/libs/remix-ui/app/src/lib/remix-app/state/modals.ts index 3120766dac..e44ab10c7d 100644 --- a/libs/remix-ui/app/src/lib/remix-app/state/modals.ts +++ b/libs/remix-ui/app/src/lib/remix-app/state/modals.ts @@ -1,18 +1,18 @@ import { ModalState } from '../interface' export const ModalInitialState: ModalState = { - modals: [], - toasters: [], - focusModal: { - id: '', - hide: true, - title: '', - message: '', - validationFn: () => { return { valid: true, message: '' } }, - okLabel: '', - okFn: () => { }, - cancelLabel: '', - cancelFn: () => { } - }, - focusToaster: { message: '', timestamp: 0 } + modals: [], + toasters: [], + focusModal: { + id: '', + hide: true, + title: '', + message: '', + validationFn: () => { return { valid: true, message: '' } }, + okLabel: '', + okFn: () => { }, + cancelLabel: '', + cancelFn: () => { } + }, + focusToaster: { message: '', timestamp: 0 } } diff --git a/libs/remix-ui/checkbox/src/lib/remix-ui-checkbox.tsx b/libs/remix-ui/checkbox/src/lib/remix-ui-checkbox.tsx index faf4dc8f39..a0b4dad390 100644 --- a/libs/remix-ui/checkbox/src/lib/remix-ui-checkbox.tsx +++ b/libs/remix-ui/checkbox/src/lib/remix-ui-checkbox.tsx @@ -24,67 +24,67 @@ export interface RemixUiCheckboxProps { } export const RemixUiCheckbox = ({ - id, - label, - onClick, - inputType, - name, - checked, - onChange, - itemName, - categoryId, - title, - visibility, - optionalClassName = '', - display = 'flex', - disabled, - tooltipPlacement = 'right', + id, + label, + onClick, + inputType, + name, + checked, + onChange, + itemName, + categoryId, + title, + visibility, + optionalClassName = '', + display = 'flex', + disabled, + tooltipPlacement = 'right', }: RemixUiCheckboxProps) => { - const childJSXWithTooltip = ( - -
- - -
-
- ) - const childJSX = ( -
- - -
- ) - return ( - title ? (childJSXWithTooltip) : (childJSX) - ) + const childJSXWithTooltip = ( + +
+ + +
+
+ ) + const childJSX = ( +
+ + +
+ ) + return ( + title ? (childJSXWithTooltip) : (childJSX) + ) } export default RemixUiCheckbox diff --git a/libs/remix-ui/clipboard/src/lib/copy-to-clipboard/copy-to-clipboard.tsx b/libs/remix-ui/clipboard/src/lib/copy-to-clipboard/copy-to-clipboard.tsx index fa01696604..6be52561db 100644 --- a/libs/remix-ui/clipboard/src/lib/copy-to-clipboard/copy-to-clipboard.tsx +++ b/libs/remix-ui/clipboard/src/lib/copy-to-clipboard/copy-to-clipboard.tsx @@ -16,57 +16,57 @@ interface ICopyToClipboard { getContent?: () => any } export const CopyToClipboard = (props: ICopyToClipboard) => { - const { tip = 'Copy', icon = 'fa-copy', direction = 'right', getContent, children, ...otherProps } = props - let { content } = props - const [message, setMessage] = useState(tip) + const { tip = 'Copy', icon = 'fa-copy', direction = 'right', getContent, children, ...otherProps } = props + let { content } = props + const [message, setMessage] = useState(tip) - const copyData = () => { - try { - if (content === '') { - setMessage('Cannot copy empty content!') - return - } - if (typeof content !== 'string') { - content = JSON.stringify(content, null, '\t') - } - copy(content) - setMessage('Copied') - } catch (e) { - console.error(e) - } + const copyData = () => { + try { + if (content === '') { + setMessage('Cannot copy empty content!') + return + } + if (typeof content !== 'string') { + content = JSON.stringify(content, null, '\t') + } + copy(content) + setMessage('Copied') + } catch (e) { + console.error(e) } + } - const handleClick = (e: any) => { - if (content) { // module `copy` keeps last copied thing in the memory, so don't show tooltip if nothing is copied, because nothing was added to memory - copyData() - } else { - content = getContent && getContent() - copyData() - } - e.preventDefault() + const handleClick = (e: any) => { + if (content) { // module `copy` keeps last copied thing in the memory, so don't show tooltip if nothing is copied, because nothing was added to memory + copyData() + } else { + content = getContent && getContent() + copyData() } + e.preventDefault() + } - const reset = () => { - setTimeout(() => setMessage(tip), 500) - } + const reset = () => { + setTimeout(() => setMessage(tip), 500) + } - const childJSX = ( - children || () - ) + const childJSX = ( + children || () + ) - return ( - - - {childJSX} - - - ) + return ( + + + {childJSX} + + + ) } export default CopyToClipboard diff --git a/libs/remix-ui/debugger-ui/src/hooks/extract-data.tsx b/libs/remix-ui/debugger-ui/src/hooks/extract-data.tsx index e60dccd6e7..28294a7aa6 100644 --- a/libs/remix-ui/debugger-ui/src/hooks/extract-data.tsx +++ b/libs/remix-ui/debugger-ui/src/hooks/extract-data.tsx @@ -2,57 +2,57 @@ import React, { useState, useEffect } from 'react' // eslint-disable-line import { ExtractData, ExtractFunc } from '../types' // eslint-disable-line export const useExtractData = (json, extractFunc?: ExtractFunc): Array<{ key: string, data: ExtractData }> => { - const [data, setData] = useState([]) - - useEffect(() => { - const data: Array<{ key: string, data: ExtractData }> = Object.keys(json).map((innerKey) => { - if (extractFunc) { - return { - key: innerKey, - data: extractFunc(json[innerKey], json) - } - } else { - return { - key: innerKey, - data: extractDataDefault(json[innerKey], json) - } - } - }) - - setData(data) - - return () => { - setData(null) + const [data, setData] = useState([]) + + useEffect(() => { + const data: Array<{ key: string, data: ExtractData }> = Object.keys(json).map((innerKey) => { + if (extractFunc) { + return { + key: innerKey, + data: extractFunc(json[innerKey], json) } - }, [json, extractFunc]) - - const extractDataDefault: ExtractFunc = (item, parent?) => { - const ret: ExtractData = {} - - if (item instanceof Array) { - ret.children = item.map((item, index) => { - return { key: index, value: item } - }) - ret.self = 'Array' - ret.isNode = true - ret.isLeaf = false - } else if (item instanceof Object) { - ret.children = Object.keys(item).map((key) => { - return { key: key, value: item[key] } - }) - ret.self = 'Object' - ret.isNode = true - ret.isLeaf = false - } else { - ret.self = item - ret.children = null - ret.isNode = false - ret.isLeaf = true + } else { + return { + key: innerKey, + data: extractDataDefault(json[innerKey], json) } - return ret + } + }) + + setData(data) + + return () => { + setData(null) + } + }, [json, extractFunc]) + + const extractDataDefault: ExtractFunc = (item, parent?) => { + const ret: ExtractData = {} + + if (item instanceof Array) { + ret.children = item.map((item, index) => { + return { key: index, value: item } + }) + ret.self = 'Array' + ret.isNode = true + ret.isLeaf = false + } else if (item instanceof Object) { + ret.children = Object.keys(item).map((key) => { + return { key: key, value: item[key] } + }) + ret.self = 'Object' + ret.isNode = true + ret.isLeaf = false + } else { + ret.self = item + ret.children = null + ret.isNode = false + ret.isLeaf = true } + return ret + } - return data + return data } export default useExtractData diff --git a/libs/remix-ui/debugger-ui/src/lib/api/debugger-api.ts b/libs/remix-ui/debugger-ui/src/lib/api/debugger-api.ts index 34fc44ed7e..128e6beb5d 100644 --- a/libs/remix-ui/debugger-ui/src/lib/api/debugger-api.ts +++ b/libs/remix-ui/debugger-ui/src/lib/api/debugger-api.ts @@ -7,194 +7,194 @@ const { toHexPaddedString } = util export const DebuggerApiMixin = (Base) => class extends Base { - initialWeb3 - debuggerBackend - - initDebuggerApi () { - const self = this - this.web3Provider = { - sendAsync (payload, callback) { - self.call('web3Provider', 'sendAsync', payload) - .then(result => callback(null, result)) - .catch(e => callback(e)) - } - } - this._web3 = new Web3(this.web3Provider) - // this._web3 can be overwritten and reset to initial value in 'debug' method - this.initialWeb3 = this._web3 - init.extendWeb3(this._web3) - - this.offsetToLineColumnConverter = { - async offsetToLineColumn (rawLocation, file, sources, asts) { - return await self.call('offsetToLineColumnConverter', 'offsetToLineColumn', rawLocation, file, sources, asts) - } - } - } - - // on() - // call() - // onDebugRequested() - // onRemoveHighlights() - - web3 () { - return this._web3 - } - - async discardHighlight () { - await this.call('editor', 'discardHighlight') - await this.call('editor', 'discardLineTexts' as any) - } - - async highlight (lineColumnPos, path, rawLocation, stepDetail, lineGasCost) { - await this.call('editor', 'highlight', lineColumnPos, path, '', { focus: true }) - const label = `${stepDetail.op} costs ${stepDetail.gasCost} gas - this line costs ${lineGasCost} gas - ${stepDetail.gas} gas left` - const linetext: lineText = { - content: label, - position: lineColumnPos, - hide: false, - className: 'text-muted small', - afterContentClassName: 'text-muted small fas fa-gas-pump pl-4', - from: 'debugger', - hoverMessage: [{ - value: label, - }, - ], - } - await this.call('editor', 'addLineText' as any, linetext, path) - } - - async getFile (path) { - return await this.call('fileManager', 'getFile', path) - } - - async setFile (path, content) { - await this.call('fileManager', 'setFile', path, content) - } - - onBreakpointCleared (listener) { - this.onBreakpointClearedListener = listener - } - - onBreakpointAdded (listener) { - this.onBreakpointAddedListener = listener - } - - onEditorContentChanged (listener) { - this.onEditorContentChangedListener = listener - } - - onEnvChanged (listener) { - this.onEnvChangedListener = listener - } - - onDebugRequested (listener) { - this.onDebugRequestedListener = listener - } - - onRemoveHighlights (listener) { - this.onRemoveHighlightsListener = listener - } - - async fetchContractAndCompile (address, receipt) { - const target = (address && traceHelper.isContractCreation(address)) ? receipt.contractAddress : address - const targetAddress = target || receipt.contractAddress || receipt.to - const codeAtAddress = await this._web3.eth.getCode(targetAddress) - const output = await this.call('fetchAndCompile', 'resolve', targetAddress, codeAtAddress, '.debug') - if (output) { - return new CompilerAbstract(output.languageversion, output.data, output.source) - } - return null - } - - async getDebugWeb3 () { - let web3 - let network + initialWeb3 + debuggerBackend + + initDebuggerApi () { + const self = this + this.web3Provider = { + sendAsync (payload, callback) { + self.call('web3Provider', 'sendAsync', payload) + .then(result => callback(null, result)) + .catch(e => callback(e)) + } + } + this._web3 = new Web3(this.web3Provider) + // this._web3 can be overwritten and reset to initial value in 'debug' method + this.initialWeb3 = this._web3 + init.extendWeb3(this._web3) + + this.offsetToLineColumnConverter = { + async offsetToLineColumn (rawLocation, file, sources, asts) { + return await self.call('offsetToLineColumnConverter', 'offsetToLineColumn', rawLocation, file, sources, asts) + } + } + } + + // on() + // call() + // onDebugRequested() + // onRemoveHighlights() + + web3 () { + return this._web3 + } + + async discardHighlight () { + await this.call('editor', 'discardHighlight') + await this.call('editor', 'discardLineTexts' as any) + } + + async highlight (lineColumnPos, path, rawLocation, stepDetail, lineGasCost) { + await this.call('editor', 'highlight', lineColumnPos, path, '', { focus: true }) + const label = `${stepDetail.op} costs ${stepDetail.gasCost} gas - this line costs ${lineGasCost} gas - ${stepDetail.gas} gas left` + const linetext: lineText = { + content: label, + position: lineColumnPos, + hide: false, + className: 'text-muted small', + afterContentClassName: 'text-muted small fas fa-gas-pump pl-4', + from: 'debugger', + hoverMessage: [{ + value: label, + }, + ], + } + await this.call('editor', 'addLineText' as any, linetext, path) + } + + async getFile (path) { + return await this.call('fileManager', 'getFile', path) + } + + async setFile (path, content) { + await this.call('fileManager', 'setFile', path, content) + } + + onBreakpointCleared (listener) { + this.onBreakpointClearedListener = listener + } + + onBreakpointAdded (listener) { + this.onBreakpointAddedListener = listener + } + + onEditorContentChanged (listener) { + this.onEditorContentChangedListener = listener + } + + onEnvChanged (listener) { + this.onEnvChangedListener = listener + } + + onDebugRequested (listener) { + this.onDebugRequestedListener = listener + } + + onRemoveHighlights (listener) { + this.onRemoveHighlightsListener = listener + } + + async fetchContractAndCompile (address, receipt) { + const target = (address && traceHelper.isContractCreation(address)) ? receipt.contractAddress : address + const targetAddress = target || receipt.contractAddress || receipt.to + const codeAtAddress = await this._web3.eth.getCode(targetAddress) + const output = await this.call('fetchAndCompile', 'resolve', targetAddress, codeAtAddress, '.debug') + if (output) { + return new CompilerAbstract(output.languageversion, output.data, output.source) + } + return null + } + + async getDebugWeb3 () { + let web3 + let network + try { + network = await this.call('network', 'detectNetwork') + } catch (e) { + web3 = this.web3() + } + if (!web3) { + const webDebugNode = init.web3DebugNode(network.name) + web3 = !webDebugNode ? this.web3() : webDebugNode + } + init.extendWeb3(web3) + return web3 + } + + async getTrace (hash) { + if (!hash) return + const web3 = await this.getDebugWeb3() + const currentReceipt = await web3.eth.getTransactionReceipt(hash) + const debug = new Debugger({ + web3, + offsetToLineColumnConverter: this.offsetToLineColumnConverter, + compilationResult: async (address) => { try { - network = await this.call('network', 'detectNetwork') + return await this.fetchContractAndCompile(address, currentReceipt) } catch (e) { - web3 = this.web3() - } - if (!web3) { - const webDebugNode = init.web3DebugNode(network.name) - web3 = !webDebugNode ? this.web3() : webDebugNode + console.error(e) } - init.extendWeb3(web3) - return web3 - } - - async getTrace (hash) { - if (!hash) return - const web3 = await this.getDebugWeb3() - const currentReceipt = await web3.eth.getTransactionReceipt(hash) - const debug = new Debugger({ - web3, - offsetToLineColumnConverter: this.offsetToLineColumnConverter, - compilationResult: async (address) => { - try { - return await this.fetchContractAndCompile(address, currentReceipt) - } catch (e) { - console.error(e) - } - return null - }, - debugWithGeneratedSources: false - }) - const trace = await debug.debugger.traceManager.getTrace(hash) - trace.structLogs = trace.structLogs.map((step) => { - const stack = [] - for (const prop in step.stack) { - if (prop !== 'length') { - stack.push(toHexPaddedString(step.stack[prop])) - } - } - step.stack = stack - return step - }) - return trace - } - - debug (hash, web3?) { - try { - this.call('fetchAndCompile', 'clearCache') - } catch (e) { - console.error(e) - } - if (web3) this._web3 = web3 - else this._web3 = this.initialWeb3 - init.extendWeb3(this._web3) - if (this.onDebugRequestedListener) { - this.onDebugRequestedListener(hash, web3).then((debuggerBackend) => { - this.debuggerBackend = debuggerBackend - }) + return null + }, + debugWithGeneratedSources: false + }) + const trace = await debug.debugger.traceManager.getTrace(hash) + trace.structLogs = trace.structLogs.map((step) => { + const stack = [] + for (const prop in step.stack) { + if (prop !== 'length') { + stack.push(toHexPaddedString(step.stack[prop])) } - } - - onActivation () { - this.on('editor', 'breakpointCleared', (fileName, row) => { if (this.onBreakpointClearedListener) this.onBreakpointClearedListener(fileName, row) }) - this.on('editor', 'breakpointAdded', (fileName, row) => { if (this.onBreakpointAddedListener) this.onBreakpointAddedListener(fileName, row) }) - this.on('editor', 'contentChanged', () => { if (this.onEditorContentChangedListener) this.onEditorContentChangedListener() }) - this.on('network', 'providerChanged', (provider) => { if (this.onEnvChangedListener) this.onEnvChangedListener(provider) }) - } - - onDeactivation () { - if (this.onRemoveHighlightsListener) this.onRemoveHighlightsListener() - this.off('editor', 'breakpointCleared') - this.off('editor', 'breakpointAdded') - this.off('editor', 'contentChanged') - } - - showMessage (title: string, message: string) {} - - onStartDebugging (debuggerBackend: any) { - this.call('layout', 'maximiseSidePanel') - this.emit('startDebugging') + } + step.stack = stack + return step + }) + return trace + } + + debug (hash, web3?) { + try { + this.call('fetchAndCompile', 'clearCache') + } catch (e) { + console.error(e) + } + if (web3) this._web3 = web3 + else this._web3 = this.initialWeb3 + init.extendWeb3(this._web3) + if (this.onDebugRequestedListener) { + this.onDebugRequestedListener(hash, web3).then((debuggerBackend) => { this.debuggerBackend = debuggerBackend - } - - onStopDebugging () { - this.call('layout', 'resetSidePanel') - this.emit('stopDebugging') - this.debuggerBackend = null - } + }) + } + } + + onActivation () { + this.on('editor', 'breakpointCleared', (fileName, row) => { if (this.onBreakpointClearedListener) this.onBreakpointClearedListener(fileName, row) }) + this.on('editor', 'breakpointAdded', (fileName, row) => { if (this.onBreakpointAddedListener) this.onBreakpointAddedListener(fileName, row) }) + this.on('editor', 'contentChanged', () => { if (this.onEditorContentChangedListener) this.onEditorContentChangedListener() }) + this.on('network', 'providerChanged', (provider) => { if (this.onEnvChangedListener) this.onEnvChangedListener(provider) }) + } + + onDeactivation () { + if (this.onRemoveHighlightsListener) this.onRemoveHighlightsListener() + this.off('editor', 'breakpointCleared') + this.off('editor', 'breakpointAdded') + this.off('editor', 'contentChanged') + } + + showMessage (title: string, message: string) {} + + onStartDebugging (debuggerBackend: any) { + this.call('layout', 'maximiseSidePanel') + this.emit('startDebugging') + this.debuggerBackend = debuggerBackend + } + + onStopDebugging () { + this.call('layout', 'resetSidePanel') + this.emit('stopDebugging') + this.debuggerBackend = null + } } diff --git a/libs/remix-ui/debugger-ui/src/lib/button-navigator/button-navigator.tsx b/libs/remix-ui/debugger-ui/src/lib/button-navigator/button-navigator.tsx index c8d09866aa..69018861b1 100644 --- a/libs/remix-ui/debugger-ui/src/lib/button-navigator/button-navigator.tsx +++ b/libs/remix-ui/debugger-ui/src/lib/button-navigator/button-navigator.tsx @@ -3,7 +3,23 @@ import React, { useState, useEffect } from 'react' // eslint-disable-line import './button-navigator.css' export const ButtonNavigation = ({ stepOverBack, stepIntoBack, stepIntoForward, stepOverForward, jumpOut, jumpPreviousBreakpoint, jumpNextBreakpoint, jumpToException, revertedReason, stepState, jumpOutDisabled }) => { - const [state, setState] = useState({ + const [state, setState] = useState({ + intoBackDisabled: true, + overBackDisabled: true, + intoForwardDisabled: true, + overForwardDisabled: true, + jumpOutDisabled: true, + jumpNextBreakpointDisabled: true, + jumpPreviousBreakpointDisabled: true + }) + + useEffect(() => { + stepChanged(stepState, jumpOutDisabled) + }, [stepState, jumpOutDisabled]) + + const reset = () => { + setState(() => { + return { intoBackDisabled: true, overBackDisabled: true, intoForwardDisabled: true, @@ -11,178 +27,162 @@ export const ButtonNavigation = ({ stepOverBack, stepIntoBack, stepIntoForward, jumpOutDisabled: true, jumpNextBreakpointDisabled: true, jumpPreviousBreakpointDisabled: true + } }) + } - useEffect(() => { - stepChanged(stepState, jumpOutDisabled) - }, [stepState, jumpOutDisabled]) - - const reset = () => { - setState(() => { - return { - intoBackDisabled: true, - overBackDisabled: true, - intoForwardDisabled: true, - overForwardDisabled: true, - jumpOutDisabled: true, - jumpNextBreakpointDisabled: true, - jumpPreviousBreakpointDisabled: true - } - }) + const stepChanged = (stepState, jumpOutDisabled) => { + if (stepState === 'invalid') { + // TODO: probably not necessary, already implicit done in the next steps + reset() + return } - const stepChanged = (stepState, jumpOutDisabled) => { - if (stepState === 'invalid') { - // TODO: probably not necessary, already implicit done in the next steps - reset() - return - } - - setState(() => { - return { - intoBackDisabled: stepState === 'initial', - overBackDisabled: stepState === 'initial', - jumpPreviousBreakpointDisabled: stepState === 'initial', - intoForwardDisabled: stepState === 'end', - overForwardDisabled: stepState === 'end', - jumpNextBreakpointDisabled: stepState === 'end', - jumpOutDisabled: (jumpOutDisabled !== null) && (jumpOutDisabled !== undefined) ? jumpOutDisabled : true - } - }) - } + setState(() => { + return { + intoBackDisabled: stepState === 'initial', + overBackDisabled: stepState === 'initial', + jumpPreviousBreakpointDisabled: stepState === 'initial', + intoForwardDisabled: stepState === 'end', + overForwardDisabled: stepState === 'end', + jumpNextBreakpointDisabled: stepState === 'end', + jumpOutDisabled: (jumpOutDisabled !== null) && (jumpOutDisabled !== undefined) ? jumpOutDisabled : true + } + }) + } - const stepBtnStyle = 'd-flex align-items-center justify-content-center btn btn-primary btn-sm stepButton h-75 m-0 p-1' - const disableStepBtnStyle = 'stepButtonDisabled' - const disableJumpBtnStyle = 'jumpButtonDisabled' - const stepMarkupStructure = { - stepOverBackJSX : { - markup: (
{ stepOverBack && stepOverBack() }}> - -
), - placement: 'top-start', - tagId: 'overbackTooltip', - tooltipMsg: 'Step over back' - }, - stepBackJSX : { - markup: ( -
{ stepIntoBack && stepIntoBack() }} data-id="buttonNavigatorIntoBack" id="buttonNavigatorIntoBackContainer"> - -
- ), - placement: 'top-start', - tagId: 'intobackTooltip', - tooltipMsg: 'Step back' - }, + const stepBtnStyle = 'd-flex align-items-center justify-content-center btn btn-primary btn-sm stepButton h-75 m-0 p-1' + const disableStepBtnStyle = 'stepButtonDisabled' + const disableJumpBtnStyle = 'jumpButtonDisabled' + const stepMarkupStructure = { + stepOverBackJSX : { + markup: (
{ stepOverBack && stepOverBack() }}> + +
), + placement: 'top-start', + tagId: 'overbackTooltip', + tooltipMsg: 'Step over back' + }, + stepBackJSX : { + markup: ( +
{ stepIntoBack && stepIntoBack() }} data-id="buttonNavigatorIntoBack" id="buttonNavigatorIntoBackContainer"> + +
+ ), + placement: 'top-start', + tagId: 'intobackTooltip', + tooltipMsg: 'Step back' + }, - stepIntoJSX : { - markup: ( -
{ stepIntoForward && stepIntoForward() }} data-id="buttonNavigatorIntoForward" id="buttonNavigatorIntoFowardContainer"> - -
- ), - placement: 'top-start', - tagId: 'intoforwardTooltip', - tooltipMsg: 'Step into' - }, - stepOverForwardJSX : { - markup: ( -
{ stepOverForward && stepOverForward() }} data-id="buttonNavigatorOverForward" id="buttonNavigatorOverForwardContainer"> - -
- ), - placement: 'top-end', - tagId: 'overbackTooltip', - tooltipMsg: 'Step over forward', - } + stepIntoJSX : { + markup: ( +
{ stepIntoForward && stepIntoForward() }} data-id="buttonNavigatorIntoForward" id="buttonNavigatorIntoFowardContainer"> + +
+ ), + placement: 'top-start', + tagId: 'intoforwardTooltip', + tooltipMsg: 'Step into' + }, + stepOverForwardJSX : { + markup: ( +
{ stepOverForward && stepOverForward() }} data-id="buttonNavigatorOverForward" id="buttonNavigatorOverForwardContainer"> + +
+ ), + placement: 'top-end', + tagId: 'overbackTooltip', + tooltipMsg: 'Step over forward', } - const jumpMarkupStructure = { - jumpPreviousBreakpointJSX : { - markup: ( -
{ jumpPreviousBreakpoint && jumpPreviousBreakpoint() }} data-id="buttonNavigatorJumpPreviousBreakpoint"> - -
- ), - placement: 'bottom-start', - tagId: 'jumppreviousbreakpointTooltip', - tooltipMsg: 'Jump to the previous breakpoint' - }, - jumpOutJSX : { - markup: ( -
{ jumpOut && jumpOut() }} data-id="buttonNavigatorJumpOut" id="buttonNavigatorJumpOutContainer"> - -
- ), - placement: 'bottom-end', - tagId: 'jumpoutTooltip', - tooltipMsg: 'Jump out' - }, - jumpNextBreakpointJSX : { - markup: ( -
{ jumpNextBreakpoint && jumpNextBreakpoint() }} data-id="buttonNavigatorJumpNextBreakpoint" id="buttonNavigatorJumpNextBreakpointContainer"> - -
- ), - placement: 'bottom-end', - tagId: 'jumpnextbreakpointTooltip', - tooltipMsg: 'Jump to the next breakpoint' - } + } + const jumpMarkupStructure = { + jumpPreviousBreakpointJSX : { + markup: ( +
{ jumpPreviousBreakpoint && jumpPreviousBreakpoint() }} data-id="buttonNavigatorJumpPreviousBreakpoint"> + +
+ ), + placement: 'bottom-start', + tagId: 'jumppreviousbreakpointTooltip', + tooltipMsg: 'Jump to the previous breakpoint' + }, + jumpOutJSX : { + markup: ( +
{ jumpOut && jumpOut() }} data-id="buttonNavigatorJumpOut" id="buttonNavigatorJumpOutContainer"> + +
+ ), + placement: 'bottom-end', + tagId: 'jumpoutTooltip', + tooltipMsg: 'Jump out' + }, + jumpNextBreakpointJSX : { + markup: ( +
{ jumpNextBreakpoint && jumpNextBreakpoint() }} data-id="buttonNavigatorJumpNextBreakpoint" id="buttonNavigatorJumpNextBreakpointContainer"> + +
+ ), + placement: 'bottom-end', + tagId: 'jumpnextbreakpointTooltip', + tooltipMsg: 'Jump to the next breakpoint' } + } - return ( -
-
- { - Object.keys(stepMarkupStructure).map(x => ( - - {stepMarkupStructure[x].markup} - - )) - } -
+ return ( +
+
+ { + Object.keys(stepMarkupStructure).map(x => ( + + {stepMarkupStructure[x].markup} + + )) + } +
-
- { - Object.keys(jumpMarkupStructure).map(x => ( - - {jumpMarkupStructure[x].markup} - - )) - } -
-
- This call has reverted, state changes made during the call will be reverted. - This call will run out of gas. - The parent call will throw an exception -
Click { jumpToException && jumpToException() }}>here to jump where the call reverted.
-
-
- ) +
+ { + Object.keys(jumpMarkupStructure).map(x => ( + + {jumpMarkupStructure[x].markup} + + )) + } +
+
+ This call has reverted, state changes made during the call will be reverted. + This call will run out of gas. + The parent call will throw an exception +
Click { jumpToException && jumpToException() }}>here to jump where the call reverted.
+
+
+ ) } export default ButtonNavigation diff --git a/libs/remix-ui/debugger-ui/src/lib/debugger-ui.tsx b/libs/remix-ui/debugger-ui/src/lib/debugger-ui.tsx index c8942526d1..afd68a9ee1 100644 --- a/libs/remix-ui/debugger-ui/src/lib/debugger-ui.tsx +++ b/libs/remix-ui/debugger-ui/src/lib/debugger-ui.tsx @@ -13,429 +13,429 @@ import './debugger-ui.css' const _paq = (window as any)._paq = (window as any)._paq || [] export const DebuggerUI = (props: DebuggerUIProps) => { - const debuggerModule = props.debuggerAPI - const [state, setState] = useState({ - isActive: false, - debugger: null, - currentReceipt: { - contractAddress: null, - to: null - }, - currentBlock: null, - currentTransaction: null, - blockNumber: null, - txNumber: '', - debugging: false, - opt: { - debugWithGeneratedSources: false, - debugWithLocalNode: false - }, - toastMessage: '', - validationError: '', - txNumberIsEmpty: true, - isLocalNodeUsed: false, - sourceLocationStatus: '' + const debuggerModule = props.debuggerAPI + const [state, setState] = useState({ + isActive: false, + debugger: null, + currentReceipt: { + contractAddress: null, + to: null + }, + currentBlock: null, + currentTransaction: null, + blockNumber: null, + txNumber: '', + debugging: false, + opt: { + debugWithGeneratedSources: false, + debugWithLocalNode: false + }, + toastMessage: '', + validationError: '', + txNumberIsEmpty: true, + isLocalNodeUsed: false, + sourceLocationStatus: '' + }) + + if (props.onReady) { + props.onReady({ + globalContext: () => { + return { + block: state.currentBlock, + tx: state.currentTransaction, + receipt: state.currentReceipt + } + } }) + } - if (props.onReady) { - props.onReady({ - globalContext: () => { - return { - block: state.currentBlock, - tx: state.currentTransaction, - receipt: state.currentReceipt - } - } - }) + const panelsRef = useRef(null) + const debuggerTopRef = useRef(null) + + const handleResize = () => { + if (panelsRef.current && debuggerTopRef.current) { + panelsRef.current.style.height = (window.innerHeight - debuggerTopRef.current.clientHeight) - debuggerTopRef.current.offsetTop - 7 +'px' + } + } + + useEffect(() => { + handleResize() + }, []) + + useEffect(() => { + window.addEventListener('resize', handleResize) + // TODO: not a good way to wait on the ref doms element to be rendered of course + setTimeout(() => + handleResize(), 2000) + return () => window.removeEventListener('resize', handleResize) + }, [state.debugging, state.isActive]) + + useEffect(() => { + return unLoad() + }, []) + + debuggerModule.onDebugRequested((hash, web3?) => { + if (hash) return debug(hash, web3) + }) + + debuggerModule.onRemoveHighlights(async () => { + await debuggerModule.discardHighlight() + }) + + useEffect(() => { + const setEditor = () => { + debuggerModule.onBreakpointCleared((fileName, row) => { + if (state.debugger) state.debugger.breakPointManager.remove({ fileName: fileName, row: row }) + }) + + debuggerModule.onBreakpointAdded((fileName, row) => { + if (state.debugger) state.debugger.breakPointManager.add({ fileName: fileName, row: row }) + }) + + debuggerModule.onEditorContentChanged(() => { + if (state.debugger) unLoad() + }) } - const panelsRef = useRef(null) - const debuggerTopRef = useRef(null) + setEditor() - const handleResize = () => { - if (panelsRef.current && debuggerTopRef.current) { - panelsRef.current.style.height = (window.innerHeight - debuggerTopRef.current.clientHeight) - debuggerTopRef.current.offsetTop - 7 +'px' - } + const providerChanged = () => { + debuggerModule.onEnvChanged((provider) => { + setState(prevState => { + const isLocalNodeUsed = !provider.startsWith('vm') && provider !== 'injected' + return { ...prevState, isLocalNodeUsed: isLocalNodeUsed } + }) + }) } - useEffect(() => { - handleResize() - }, []) - - useEffect(() => { - window.addEventListener('resize', handleResize) - // TODO: not a good way to wait on the ref doms element to be rendered of course - setTimeout(() => - handleResize(), 2000) - return () => window.removeEventListener('resize', handleResize) - }, [state.debugging, state.isActive]) + providerChanged() + }, [state.debugger]) - useEffect(() => { - return unLoad() - }, []) + const listenToEvents = (debuggerInstance, currentReceipt) => { + if (!debuggerInstance) return - debuggerModule.onDebugRequested((hash, web3?) => { - if (hash) return debug(hash, web3) + debuggerInstance.event.register('debuggerStatus', async (isActive) => { + await debuggerModule.discardHighlight() + setState(prevState => { + return { ...prevState, isActive } + }) }) - debuggerModule.onRemoveHighlights(async () => { - await debuggerModule.discardHighlight() + debuggerInstance.event.register('locatingBreakpoint', async (isActive) => { + setState(prevState => { + return { ...prevState, sourceLocationStatus: 'Locating breakpoint, this might take a while...' } + }) }) - useEffect(() => { - const setEditor = () => { - debuggerModule.onBreakpointCleared((fileName, row) => { - if (state.debugger) state.debugger.breakPointManager.remove({ fileName: fileName, row: row }) - }) - - debuggerModule.onBreakpointAdded((fileName, row) => { - if (state.debugger) state.debugger.breakPointManager.add({ fileName: fileName, row: row }) - }) + debuggerInstance.event.register('noBreakpointHit', async (isActive) => { + setState(prevState => { + return { ...prevState, sourceLocationStatus: '' } + }) + }) - debuggerModule.onEditorContentChanged(() => { - if (state.debugger) unLoad() - }) + debuggerInstance.event.register('newSourceLocation', async (lineColumnPos, rawLocation, generatedSources, address, stepDetail, lineGasCost) => { + if (!lineColumnPos) { + await debuggerModule.discardHighlight() + setState(prevState => { + return { ...prevState, sourceLocationStatus: 'Source location not available, neither in Sourcify nor in Etherscan. Please make sure the Etherscan api key is provided in the settings.' } + }) + return + } + const contracts = await debuggerModule.fetchContractAndCompile( + address || currentReceipt.contractAddress || currentReceipt.to, + currentReceipt) + if (contracts) { + let path = contracts.getSourceName(rawLocation.file) + if (!path) { + // check in generated sources + for (const source of generatedSources) { + if (source.id === rawLocation.file) { + path = `browser/.debugger/generated-sources/${source.name}` + let content + try { + content = await debuggerModule.getFile(path) + } catch (e) { + const message = 'Unable to fetch generated sources, the file probably doesn\'t exist yet.' + console.log(message, ' ', e) + } + if (content !== source.contents) { + await debuggerModule.setFile(path, source.contents) + } + break + } + } } - - setEditor() - - const providerChanged = () => { - debuggerModule.onEnvChanged((provider) => { - setState(prevState => { - const isLocalNodeUsed = !provider.startsWith('vm') && provider !== 'injected' - return { ...prevState, isLocalNodeUsed: isLocalNodeUsed } - }) - }) + if (path) { + setState(prevState => { + return { ...prevState, sourceLocationStatus: '' } + }) + await debuggerModule.discardHighlight() + await debuggerModule.highlight(lineColumnPos, path, rawLocation, stepDetail, lineGasCost) } + } + }) - providerChanged() - }, [state.debugger]) - - const listenToEvents = (debuggerInstance, currentReceipt) => { - if (!debuggerInstance) return - - debuggerInstance.event.register('debuggerStatus', async (isActive) => { - await debuggerModule.discardHighlight() - setState(prevState => { - return { ...prevState, isActive } - }) - }) - - debuggerInstance.event.register('locatingBreakpoint', async (isActive) => { - setState(prevState => { - return { ...prevState, sourceLocationStatus: 'Locating breakpoint, this might take a while...' } - }) - }) - - debuggerInstance.event.register('noBreakpointHit', async (isActive) => { - setState(prevState => { - return { ...prevState, sourceLocationStatus: '' } - }) - }) + debuggerInstance.event.register('debuggerUnloaded', () => unLoad()) + } - debuggerInstance.event.register('newSourceLocation', async (lineColumnPos, rawLocation, generatedSources, address, stepDetail, lineGasCost) => { - if (!lineColumnPos) { - await debuggerModule.discardHighlight() - setState(prevState => { - return { ...prevState, sourceLocationStatus: 'Source location not available, neither in Sourcify nor in Etherscan. Please make sure the Etherscan api key is provided in the settings.' } - }) - return - } - const contracts = await debuggerModule.fetchContractAndCompile( - address || currentReceipt.contractAddress || currentReceipt.to, - currentReceipt) - if (contracts) { - let path = contracts.getSourceName(rawLocation.file) - if (!path) { - // check in generated sources - for (const source of generatedSources) { - if (source.id === rawLocation.file) { - path = `browser/.debugger/generated-sources/${source.name}` - let content - try { - content = await debuggerModule.getFile(path) - } catch (e) { - const message = 'Unable to fetch generated sources, the file probably doesn\'t exist yet.' - console.log(message, ' ', e) - } - if (content !== source.contents) { - await debuggerModule.setFile(path, source.contents) - } - break - } - } - } - if (path) { - setState(prevState => { - return { ...prevState, sourceLocationStatus: '' } - }) - await debuggerModule.discardHighlight() - await debuggerModule.highlight(lineColumnPos, path, rawLocation, stepDetail, lineGasCost) - } - } - }) + const requestDebug = (blockNumber, txNumber, tx) => { + startDebugging(blockNumber, txNumber, tx) + } - debuggerInstance.event.register('debuggerUnloaded', () => unLoad()) - } - - const requestDebug = (blockNumber, txNumber, tx) => { - startDebugging(blockNumber, txNumber, tx) - } + const updateTxNumberFlag = (empty: boolean) => { + setState(prevState => { + return { + ...prevState, + txNumberIsEmpty: empty, + validationError: '' + } + }) + } - const updateTxNumberFlag = (empty: boolean) => { - setState(prevState => { - return { - ...prevState, - txNumberIsEmpty: empty, - validationError: '' - } - }) + const unloadRequested = (blockNumber, txIndex, tx) => { + unLoad() + setState(prevState => { + return { + ...prevState, + sourceLocationStatus: '' + } + }) + } + + const unLoad = () => { + debuggerModule.onStopDebugging() + if (state.debugger) state.debugger.unload() + setState(prevState => { + return { + ...prevState, + isActive: false, + debugger: null, + currentReceipt: { + contractAddress: null, + to: null + }, + currentBlock: null, + currentTransaction: null, + blockNumber: null, + ready: { + vmDebugger: false, + vmDebuggerHead: false + }, + debugging: false + } + }) + } + const startDebugging = async (blockNumber, txNumber, tx, optWeb3?) => { + if (state.debugger) { + unLoad() + await (new Promise((resolve) => setTimeout(() => resolve({}), 1000))) } - - const unloadRequested = (blockNumber, txIndex, tx) => { - unLoad() - setState(prevState => { - return { - ...prevState, - sourceLocationStatus: '' - } - }) + if (!txNumber) return + setState(prevState => { + return { + ...prevState, + txNumber: txNumber, + sourceLocationStatus: '' + } + }) + if (!isValidHash(txNumber)) { + setState(prevState => { + return { + ...prevState, + validationError: 'Invalid transaction hash.' + } + }) + return } - const unLoad = () => { - debuggerModule.onStopDebugging() - if (state.debugger) state.debugger.unload() + const web3 = optWeb3 || (state.opt.debugWithLocalNode ? await debuggerModule.web3() : await debuggerModule.getDebugWeb3()) + try { + const networkId = await web3.eth.net.getId() + _paq.push(['trackEvent', 'debugger', 'startDebugging', networkId]) + if (networkId === 42) { setState(prevState => { - return { - ...prevState, - isActive: false, - debugger: null, - currentReceipt: { - contractAddress: null, - to: null - }, - currentBlock: null, - currentTransaction: null, - blockNumber: null, - ready: { - vmDebugger: false, - vmDebuggerHead: false - }, - debugging: false - } + return { + ...prevState, + validationError: 'Unfortunately, the Kovan network is not supported.' + } }) + return + } + } catch (e) { + console.error(e) } - const startDebugging = async (blockNumber, txNumber, tx, optWeb3?) => { - if (state.debugger) { - unLoad() - await (new Promise((resolve) => setTimeout(() => resolve({}), 1000))) - } - if (!txNumber) return - setState(prevState => { - return { - ...prevState, - txNumber: txNumber, - sourceLocationStatus: '' - } - }) - if (!isValidHash(txNumber)) { - setState(prevState => { - return { - ...prevState, - validationError: 'Invalid transaction hash.' - } - }) - return + let currentReceipt + let currentBlock + let currentTransaction + try { + currentReceipt = await web3.eth.getTransactionReceipt(txNumber) + currentBlock = await web3.eth.getBlock(currentReceipt.blockHash) + currentTransaction = await web3.eth.getTransaction(txNumber) + } catch (e) { + setState(prevState => { + return { + ...prevState, + validationError: e.message } + }) + console.log(e.message) + } - const web3 = optWeb3 || (state.opt.debugWithLocalNode ? await debuggerModule.web3() : await debuggerModule.getDebugWeb3()) + const localCache = {} + const debuggerInstance = new Debugger({ + web3, + offsetToLineColumnConverter: debuggerModule.offsetToLineColumnConverter, + compilationResult: async (address) => { try { - const networkId = await web3.eth.net.getId() - _paq.push(['trackEvent', 'debugger', 'startDebugging', networkId]) - if (networkId === 42) { - setState(prevState => { - return { - ...prevState, - validationError: 'Unfortunately, the Kovan network is not supported.' - } - }) - return - } + if (!localCache[address]) localCache[address] = await debuggerModule.fetchContractAndCompile(address, currentReceipt) + return localCache[address] } catch (e) { - console.error(e) + // debuggerModule.showMessage('Debugging error', 'Unable to fetch a transaction.') + console.error(e) } - let currentReceipt - let currentBlock - let currentTransaction - try { - currentReceipt = await web3.eth.getTransactionReceipt(txNumber) - currentBlock = await web3.eth.getBlock(currentReceipt.blockHash) - currentTransaction = await web3.eth.getTransaction(txNumber) - } catch (e) { - setState(prevState => { - return { - ...prevState, - validationError: e.message - } - }) - console.log(e.message) - } - - const localCache = {} - const debuggerInstance = new Debugger({ - web3, - offsetToLineColumnConverter: debuggerModule.offsetToLineColumnConverter, - compilationResult: async (address) => { - try { - if (!localCache[address]) localCache[address] = await debuggerModule.fetchContractAndCompile(address, currentReceipt) - return localCache[address] - } catch (e) { - // debuggerModule.showMessage('Debugging error', 'Unable to fetch a transaction.') - console.error(e) - } - return null - }, - debugWithGeneratedSources: state.opt.debugWithGeneratedSources - }) - - setTimeout(async() => { - debuggerModule.onStartDebugging(debuggerInstance) - try { - await debuggerInstance.debug(blockNumber, txNumber, tx, () => { - listenToEvents(debuggerInstance, currentReceipt) - setState(prevState => { - return { - ...prevState, - blockNumber, - txNumber, - debugging: true, - currentReceipt, - currentBlock, - currentTransaction, - debugger: debuggerInstance, - toastMessage: `debugging ${txNumber}`, - validationError: '' - } - }) - }) - } catch (error) { - unLoad() - setState(prevState => { - let errorMsg = error.message || error - if (typeof errorMsg !== 'string') { - errorMsg = JSON.stringify(errorMsg) + '. Possible error: the current endpoint does not support retrieving the trace of a transaction.' - } - return { - ...prevState, - validationError: errorMsg - } - }) - } - }, 300) - handleResize() - - return debuggerInstance - } + return null + }, + debugWithGeneratedSources: state.opt.debugWithGeneratedSources + }) - const debug = (txHash, web3?) => { - setState(prevState => { + setTimeout(async() => { + debuggerModule.onStartDebugging(debuggerInstance) + try { + await debuggerInstance.debug(blockNumber, txNumber, tx, () => { + listenToEvents(debuggerInstance, currentReceipt) + setState(prevState => { return { - ...prevState, - validationError: '', - txNumber: txHash, - sourceLocationStatus: '' + ...prevState, + blockNumber, + txNumber, + debugging: true, + currentReceipt, + currentBlock, + currentTransaction, + debugger: debuggerInstance, + toastMessage: `debugging ${txNumber}`, + validationError: '' } + }) }) - return startDebugging(null, txHash, null, web3) - } - - const stepManager = { - jumpTo: state.debugger && state.debugger.step_manager ? state.debugger.step_manager.jumpTo.bind(state.debugger.step_manager) : null, - stepOverBack: state.debugger && state.debugger.step_manager ? state.debugger.step_manager.stepOverBack.bind(state.debugger.step_manager) : null, - stepIntoBack: state.debugger && state.debugger.step_manager ? state.debugger.step_manager.stepIntoBack.bind(state.debugger.step_manager) : null, - stepIntoForward: state.debugger && state.debugger.step_manager ? state.debugger.step_manager.stepIntoForward.bind(state.debugger.step_manager) : null, - stepOverForward: state.debugger && state.debugger.step_manager ? state.debugger.step_manager.stepOverForward.bind(state.debugger.step_manager) : null, - jumpOut: state.debugger && state.debugger.step_manager ? state.debugger.step_manager.jumpOut.bind(state.debugger.step_manager) : null, - jumpPreviousBreakpoint: state.debugger && state.debugger.step_manager ? state.debugger.step_manager.jumpPreviousBreakpoint.bind(state.debugger.step_manager) : null, - jumpNextBreakpoint: state.debugger && state.debugger.step_manager ? state.debugger.step_manager.jumpNextBreakpoint.bind(state.debugger.step_manager) : null, - jumpToException: state.debugger && state.debugger.step_manager ? state.debugger.step_manager.jumpToException.bind(state.debugger.step_manager) : null, - traceLength: state.debugger && state.debugger.step_manager ? state.debugger.step_manager.traceLength : null, - registerEvent: state.debugger && state.debugger.step_manager ? state.debugger.step_manager.event.register.bind(state.debugger.step_manager.event) : null - } + } catch (error) { + unLoad() + setState(prevState => { + let errorMsg = error.message || error + if (typeof errorMsg !== 'string') { + errorMsg = JSON.stringify(errorMsg) + '. Possible error: the current endpoint does not support retrieving the trace of a transaction.' + } + return { + ...prevState, + validationError: errorMsg + } + }) + } + }, 300) + handleResize() - const vmDebugger = { - registerEvent: state.debugger && state.debugger.vmDebuggerLogic ? state.debugger.vmDebuggerLogic.event.register.bind(state.debugger.vmDebuggerLogic.event) : null, - triggerEvent: state.debugger && state.debugger.vmDebuggerLogic ? state.debugger.vmDebuggerLogic.event.trigger.bind(state.debugger.vmDebuggerLogic.event) : null - } + return debuggerInstance + } - const customJSX = ( - - { - setState(prevState => { - return { ...prevState, opt: { ...prevState.opt, debugWithGeneratedSources: checked } } - }) - }} type="checkbox" /> - - - ) - return ( + const debug = (txHash, web3?) => { + setState(prevState => { + return { + ...prevState, + validationError: '', + txNumber: txHash, + sourceLocationStatus: '' + } + }) + return startDebugging(null, txHash, null, web3) + } + + const stepManager = { + jumpTo: state.debugger && state.debugger.step_manager ? state.debugger.step_manager.jumpTo.bind(state.debugger.step_manager) : null, + stepOverBack: state.debugger && state.debugger.step_manager ? state.debugger.step_manager.stepOverBack.bind(state.debugger.step_manager) : null, + stepIntoBack: state.debugger && state.debugger.step_manager ? state.debugger.step_manager.stepIntoBack.bind(state.debugger.step_manager) : null, + stepIntoForward: state.debugger && state.debugger.step_manager ? state.debugger.step_manager.stepIntoForward.bind(state.debugger.step_manager) : null, + stepOverForward: state.debugger && state.debugger.step_manager ? state.debugger.step_manager.stepOverForward.bind(state.debugger.step_manager) : null, + jumpOut: state.debugger && state.debugger.step_manager ? state.debugger.step_manager.jumpOut.bind(state.debugger.step_manager) : null, + jumpPreviousBreakpoint: state.debugger && state.debugger.step_manager ? state.debugger.step_manager.jumpPreviousBreakpoint.bind(state.debugger.step_manager) : null, + jumpNextBreakpoint: state.debugger && state.debugger.step_manager ? state.debugger.step_manager.jumpNextBreakpoint.bind(state.debugger.step_manager) : null, + jumpToException: state.debugger && state.debugger.step_manager ? state.debugger.step_manager.jumpToException.bind(state.debugger.step_manager) : null, + traceLength: state.debugger && state.debugger.step_manager ? state.debugger.step_manager.traceLength : null, + registerEvent: state.debugger && state.debugger.step_manager ? state.debugger.step_manager.event.register.bind(state.debugger.step_manager.event) : null + } + + const vmDebugger = { + registerEvent: state.debugger && state.debugger.vmDebuggerLogic ? state.debugger.vmDebuggerLogic.event.register.bind(state.debugger.vmDebuggerLogic.event) : null, + triggerEvent: state.debugger && state.debugger.vmDebuggerLogic ? state.debugger.vmDebuggerLogic.event.trigger.bind(state.debugger.vmDebuggerLogic.event) : null + } + + const customJSX = ( + + { + setState(prevState => { + return { ...prevState, opt: { ...prevState.opt, debugWithGeneratedSources: checked } } + }) + }} type="checkbox" /> + + + ) + return ( +
+ +
- -
-
-
- } - placement="bottom-start" - > - {customJSX} - -
- { state.isLocalNodeUsed &&
- - { - setState(prevState => { - return { ...prevState, opt: { ...prevState.opt, debugWithLocalNode: checked } } - }) - }} - type="checkbox" - /> - - -
- } - { state.validationError && {state.validationError} } -
- - { state.debugging && state.sourceLocationStatus &&
{state.sourceLocationStatus}
} - { !state.debugging && +
+ } + placement="bottom-start" + > + {customJSX} + +
+ { state.isLocalNodeUsed &&
+ + { + setState(prevState => { + return { ...prevState, opt: { ...prevState.opt, debugWithLocalNode: checked } } + }) + }} + type="checkbox" + /> + + +
+ } + { state.validationError && {state.validationError} } +
+ + { state.debugging && state.sourceLocationStatus &&
{state.sourceLocationStatus}
} + { !state.debugging && } - { state.debugging && } -
-
- { state.debugging && } - { state.debugging && } -
-
- ) + { state.debugging && } +
+
+ { state.debugging && } + { state.debugging && } +
+ + ) } export default DebuggerUI diff --git a/libs/remix-ui/debugger-ui/src/lib/slider/slider.tsx b/libs/remix-ui/debugger-ui/src/lib/slider/slider.tsx index b49a104456..9a94154b2a 100644 --- a/libs/remix-ui/debugger-ui/src/lib/slider/slider.tsx +++ b/libs/remix-ui/debugger-ui/src/lib/slider/slider.tsx @@ -1,47 +1,47 @@ import React, { useState, useEffect, useRef } from 'react' // eslint-disable-line export const Slider = ({ jumpTo, sliderValue, traceLength }) => { - const onChangeId = useRef(null) - const slider = useRef(null) + const onChangeId = useRef(null) + const slider = useRef(null) - useEffect(() => { - setValue(sliderValue) - }, [sliderValue]) + useEffect(() => { + setValue(sliderValue) + }, [sliderValue]) - const setValue = (value) => { - if (value < 0) return - if (value === slider.current.value) return - slider.current.value = value - if (onChangeId.current) { - clearTimeout(onChangeId.current) - } - ((value) => { - onChangeId.current = setTimeout(() => { - jumpTo && jumpTo(value) - }, 100) - })(value) + const setValue = (value) => { + if (value < 0) return + if (value === slider.current.value) return + slider.current.value = value + if (onChangeId.current) { + clearTimeout(onChangeId.current) } + ((value) => { + onChangeId.current = setTimeout(() => { + jumpTo && jumpTo(value) + }, 100) + })(value) + } - const handleChange = (e) => { - setValue(parseInt(e.target.value)) - } + const handleChange = (e) => { + setValue(parseInt(e.target.value)) + } - if (slider.current) slider.current.internal_onmouseup = handleChange + if (slider.current) slider.current.internal_onmouseup = handleChange - return ( -
- -
- ) + return ( +
+ +
+ ) } export default Slider diff --git a/libs/remix-ui/debugger-ui/src/lib/step-manager/step-manager.tsx b/libs/remix-ui/debugger-ui/src/lib/step-manager/step-manager.tsx index cba5f18926..bd98424adf 100644 --- a/libs/remix-ui/debugger-ui/src/lib/step-manager/step-manager.tsx +++ b/libs/remix-ui/debugger-ui/src/lib/step-manager/step-manager.tsx @@ -3,49 +3,49 @@ import Slider from '../slider/slider' // eslint-disable-line import ButtonNavigator from '../button-navigator/button-navigator' // eslint-disable-line export const StepManager = ({ stepManager: { jumpTo, traceLength, stepIntoBack, stepIntoForward, stepOverBack, stepOverForward, jumpOut, jumpNextBreakpoint, jumpPreviousBreakpoint, jumpToException, registerEvent } }) => { - const [state, setState] = useState({ - sliderValue: -1, - revertWarning: '', - stepState: '', - jumpOutDisabled: true - }) + const [state, setState] = useState({ + sliderValue: -1, + revertWarning: '', + stepState: '', + jumpOutDisabled: true + }) - useEffect(() => { - registerEvent && registerEvent('revertWarning', setRevertWarning) - registerEvent && registerEvent('stepChanged', updateStep) - }, [registerEvent]) + useEffect(() => { + registerEvent && registerEvent('revertWarning', setRevertWarning) + registerEvent && registerEvent('stepChanged', updateStep) + }, [registerEvent]) - const setRevertWarning = (warning) => { - setState(prevState => { - return { ...prevState, revertWarning: warning } - }) - } + const setRevertWarning = (warning) => { + setState(prevState => { + return { ...prevState, revertWarning: warning } + }) + } - const updateStep = (step, stepState, jumpOutDisabled) => { - setState(prevState => { - return { ...prevState, sliderValue: step, stepState, jumpOutDisabled } - }) - } - const { sliderValue, revertWarning, stepState, jumpOutDisabled } = state + const updateStep = (step, stepState, jumpOutDisabled) => { + setState(prevState => { + return { ...prevState, sliderValue: step, stepState, jumpOutDisabled } + }) + } + const { sliderValue, revertWarning, stepState, jumpOutDisabled } = state - return ( -
- - -
- ) + return ( +
+ + +
+ ) } export default StepManager diff --git a/libs/remix-ui/debugger-ui/src/lib/tx-browser/tx-browser.tsx b/libs/remix-ui/debugger-ui/src/lib/tx-browser/tx-browser.tsx index 24a5d10dba..b91de3d5ee 100644 --- a/libs/remix-ui/debugger-ui/src/lib/tx-browser/tx-browser.tsx +++ b/libs/remix-ui/debugger-ui/src/lib/tx-browser/tx-browser.tsx @@ -4,97 +4,97 @@ import { useIntl, FormattedMessage } from 'react-intl' import './tx-browser.css' export const TxBrowser = ({ requestDebug, updateTxNumberFlag, unloadRequested, transactionNumber, debugging }) => { - const [state, setState] = useState({ - txNumber: '' - }) + const [state, setState] = useState({ + txNumber: '' + }) - const inputValue = useRef(null) + const inputValue = useRef(null) - const intl = useIntl() + const intl = useIntl() - useEffect(() => { - setState(prevState => { - return { - ...prevState, - txNumber: transactionNumber - } - }) - }, [transactionNumber]) + useEffect(() => { + setState(prevState => { + return { + ...prevState, + txNumber: transactionNumber + } + }) + }, [transactionNumber]) - const handleSubmit = () => { - if (debugging) { - unload() - } else { - requestDebug(undefined, state.txNumber) - } + const handleSubmit = () => { + if (debugging) { + unload() + } else { + requestDebug(undefined, state.txNumber) } + } - const unload = () => { - unloadRequested() - } + const unload = () => { + unloadRequested() + } - const txInputChanged = (value) => { - // todo check validation of txnumber in the input element, use - // required - // oninvalid="setCustomValidity('Please provide a valid transaction number, must start with 0x and have length of 22')" - // pattern="^0[x,X]+[0-9a-fA-F]{22}" - // this.state.txNumberInput.setCustomValidity('') - setState(prevState => { - return { - ...prevState, - txNumber: value - } - }) - } + const txInputChanged = (value) => { + // todo check validation of txnumber in the input element, use + // required + // oninvalid="setCustomValidity('Please provide a valid transaction number, must start with 0x and have length of 22')" + // pattern="^0[x,X]+[0-9a-fA-F]{22}" + // this.state.txNumberInput.setCustomValidity('') + setState(prevState => { + return { + ...prevState, + txNumber: value + } + }) + } - const txInputOnInput = () => { - updateTxNumberFlag(!inputValue.current.value) - } - const customJSX = ( -
- + const txInputOnInput = () => { + updateTxNumberFlag(!inputValue.current.value) + } + const customJSX = ( +
+ +
+ ) + return ( +
+
+
+ txInputChanged(value)} + onInput={txInputOnInput} + placeholder={intl.formatMessage({id: 'debugger.placeholder'})} + data-id='debuggerTransactionInput' + disabled={debugging} + />
- ) - return ( -
-
-
- txInputChanged(value)} - onInput={txInputOnInput} - placeholder={intl.formatMessage({id: 'debugger.placeholder'})} - data-id='debuggerTransactionInput' - disabled={debugging} - /> -
-
- } - tooltipId={'debuggingButtontooltip'} - tooltipClasses="text-nowrap" - > - {customJSX} - -
-
- +
+ } + tooltipId={'debuggingButtontooltip'} + tooltipClasses="text-nowrap" + > + {customJSX} +
- ) +
+ +
+ ) } export default TxBrowser diff --git a/libs/remix-ui/debugger-ui/src/lib/vm-debugger/assembly-items.tsx b/libs/remix-ui/debugger-ui/src/lib/vm-debugger/assembly-items.tsx index 50a6572c48..b1a86d8f74 100644 --- a/libs/remix-ui/debugger-ui/src/lib/vm-debugger/assembly-items.tsx +++ b/libs/remix-ui/debugger-ui/src/lib/vm-debugger/assembly-items.tsx @@ -3,147 +3,147 @@ import { initialState, reducer } from '../../reducers/assembly-items' import './styles/assembly-items.css' export const AssemblyItems = ({ registerEvent }) => { - const [assemblyItems, dispatch] = useReducer(reducer, initialState) - const [absoluteSelectedIndex, setAbsoluteSelectedIndex] = useState(0) - const [selectedItem, setSelectedItem] = useState(0) - const [nextSelectedItems, setNextSelectedItems] = useState([1]) - const [returnInstructionIndexes, setReturnInstructionIndexes] = useState([]) - const [outOfGasInstructionIndexes, setOutOfGasInstructionIndexes] = useState([]) - const [opcodeTooltipText, setOpcodeTooltipText] = useState('') - const refs = useRef({}) - const asmItemsRef = useRef(null) - - useEffect(() => { - registerEvent && registerEvent('codeManagerChanged', (code, address, index, nextIndexes, returnInstructionIndexes, outOfGasInstructionIndexes) => { - dispatch({ type: 'FETCH_OPCODES_SUCCESS', payload: { code, address, index, nextIndexes, returnInstructionIndexes, outOfGasInstructionIndexes } }) - }) - - registerEvent && registerEvent('lineGasCostChanged', (instructionsIndexes: number[], line: []) => { - dispatch({ type: 'FETCH_INDEXES_FOR_NEW_LINE', payload: { currentLineIndexes: instructionsIndexes || [], line } }) - }) - }, []) - - useEffect(() => { - if (absoluteSelectedIndex !== assemblyItems.index) { - clearItems() - indexChanged(assemblyItems.index) - nextIndexesChanged(assemblyItems.nextIndexes) - returnIndexes(assemblyItems.returnInstructionIndexes) - outOfGasIndexes(assemblyItems.outOfGasInstructionIndexes) - } - }, [assemblyItems.opCodes.index]) - - const clearItem = (currentItem) => { - if (currentItem) { - currentItem.removeAttribute('selected') - currentItem.removeAttribute('style') - if (currentItem.firstChild) { - currentItem.firstChild.removeAttribute('style') - } - } - } - - const clearItems = () => { - clearItem(refs.current[selectedItem] ? refs.current[selectedItem] : null) - if (nextSelectedItems) { - nextSelectedItems.map((index) => { - clearItem(refs.current[index] ? refs.current[index] : null) - }) - } - - returnInstructionIndexes.map((index) => { - if (index < 0) return - clearItem(refs.current[index] ? refs.current[index] : null) - }) - - outOfGasInstructionIndexes.map((index) => { - if (index < 0) return - clearItem(refs.current[index] ? refs.current[index] : null) - }) + const [assemblyItems, dispatch] = useReducer(reducer, initialState) + const [absoluteSelectedIndex, setAbsoluteSelectedIndex] = useState(0) + const [selectedItem, setSelectedItem] = useState(0) + const [nextSelectedItems, setNextSelectedItems] = useState([1]) + const [returnInstructionIndexes, setReturnInstructionIndexes] = useState([]) + const [outOfGasInstructionIndexes, setOutOfGasInstructionIndexes] = useState([]) + const [opcodeTooltipText, setOpcodeTooltipText] = useState('') + const refs = useRef({}) + const asmItemsRef = useRef(null) + + useEffect(() => { + registerEvent && registerEvent('codeManagerChanged', (code, address, index, nextIndexes, returnInstructionIndexes, outOfGasInstructionIndexes) => { + dispatch({ type: 'FETCH_OPCODES_SUCCESS', payload: { code, address, index, nextIndexes, returnInstructionIndexes, outOfGasInstructionIndexes } }) + }) + + registerEvent && registerEvent('lineGasCostChanged', (instructionsIndexes: number[], line: []) => { + dispatch({ type: 'FETCH_INDEXES_FOR_NEW_LINE', payload: { currentLineIndexes: instructionsIndexes || [], line } }) + }) + }, []) + + useEffect(() => { + if (absoluteSelectedIndex !== assemblyItems.index) { + clearItems() + indexChanged(assemblyItems.index) + nextIndexesChanged(assemblyItems.nextIndexes) + returnIndexes(assemblyItems.returnInstructionIndexes) + outOfGasIndexes(assemblyItems.outOfGasInstructionIndexes) } - - const indexChanged = (index: number) => { - if (index < 0) return - - const codeView = asmItemsRef.current - - const currentItem = codeView.children[index] - if (currentItem) { - currentItem.style.setProperty('background-color', 'var(--primary)') - currentItem.style.setProperty('color', 'var(--light)') - currentItem.setAttribute('selected', 'selected') - codeView.scrollTop = currentItem.offsetTop - parseInt(codeView.offsetTop) - } - - setSelectedItem(index) - setAbsoluteSelectedIndex(assemblyItems.opCodes.index) + }, [assemblyItems.opCodes.index]) + + const clearItem = (currentItem) => { + if (currentItem) { + currentItem.removeAttribute('selected') + currentItem.removeAttribute('style') + if (currentItem.firstChild) { + currentItem.firstChild.removeAttribute('style') + } } - - const nextIndexesChanged = (indexes: Array) => { - indexes.map((index) => { - if (index < 0) return - - const codeView = asmItemsRef.current - - const currentItem = codeView.children[index] - if (currentItem) { - currentItem.style.setProperty('color', 'var(--primary)') - currentItem.style.setProperty('font-weight', 'bold') - currentItem.setAttribute('selected', 'selected') - } - }) - setNextSelectedItems(indexes) + } + + const clearItems = () => { + clearItem(refs.current[selectedItem] ? refs.current[selectedItem] : null) + if (nextSelectedItems) { + nextSelectedItems.map((index) => { + clearItem(refs.current[index] ? refs.current[index] : null) + }) } - const returnIndexes = (indexes) => { - indexes.map((index) => { - if (index < 0) return - - const codeView = asmItemsRef.current + returnInstructionIndexes.map((index) => { + if (index < 0) return + clearItem(refs.current[index] ? refs.current[index] : null) + }) - const currentItem = codeView.children[index] - if (currentItem) { - currentItem.style.setProperty('border-style', 'dotted') - currentItem.setAttribute('selected', 'selected') - } - }) - setReturnInstructionIndexes(indexes) - } + outOfGasInstructionIndexes.map((index) => { + if (index < 0) return + clearItem(refs.current[index] ? refs.current[index] : null) + }) + } - const outOfGasIndexes = (indexes) => { - indexes.map((index) => { - if (index < 0) return + const indexChanged = (index: number) => { + if (index < 0) return - const codeView = asmItemsRef.current + const codeView = asmItemsRef.current - const currentItem = codeView.children[index] - if (currentItem) { - currentItem.style.setProperty('border-color', 'var(--danger)') - currentItem.style.setProperty('border-style', 'dotted') - currentItem.setAttribute('selected', 'selected') - } - }) - setOutOfGasInstructionIndexes(indexes) + const currentItem = codeView.children[index] + if (currentItem) { + currentItem.style.setProperty('background-color', 'var(--primary)') + currentItem.style.setProperty('color', 'var(--light)') + currentItem.setAttribute('selected', 'selected') + codeView.scrollTop = currentItem.offsetTop - parseInt(codeView.offsetTop) } - return ( -
-
-
- { assemblyItems.display.length == 0 &&
No data available
} -
- { - assemblyItems.display.map((item, i) => { - return
{ refs.current[i] = ref }}> - {item}{assemblyItems.currentLineIndexes.includes(i) ? - LINE {assemblyItems.line + 1} : ' - '} -
- }) - } -
+ setSelectedItem(index) + setAbsoluteSelectedIndex(assemblyItems.opCodes.index) + } + + const nextIndexesChanged = (indexes: Array) => { + indexes.map((index) => { + if (index < 0) return + + const codeView = asmItemsRef.current + + const currentItem = codeView.children[index] + if (currentItem) { + currentItem.style.setProperty('color', 'var(--primary)') + currentItem.style.setProperty('font-weight', 'bold') + currentItem.setAttribute('selected', 'selected') + } + }) + setNextSelectedItems(indexes) + } + + const returnIndexes = (indexes) => { + indexes.map((index) => { + if (index < 0) return + + const codeView = asmItemsRef.current + + const currentItem = codeView.children[index] + if (currentItem) { + currentItem.style.setProperty('border-style', 'dotted') + currentItem.setAttribute('selected', 'selected') + } + }) + setReturnInstructionIndexes(indexes) + } + + const outOfGasIndexes = (indexes) => { + indexes.map((index) => { + if (index < 0) return + + const codeView = asmItemsRef.current + + const currentItem = codeView.children[index] + if (currentItem) { + currentItem.style.setProperty('border-color', 'var(--danger)') + currentItem.style.setProperty('border-style', 'dotted') + currentItem.setAttribute('selected', 'selected') + } + }) + setOutOfGasInstructionIndexes(indexes) + } + + return ( +
+
+
+ { assemblyItems.display.length == 0 &&
No data available
} +
+ { + assemblyItems.display.map((item, i) => { + return
{ refs.current[i] = ref }}> + {item}{assemblyItems.currentLineIndexes.includes(i) ? - LINE {assemblyItems.line + 1} : ' - '}
-
+ }) + } +
- ) +
+
+ ) } export default AssemblyItems diff --git a/libs/remix-ui/debugger-ui/src/lib/vm-debugger/calldata-panel.tsx b/libs/remix-ui/debugger-ui/src/lib/vm-debugger/calldata-panel.tsx index c33dce048b..406dcd455e 100644 --- a/libs/remix-ui/debugger-ui/src/lib/vm-debugger/calldata-panel.tsx +++ b/libs/remix-ui/debugger-ui/src/lib/vm-debugger/calldata-panel.tsx @@ -2,11 +2,11 @@ import React from 'react' // eslint-disable-line import DropdownPanel from './dropdown-panel' // eslint-disable-line export const CalldataPanel = ({ calldata, className = "" }) => { - return ( -
- -
- ) + return ( +
+ +
+ ) } export default CalldataPanel diff --git a/libs/remix-ui/debugger-ui/src/lib/vm-debugger/callstack-panel.tsx b/libs/remix-ui/debugger-ui/src/lib/vm-debugger/callstack-panel.tsx index d1d40c8dbf..f43d7941c9 100644 --- a/libs/remix-ui/debugger-ui/src/lib/vm-debugger/callstack-panel.tsx +++ b/libs/remix-ui/debugger-ui/src/lib/vm-debugger/callstack-panel.tsx @@ -2,11 +2,11 @@ import React from 'react' // eslint-disable-line import DropdownPanel from './dropdown-panel' // eslint-disable-line export const CallstackPanel = ({ calldata, className }) => { - return ( -
- -
- ) + return ( +
+ +
+ ) } export default CallstackPanel diff --git a/libs/remix-ui/debugger-ui/src/lib/vm-debugger/code-list-view.tsx b/libs/remix-ui/debugger-ui/src/lib/vm-debugger/code-list-view.tsx index 657f5b6f07..f282c88673 100644 --- a/libs/remix-ui/debugger-ui/src/lib/vm-debugger/code-list-view.tsx +++ b/libs/remix-ui/debugger-ui/src/lib/vm-debugger/code-list-view.tsx @@ -2,11 +2,11 @@ import React, { useState, useEffect } from 'react' // eslint-disable-line import AssemblyItems from './assembly-items' // eslint-disable-line export const CodeListView = ({ registerEvent, className = ""}) => { - return ( -
- -
- ) + return ( +
+ +
+ ) } export default CodeListView diff --git a/libs/remix-ui/debugger-ui/src/lib/vm-debugger/dropdown-panel.tsx b/libs/remix-ui/debugger-ui/src/lib/vm-debugger/dropdown-panel.tsx index 8fdd0ceee0..3cb9e850ed 100644 --- a/libs/remix-ui/debugger-ui/src/lib/vm-debugger/dropdown-panel.tsx +++ b/libs/remix-ui/debugger-ui/src/lib/vm-debugger/dropdown-panel.tsx @@ -6,218 +6,218 @@ import { initialState, reducer } from '../../reducers/calldata' import './styles/dropdown-panel.css' export const DropdownPanel = (props: DropdownPanelProps) => { - const [calldataObj, dispatch] = useReducer(reducer, initialState) - const { dropdownName, className, dropdownMessage, calldata, header, loading, extractFunc, formatSelfFunc, registerEvent, triggerEvent, loadMoreEvent, loadMoreCompletedEvent, headStyle, bodyStyle, hexHighlight } = props - const extractDataDefault: ExtractFunc = (item, parent?) => { - const ret: ExtractData = {} - - if (item instanceof Array) { - ret.children = item.map((item, index) => { - return { key: index, value: item } - }) - ret.self = 'Array' - ret.isNode = true - ret.isLeaf = false - } else if (item instanceof Object) { - ret.children = Object.keys(item).map((key) => { - return { key: key, value: item[key] } - }) - ret.self = 'Object' - ret.isNode = true - ret.isLeaf = false - } else { - ret.self = item - ret.children = null - ret.isNode = false - ret.isLeaf = true - } - return ret + const [calldataObj, dispatch] = useReducer(reducer, initialState) + const { dropdownName, className, dropdownMessage, calldata, header, loading, extractFunc, formatSelfFunc, registerEvent, triggerEvent, loadMoreEvent, loadMoreCompletedEvent, headStyle, bodyStyle, hexHighlight } = props + const extractDataDefault: ExtractFunc = (item, parent?) => { + const ret: ExtractData = {} + + if (item instanceof Array) { + ret.children = item.map((item, index) => { + return { key: index, value: item } + }) + ret.self = 'Array' + ret.isNode = true + ret.isLeaf = false + } else if (item instanceof Object) { + ret.children = Object.keys(item).map((key) => { + return { key: key, value: item[key] } + }) + ret.self = 'Object' + ret.isNode = true + ret.isLeaf = false + } else { + ret.self = item + ret.children = null + ret.isNode = false + ret.isLeaf = true } - const formatSelfDefault = (key: string | number, data: ExtractData) => { - let value - if (hexHighlight && typeof (data.self) === 'string') { - const isHex = data.self.startsWith('0x') || hexHighlight - if (isHex) { - const regex = /^(0+)(.*)/g - const split = regex.exec(data.self.replace('0x', '')) - if (split && split[1]) { - value = (0x{split[1]}{ split[2] && {split[2]} }) - } else value = (0x{data.self.replace('0x', '')}) - } else value = {data.self} - } else value = {data.self} - return ( -
- - -
- ) + return ret + } + const formatSelfDefault = (key: string | number, data: ExtractData) => { + let value + if (hexHighlight && typeof (data.self) === 'string') { + const isHex = data.self.startsWith('0x') || hexHighlight + if (isHex) { + const regex = /^(0+)(.*)/g + const split = regex.exec(data.self.replace('0x', '')) + if (split && split[1]) { + value = (0x{split[1]}{ split[2] && {split[2]} }) + } else value = (0x{data.self.replace('0x', '')}) + } else value = {data.self} + } else value = {data.self} + return ( +
+ + +
+ ) + } + const [state, setState] = useState({ + header: '', + toggleDropdown: true, + message: { + innerText: 'No data available.', + display: 'block' + }, + dropdownContent: { + innerText: '', + display: 'none' + }, + title: { + innerText: '', + display: 'none' + }, + copiableContent: '', + updating: false, + expandPath: [], + data: null + }) + + useEffect(() => { + registerEvent && registerEvent(loadMoreCompletedEvent, (updatedCalldata) => { + dispatch({ type: 'UPDATE_CALLDATA_SUCCESS', payload: updatedCalldata }) + }) + }, []) + + useEffect(() => { + dispatch({ type: 'FETCH_CALLDATA_SUCCESS', payload: calldata }) + }, [calldata]) + + useEffect(() => { + update(calldata) + }, [calldataObj.calldata]) + + useEffect(() => { + message(dropdownMessage) + }, [dropdownMessage]) + + useEffect(() => { + if (loading && !state.updating) setLoading() + }, [loading]) + + const handleToggle = () => { + setState(prevState => { + return { + ...prevState, + toggleDropdown: !prevState.toggleDropdown + } + }) + } + + const handleExpand = (keyPath) => { + if (!state.expandPath.includes(keyPath)) { + state.expandPath.push(keyPath) + } else { + state.expandPath = state.expandPath.filter(path => !path.startsWith(keyPath)) } - const [state, setState] = useState({ - header: '', - toggleDropdown: true, + } + + const message = (message) => { + if (message === state.message.innerText) return + setState(prevState => { + return { + ...prevState, message: { - innerText: 'No data available.', - display: 'block' + innerText: message, + display: message ? 'block' : '' }, - dropdownContent: { - innerText: '', - display: 'none' + updating: false + } + }) + } + + const setLoading = () => { + setState(prevState => { + return { + ...prevState, + message: { + innerText: '', + display: 'none' }, - title: { - innerText: '', - display: 'none' + dropdownContent: { + ...prevState.dropdownContent, + display: 'none' }, copiableContent: '', - updating: false, - expandPath: [], - data: null + updating: true + } }) + } - useEffect(() => { - registerEvent && registerEvent(loadMoreCompletedEvent, (updatedCalldata) => { - dispatch({ type: 'UPDATE_CALLDATA_SUCCESS', payload: updatedCalldata }) - }) - }, []) - - useEffect(() => { - dispatch({ type: 'FETCH_CALLDATA_SUCCESS', payload: calldata }) - }, [calldata]) - - useEffect(() => { - update(calldata) - }, [calldataObj.calldata]) - - useEffect(() => { - message(dropdownMessage) - }, [dropdownMessage]) - - useEffect(() => { - if (loading && !state.updating) setLoading() - }, [loading]) - - const handleToggle = () => { - setState(prevState => { - return { - ...prevState, - toggleDropdown: !prevState.toggleDropdown - } - }) - } - - const handleExpand = (keyPath) => { - if (!state.expandPath.includes(keyPath)) { - state.expandPath.push(keyPath) - } else { - state.expandPath = state.expandPath.filter(path => !path.startsWith(keyPath)) - } - } - - const message = (message) => { - if (message === state.message.innerText) return - setState(prevState => { - return { - ...prevState, - message: { - innerText: message, - display: message ? 'block' : '' - }, - updating: false - } - }) - } + const update = function (calldata) { + let isEmpty = !calldata - const setLoading = () => { - setState(prevState => { - return { - ...prevState, - message: { - innerText: '', - display: 'none' - }, - dropdownContent: { - ...prevState.dropdownContent, - display: 'none' - }, - copiableContent: '', - updating: true - } - }) - } + if (calldata && Array.isArray(calldata) && calldata.length === 0) isEmpty = true + else if (calldata && Object.keys(calldata).length === 0 && calldata.constructor === Object) isEmpty = true - const update = function (calldata) { - let isEmpty = !calldata - - if (calldata && Array.isArray(calldata) && calldata.length === 0) isEmpty = true - else if (calldata && Object.keys(calldata).length === 0 && calldata.constructor === Object) isEmpty = true - - setState(prevState => { - return { - ...prevState, - dropdownContent: { - ...prevState.dropdownContent, - display: 'block' - }, - // replace 0xNaN with 0x0 - copiableContent: JSON.stringify(calldata, null, '\t').replace(/0xNaN/g, '0x0'), - message: { - innerText: isEmpty ? 'No data available' : '', - display: isEmpty ? 'block' : 'none' - }, - updating: false, - toggleDropdown: true, - data: calldata - } - }) - } + setState(prevState => { + return { + ...prevState, + dropdownContent: { + ...prevState.dropdownContent, + display: 'block' + }, + // replace 0xNaN with 0x0 + copiableContent: JSON.stringify(calldata, null, '\t').replace(/0xNaN/g, '0x0'), + message: { + innerText: isEmpty ? 'No data available' : '', + display: isEmpty ? 'block' : 'none' + }, + updating: false, + toggleDropdown: true, + data: calldata + } + }) + } + + const renderData = (item: ExtractData, parent, key: string | number, keyPath: string) => { + const data = extractFunc ? extractFunc(item, parent) : extractDataDefault(item, parent) + const children = (data.children || []).map((child) => { + return ( + renderData(child.value, data, child.key, keyPath + '/' + child.key) + ) + }) - const renderData = (item: ExtractData, parent, key: string | number, keyPath: string) => { - const data = extractFunc ? extractFunc(item, parent) : extractDataDefault(item, parent) - const children = (data.children || []).map((child) => { - return ( - renderData(child.value, data, child.key, keyPath + '/' + child.key) - ) - }) - - if (children && children.length > 0) { - return ( - handleExpand(keyPath)} expand={state.expandPath.includes(keyPath)}> - - {children} - {data.hasNext && { triggerEvent(loadMoreEvent, [data.cursor]) }} />} - - - ) - } else { - return handleExpand(keyPath)} expand={state.expandPath.includes(keyPath)} /> - } + if (children && children.length > 0) { + return ( + handleExpand(keyPath)} expand={state.expandPath.includes(keyPath)}> + + {children} + {data.hasNext && { triggerEvent(loadMoreEvent, [data.cursor]) }} />} + + + ) + } else { + return handleExpand(keyPath)} expand={state.expandPath.includes(keyPath)} /> } - - const uniquePanelName = dropdownName.split(' ').join('') - - return ( -
-
-
-
{dropdownName}
{header} - -
-
- -
- { - state.data && + } + + const uniquePanelName = dropdownName.split(' ').join('') + + return ( +
+
+
+
{dropdownName}
{header} + +
+
+ +
+ { + state.data && - { - Object.keys(state.data).map((innerkey) => renderData(state.data[innerkey], state.data, innerkey, innerkey)) - } + { + Object.keys(state.data).map((innerkey) => renderData(state.data[innerkey], state.data, innerkey, innerkey)) + } - } -
- -
{state.message.innerText}
-
+ }
- ) + +
{state.message.innerText}
+
+
+ ) } export default DropdownPanel diff --git a/libs/remix-ui/debugger-ui/src/lib/vm-debugger/full-storages-changes.tsx b/libs/remix-ui/debugger-ui/src/lib/vm-debugger/full-storages-changes.tsx index acfe871891..d4ef8038f3 100644 --- a/libs/remix-ui/debugger-ui/src/lib/vm-debugger/full-storages-changes.tsx +++ b/libs/remix-ui/debugger-ui/src/lib/vm-debugger/full-storages-changes.tsx @@ -2,11 +2,11 @@ import React from 'react' // eslint-disable-line import { DropdownPanel } from './dropdown-panel' // eslint-disable-line export const FullStoragesChanges = ({ calldata, className = "" }) => { - return ( -
- -
- ) + return ( +
+ +
+ ) } export default FullStoragesChanges diff --git a/libs/remix-ui/debugger-ui/src/lib/vm-debugger/function-panel.tsx b/libs/remix-ui/debugger-ui/src/lib/vm-debugger/function-panel.tsx index d3544f7cbe..3ab09527c2 100644 --- a/libs/remix-ui/debugger-ui/src/lib/vm-debugger/function-panel.tsx +++ b/libs/remix-ui/debugger-ui/src/lib/vm-debugger/function-panel.tsx @@ -3,17 +3,17 @@ import DropdownPanel from './dropdown-panel' // eslint-disable-line import { default as deepequal } from 'deep-equal' // eslint-disable-line export const FunctionPanel = ({ data, className }) => { - const [calldata, setCalldata] = useState(null) + const [calldata, setCalldata] = useState(null) - useEffect(() => { - if (!deepequal(calldata, data)) setCalldata(data) - }, [data]) + useEffect(() => { + if (!deepequal(calldata, data)) setCalldata(data) + }, [data]) - return ( -
- -
- ) + return ( +
+ +
+ ) } export default FunctionPanel diff --git a/libs/remix-ui/debugger-ui/src/lib/vm-debugger/global-variables.tsx b/libs/remix-ui/debugger-ui/src/lib/vm-debugger/global-variables.tsx index ed5f4aec0a..5610c8cc76 100644 --- a/libs/remix-ui/debugger-ui/src/lib/vm-debugger/global-variables.tsx +++ b/libs/remix-ui/debugger-ui/src/lib/vm-debugger/global-variables.tsx @@ -4,28 +4,28 @@ import { BN } from 'bn.js' import Web3 from 'web3' export const GlobalVariables = ({ block, receipt, tx, className }) => { - // see https://docs.soliditylang.org/en/latest/units-and-global-variables.html#block-and-transaction-properties - const globals = { - 'block.chainid': tx && tx.chainId, - 'block.coinbase': block && block.miner, - 'block.difficulty': block && block.difficulty, - 'block.gaslimit': block && block.gasLimit, - 'block.number': block && block.number, - 'block.timestamp': block && block.timestamp, - 'msg.sender': tx && tx.from, - 'msg.sig': tx && tx.input && tx.input.substring(0, 10), - 'msg.value': tx && (tx.value + ' Wei'), - 'tx.origin': tx && tx.from - } - if (block && block.baseFeePerGas) { - globals['block.basefee'] = Web3.utils.toBN(block.baseFeePerGas).toString(10) + ` Wei (${block.baseFeePerGas})` - } + // see https://docs.soliditylang.org/en/latest/units-and-global-variables.html#block-and-transaction-properties + const globals = { + 'block.chainid': tx && tx.chainId, + 'block.coinbase': block && block.miner, + 'block.difficulty': block && block.difficulty, + 'block.gaslimit': block && block.gasLimit, + 'block.number': block && block.number, + 'block.timestamp': block && block.timestamp, + 'msg.sender': tx && tx.from, + 'msg.sig': tx && tx.input && tx.input.substring(0, 10), + 'msg.value': tx && (tx.value + ' Wei'), + 'tx.origin': tx && tx.from + } + if (block && block.baseFeePerGas) { + globals['block.basefee'] = Web3.utils.toBN(block.baseFeePerGas).toString(10) + ` Wei (${block.baseFeePerGas})` + } - return ( -
- -
- ) + return ( +
+ +
+ ) } export default GlobalVariables diff --git a/libs/remix-ui/debugger-ui/src/lib/vm-debugger/memory-panel.tsx b/libs/remix-ui/debugger-ui/src/lib/vm-debugger/memory-panel.tsx index 10931a103a..3889ba3ad1 100644 --- a/libs/remix-ui/debugger-ui/src/lib/vm-debugger/memory-panel.tsx +++ b/libs/remix-ui/debugger-ui/src/lib/vm-debugger/memory-panel.tsx @@ -2,11 +2,11 @@ import React from 'react' // eslint-disable-line import DropdownPanel from './dropdown-panel' // eslint-disable-line export const MemoryPanel = ({ calldata, className}) => { - return ( -
- -
- ) + return ( +
+ +
+ ) } export default MemoryPanel diff --git a/libs/remix-ui/debugger-ui/src/lib/vm-debugger/solidity-locals.tsx b/libs/remix-ui/debugger-ui/src/lib/vm-debugger/solidity-locals.tsx index 51913796af..a69a34be7d 100644 --- a/libs/remix-ui/debugger-ui/src/lib/vm-debugger/solidity-locals.tsx +++ b/libs/remix-ui/debugger-ui/src/lib/vm-debugger/solidity-locals.tsx @@ -4,59 +4,59 @@ import { extractData } from '../../utils/solidityTypeFormatter' // eslint-disabl import { ExtractData } from '../../types' // eslint-disable-line export const SolidityLocals = ({ data, message, registerEvent, triggerEvent, className = "" }) => { - const [calldata, setCalldata] = useState(null) + const [calldata, setCalldata] = useState(null) - useEffect(() => { - data && setCalldata(data) - }, [data]) + useEffect(() => { + data && setCalldata(data) + }, [data]) - const formatSelf = (key: string, data: ExtractData) => { - let color = 'var(--primary)' - if (data.isArray || data.isStruct || data.isMapping) { - color = 'var(--info)' - } else if ( - data.type.indexOf('uint') === 0 || + const formatSelf = (key: string, data: ExtractData) => { + let color = 'var(--primary)' + if (data.isArray || data.isStruct || data.isMapping) { + color = 'var(--info)' + } else if ( + data.type.indexOf('uint') === 0 || data.type.indexOf('int') === 0 || data.type.indexOf('bool') === 0 || data.type.indexOf('enum') === 0 - ) { - color = 'var(--green)' - } else if (data.type === 'string') { - color = 'var(--teal)' + ) { + color = 'var(--green)' + } else if (data.type === 'string') { + color = 'var(--teal)' } else if (data.self == 0x0) { // eslint-disable-line - color = 'var(--gray)' - } - if (data.type === 'string') { - data.self = JSON.stringify(data.self) - } - return ( - - ) + color = 'var(--gray)' + } + if (data.type === 'string') { + data.self = JSON.stringify(data.self) } - return ( -
- -
+ ) + } + + return ( +
+ +
+ ) } export default SolidityLocals diff --git a/libs/remix-ui/debugger-ui/src/lib/vm-debugger/solidity-state.tsx b/libs/remix-ui/debugger-ui/src/lib/vm-debugger/solidity-state.tsx index b36c8c7455..c107810b2b 100644 --- a/libs/remix-ui/debugger-ui/src/lib/vm-debugger/solidity-state.tsx +++ b/libs/remix-ui/debugger-ui/src/lib/vm-debugger/solidity-state.tsx @@ -4,46 +4,46 @@ import { extractData } from '../../utils/solidityTypeFormatter' import { ExtractData } from '../../types' // eslint-disable-line export const SolidityState = ({ calldata, message, className }) => { - const formatSelf = (key: string, data: ExtractData) => { - try { - let color = 'var(--primary)' - if (data.isArray || data.isStruct || data.isMapping) { - color = 'var(--info)' - } else if ( - data.type.indexOf('uint') === 0 || + const formatSelf = (key: string, data: ExtractData) => { + try { + let color = 'var(--primary)' + if (data.isArray || data.isStruct || data.isMapping) { + color = 'var(--info)' + } else if ( + data.type.indexOf('uint') === 0 || data.type.indexOf('int') === 0 || data.type.indexOf('bool') === 0 || data.type.indexOf('enum') === 0 - ) { - color = 'var(--green)' - } else if (data.type === 'string') { - color = 'var(--teal)' + ) { + color = 'var(--green)' + } else if (data.type === 'string') { + color = 'var(--teal)' } else if (data.self == 0x0) { // eslint-disable-line - color = 'var(--gray)' - } - return ( - - ) - } catch (e) { - return (<>) - } + color = 'var(--gray)' + } + return ( + + ) + } catch (e) { + return (<>) } + } - return ( -
- { - - } -
- ) + return ( +
+ { + + } +
+ ) } export default SolidityState diff --git a/libs/remix-ui/debugger-ui/src/lib/vm-debugger/stack-panel.tsx b/libs/remix-ui/debugger-ui/src/lib/vm-debugger/stack-panel.tsx index 44ca714fe5..7094b67bf8 100644 --- a/libs/remix-ui/debugger-ui/src/lib/vm-debugger/stack-panel.tsx +++ b/libs/remix-ui/debugger-ui/src/lib/vm-debugger/stack-panel.tsx @@ -2,11 +2,11 @@ import React from 'react' // eslint-disable-line import DropdownPanel from './dropdown-panel' // eslint-disable-line export const StackPanel = ({ calldata, className }) => { - return ( -
- -
- ) + return ( +
+ +
+ ) } export default StackPanel diff --git a/libs/remix-ui/debugger-ui/src/lib/vm-debugger/step-detail.tsx b/libs/remix-ui/debugger-ui/src/lib/vm-debugger/step-detail.tsx index ccf0814567..0d617b3023 100644 --- a/libs/remix-ui/debugger-ui/src/lib/vm-debugger/step-detail.tsx +++ b/libs/remix-ui/debugger-ui/src/lib/vm-debugger/step-detail.tsx @@ -2,11 +2,11 @@ import React from 'react' // eslint-disable-line import DropdownPanel from './dropdown-panel' // eslint-disable-line export const StepDetail = ({ stepDetail, className = "" }) => { - return ( -
- -
- ) + return ( +
+ +
+ ) } export default StepDetail diff --git a/libs/remix-ui/debugger-ui/src/lib/vm-debugger/storage-panel.tsx b/libs/remix-ui/debugger-ui/src/lib/vm-debugger/storage-panel.tsx index 38dcc90f3b..20e5c9d825 100644 --- a/libs/remix-ui/debugger-ui/src/lib/vm-debugger/storage-panel.tsx +++ b/libs/remix-ui/debugger-ui/src/lib/vm-debugger/storage-panel.tsx @@ -2,11 +2,11 @@ import React from 'react' // eslint-disable-line import DropdownPanel from './dropdown-panel' // eslint-disable-line export const StoragePanel = ({ calldata, header, className }) => { - return ( -
- -
- ) + return ( +
+ +
+ ) } export default StoragePanel diff --git a/libs/remix-ui/debugger-ui/src/lib/vm-debugger/vm-debugger-head.tsx b/libs/remix-ui/debugger-ui/src/lib/vm-debugger/vm-debugger-head.tsx index 669a5cf89c..654c390c51 100644 --- a/libs/remix-ui/debugger-ui/src/lib/vm-debugger/vm-debugger-head.tsx +++ b/libs/remix-ui/debugger-ui/src/lib/vm-debugger/vm-debugger-head.tsx @@ -6,110 +6,110 @@ import SolidityState from './solidity-state' // eslint-disable-line import SolidityLocals from './solidity-locals' // eslint-disable-line export const VmDebuggerHead = ({ vmDebugger: { registerEvent, triggerEvent }, debugging }) => { - const [functionPanel, setFunctionPanel] = useState(null) - const [stepDetail, setStepDetail] = useState({ - 'vm trace step': '-', - 'execution step': '-', - 'add memory': '', - gas: '', - 'remaining gas': '-', - 'loaded address': '-' - }) + const [functionPanel, setFunctionPanel] = useState(null) + const [stepDetail, setStepDetail] = useState({ + 'vm trace step': '-', + 'execution step': '-', + 'add memory': '', + gas: '', + 'remaining gas': '-', + 'loaded address': '-' + }) - const [solidityState, setSolidityState] = useState({ - calldata: null, - message: null - }) - const [solidityLocals, setSolidityLocals] = useState({ - calldata: null, - message: null - }) + const [solidityState, setSolidityState] = useState({ + calldata: null, + message: null + }) + const [solidityLocals, setSolidityLocals] = useState({ + calldata: null, + message: null + }) - useEffect(() => { - registerEvent && registerEvent('functionsStackUpdate', (stack) => { - if (stack === null || stack.length === 0) return - const functions = [] + useEffect(() => { + registerEvent && registerEvent('functionsStackUpdate', (stack) => { + if (stack === null || stack.length === 0) return + const functions = [] - for (const func of stack) { - functions.push((func.functionDefinition.name || func.functionDefinition.kind) + '(' + func.inputs.join(', ') + ')' + ' - ' + func.gasCost + ' gas') - } - setFunctionPanel(() => functions) - }) - registerEvent && registerEvent('traceUnloaded', () => { - setStepDetail(() => { - return { 'vm trace step': '-', 'execution step': '-', 'add memory': '', gas: '', 'remaining gas': '-', 'loaded address': '-' } - }) - }) - registerEvent && registerEvent('newTraceLoaded', () => { - setStepDetail(() => { - return { 'vm trace step': '-', 'execution step': '-', 'add memory': '', gas: '', 'remaining gas': '-', 'loaded address': '-' } - }) - }) - registerEvent && registerEvent('traceCurrentStepUpdate', (error, step) => { - setStepDetail(prevState => { - return { ...prevState, 'execution step': (error ? '-' : step) } - }) - }) - registerEvent && registerEvent('traceMemExpandUpdate', (error, addmem) => { - setStepDetail(prevState => { - return { ...prevState, 'add memory': (error ? '-' : addmem) } - }) - }) - registerEvent && registerEvent('traceStepCostUpdate', (error, gas) => { - setStepDetail(prevState => { - return { ...prevState, gas: (error ? '-' : gas) } - }) - }) - registerEvent && registerEvent('traceCurrentCalledAddressAtUpdate', (error, address) => { - setStepDetail(prevState => { - return { ...prevState, 'loaded address': (error ? '-' : address) } - }) - }) - registerEvent && registerEvent('traceRemainingGasUpdate', (error, remainingGas) => { - setStepDetail(prevState => { - return { ...prevState, 'remaining gas': (error ? '-' : remainingGas) } - }) - }) - registerEvent && registerEvent('indexUpdate', (index) => { - setStepDetail(prevState => { - return { ...prevState, 'vm trace step': index } - }) - }) - registerEvent && registerEvent('solidityState', (calldata) => { - setSolidityState(() => { - return { ...solidityState, calldata } - }) - }) - registerEvent && registerEvent('solidityStateMessage', (message) => { - setSolidityState(() => { - return { ...solidityState, message } - }) - }) - registerEvent && registerEvent('solidityLocals', (calldata) => { - setSolidityLocals(() => { - return { ...solidityLocals, calldata } - }) - }) - registerEvent && registerEvent('solidityLocalsMessage', (message) => { - setSolidityLocals(() => { - return { ...solidityLocals, message } - }) - }) - }, [debugging]) + for (const func of stack) { + functions.push((func.functionDefinition.name || func.functionDefinition.kind) + '(' + func.inputs.join(', ') + ')' + ' - ' + func.gasCost + ' gas') + } + setFunctionPanel(() => functions) + }) + registerEvent && registerEvent('traceUnloaded', () => { + setStepDetail(() => { + return { 'vm trace step': '-', 'execution step': '-', 'add memory': '', gas: '', 'remaining gas': '-', 'loaded address': '-' } + }) + }) + registerEvent && registerEvent('newTraceLoaded', () => { + setStepDetail(() => { + return { 'vm trace step': '-', 'execution step': '-', 'add memory': '', gas: '', 'remaining gas': '-', 'loaded address': '-' } + }) + }) + registerEvent && registerEvent('traceCurrentStepUpdate', (error, step) => { + setStepDetail(prevState => { + return { ...prevState, 'execution step': (error ? '-' : step) } + }) + }) + registerEvent && registerEvent('traceMemExpandUpdate', (error, addmem) => { + setStepDetail(prevState => { + return { ...prevState, 'add memory': (error ? '-' : addmem) } + }) + }) + registerEvent && registerEvent('traceStepCostUpdate', (error, gas) => { + setStepDetail(prevState => { + return { ...prevState, gas: (error ? '-' : gas) } + }) + }) + registerEvent && registerEvent('traceCurrentCalledAddressAtUpdate', (error, address) => { + setStepDetail(prevState => { + return { ...prevState, 'loaded address': (error ? '-' : address) } + }) + }) + registerEvent && registerEvent('traceRemainingGasUpdate', (error, remainingGas) => { + setStepDetail(prevState => { + return { ...prevState, 'remaining gas': (error ? '-' : remainingGas) } + }) + }) + registerEvent && registerEvent('indexUpdate', (index) => { + setStepDetail(prevState => { + return { ...prevState, 'vm trace step': index } + }) + }) + registerEvent && registerEvent('solidityState', (calldata) => { + setSolidityState(() => { + return { ...solidityState, calldata } + }) + }) + registerEvent && registerEvent('solidityStateMessage', (message) => { + setSolidityState(() => { + return { ...solidityState, message } + }) + }) + registerEvent && registerEvent('solidityLocals', (calldata) => { + setSolidityLocals(() => { + return { ...solidityLocals, calldata } + }) + }) + registerEvent && registerEvent('solidityLocalsMessage', (message) => { + setSolidityLocals(() => { + return { ...solidityLocals, message } + }) + }) + }, [debugging]) - return ( -
-
- - - -
-
- - -
-
- ) + return ( +
+
+ + + +
+
+ + +
+
+ ) } export default VmDebuggerHead diff --git a/libs/remix-ui/debugger-ui/src/lib/vm-debugger/vm-debugger.tsx b/libs/remix-ui/debugger-ui/src/lib/vm-debugger/vm-debugger.tsx index 508097d5df..7c3ffc84e2 100644 --- a/libs/remix-ui/debugger-ui/src/lib/vm-debugger/vm-debugger.tsx +++ b/libs/remix-ui/debugger-ui/src/lib/vm-debugger/vm-debugger.tsx @@ -9,77 +9,77 @@ import FullStoragesChangesPanel from './full-storages-changes' // eslint-disable import GlobalVariables from './global-variables' // eslint-disable-line export const VmDebugger = ({ vmDebugger: { registerEvent }, currentBlock, currentReceipt, currentTransaction, debugging }) => { - const [calldataPanel, setCalldataPanel] = useState(null) - const [memoryPanel, setMemoryPanel] = useState(null) - const [callStackPanel, setCallStackPanel] = useState(null) - const [stackPanel, setStackPanel] = useState(null) - const [storagePanel, setStoragePanel] = useState({ - calldata: null, - header: null - }) - const [returnValuesPanel, setReturnValuesPanel] = useState(null) - const [fullStoragesChangesPanel, setFullStoragesChangesPanel] = useState(null) + const [calldataPanel, setCalldataPanel] = useState(null) + const [memoryPanel, setMemoryPanel] = useState(null) + const [callStackPanel, setCallStackPanel] = useState(null) + const [stackPanel, setStackPanel] = useState(null) + const [storagePanel, setStoragePanel] = useState({ + calldata: null, + header: null + }) + const [returnValuesPanel, setReturnValuesPanel] = useState(null) + const [fullStoragesChangesPanel, setFullStoragesChangesPanel] = useState(null) - useEffect(() => { - registerEvent && registerEvent('traceManagerCallDataUpdate', (calldata) => { - setCalldataPanel(() => calldata) - }) - registerEvent && registerEvent('traceManagerMemoryUpdate', (calldata) => { - setMemoryPanel(() => calldata) - }) - registerEvent && registerEvent('traceManagerCallStackUpdate', (calldata) => { - setCallStackPanel(() => calldata) - }) - registerEvent && registerEvent('traceManagerStackUpdate', (calldata) => { - setStackPanel(() => calldata) - }) - registerEvent && registerEvent('traceManagerStorageUpdate', (calldata, header) => { - setStoragePanel(() => { - return { calldata, header } - }) - }) - registerEvent && registerEvent('traceReturnValueUpdate', (calldata) => { - setReturnValuesPanel(() => calldata) - }) - registerEvent && registerEvent('traceAddressesUpdate', (calldata) => { - setFullStoragesChangesPanel(() => { - return {} - }) - }) - registerEvent && registerEvent('traceStorageUpdate', (calldata) => { - setFullStoragesChangesPanel(() => calldata) - }) - }, [debugging]) + useEffect(() => { + registerEvent && registerEvent('traceManagerCallDataUpdate', (calldata) => { + setCalldataPanel(() => calldata) + }) + registerEvent && registerEvent('traceManagerMemoryUpdate', (calldata) => { + setMemoryPanel(() => calldata) + }) + registerEvent && registerEvent('traceManagerCallStackUpdate', (calldata) => { + setCallStackPanel(() => calldata) + }) + registerEvent && registerEvent('traceManagerStackUpdate', (calldata) => { + setStackPanel(() => calldata) + }) + registerEvent && registerEvent('traceManagerStorageUpdate', (calldata, header) => { + setStoragePanel(() => { + return { calldata, header } + }) + }) + registerEvent && registerEvent('traceReturnValueUpdate', (calldata) => { + setReturnValuesPanel(() => calldata) + }) + registerEvent && registerEvent('traceAddressesUpdate', (calldata) => { + setFullStoragesChangesPanel(() => { + return {} + }) + }) + registerEvent && registerEvent('traceStorageUpdate', (calldata) => { + setFullStoragesChangesPanel(() => calldata) + }) + }, [debugging]) - return ( -
-
- - - - - - -
-
- - -
-
- ) + return ( +
+
+ + + + + + +
+
+ + +
+
+ ) } export default VmDebugger diff --git a/libs/remix-ui/debugger-ui/src/reducers/assembly-items.ts b/libs/remix-ui/debugger-ui/src/reducers/assembly-items.ts index 5c8c51d79c..f0bfb95c74 100644 --- a/libs/remix-ui/debugger-ui/src/reducers/assembly-items.ts +++ b/libs/remix-ui/debugger-ui/src/reducers/assembly-items.ts @@ -6,92 +6,92 @@ interface Action { } export const initialState = { - opCodes: { - code: [], - index: 0, - address: '' - }, - display: [], + opCodes: { + code: [], index: 0, - initialIndex: 0, - nextIndexes: [-1], - returnInstructionIndexes: [], - outOfGasInstructionIndexes: [], - top: 0, - bottom: 0, - isRequesting: false, - isSuccessful: false, - hasError: null, - absoluteCurrentLineIndexes: [], - currentLineIndexes: [], - line: -1 + address: '' + }, + display: [], + index: 0, + initialIndex: 0, + nextIndexes: [-1], + returnInstructionIndexes: [], + outOfGasInstructionIndexes: [], + top: 0, + bottom: 0, + isRequesting: false, + isSuccessful: false, + hasError: null, + absoluteCurrentLineIndexes: [], + currentLineIndexes: [], + line: -1 } const reducedOpcode = (opCodes, payload) => { - const length = 100 - let bottom = opCodes.index - 10 - bottom = bottom < 0 ? 0 : bottom - const top = bottom + length - return { - index: opCodes.index - bottom, - nextIndexes: opCodes.nextIndexes ? opCodes.nextIndexes.map(index => index - bottom) : [], - currentLineIndexes: (opCodes.absoluteCurrentLineIndexes && opCodes.absoluteCurrentLineIndexes.map(index => index - bottom)) || [], - display: opCodes.code.slice(bottom, top), - returnInstructionIndexes: payload.returnInstructionIndexes.map((index) => index.instructionIndex - bottom), - outOfGasInstructionIndexes: payload.outOfGasInstructionIndexes.map((index) => index.instructionIndex - bottom) - } + const length = 100 + let bottom = opCodes.index - 10 + bottom = bottom < 0 ? 0 : bottom + const top = bottom + length + return { + index: opCodes.index - bottom, + nextIndexes: opCodes.nextIndexes ? opCodes.nextIndexes.map(index => index - bottom) : [], + currentLineIndexes: (opCodes.absoluteCurrentLineIndexes && opCodes.absoluteCurrentLineIndexes.map(index => index - bottom)) || [], + display: opCodes.code.slice(bottom, top), + returnInstructionIndexes: payload.returnInstructionIndexes.map((index) => index.instructionIndex - bottom), + outOfGasInstructionIndexes: payload.outOfGasInstructionIndexes.map((index) => index.instructionIndex - bottom) + } } export const reducer = (state = initialState, action: Action) => { - switch (action.type) { - case 'FETCH_OPCODES_REQUEST': { - return { - ...state, - isRequesting: true, - isSuccessful: false, - hasError: null - } + switch (action.type) { + case 'FETCH_OPCODES_REQUEST': { + return { + ...state, + isRequesting: true, + isSuccessful: false, + hasError: null } - case 'FETCH_OPCODES_SUCCESS': { - const opCodes = action.payload.address === state.opCodes.address ? { - ...state.opCodes, index: action.payload.index, nextIndexes: action.payload.nextIndexes, absoluteCurrentLineIndexes: state.absoluteCurrentLineIndexes - } : deepEqual(action.payload.code, state.opCodes.code) ? state.opCodes : action.payload + } + case 'FETCH_OPCODES_SUCCESS': { + const opCodes = action.payload.address === state.opCodes.address ? { + ...state.opCodes, index: action.payload.index, nextIndexes: action.payload.nextIndexes, absoluteCurrentLineIndexes: state.absoluteCurrentLineIndexes + } : deepEqual(action.payload.code, state.opCodes.code) ? state.opCodes : action.payload - const reduced = reducedOpcode(opCodes, action.payload) - return { - ...state, - opCodes, - display: reduced.display, - initialIndex: action.payload.index, - index: reduced.index, - nextIndexes: reduced.nextIndexes, - isRequesting: false, - isSuccessful: true, - hasError: null, - returnInstructionIndexes: reduced.returnInstructionIndexes, - outOfGasInstructionIndexes: reduced.outOfGasInstructionIndexes, - currentLineIndexes: reduced.currentLineIndexes - } - } - case 'FETCH_OPCODES_ERROR': { - return { - ...state, - isRequesting: false, - isSuccessful: false, - hasError: action.payload - } + const reduced = reducedOpcode(opCodes, action.payload) + return { + ...state, + opCodes, + display: reduced.display, + initialIndex: action.payload.index, + index: reduced.index, + nextIndexes: reduced.nextIndexes, + isRequesting: false, + isSuccessful: true, + hasError: null, + returnInstructionIndexes: reduced.returnInstructionIndexes, + outOfGasInstructionIndexes: reduced.outOfGasInstructionIndexes, + currentLineIndexes: reduced.currentLineIndexes } - case 'FETCH_INDEXES_FOR_NEW_LINE': { - let bottom = state.initialIndex - 10 - bottom = bottom < 0 ? 0 : bottom - return { - ...state, - absoluteCurrentLineIndexes: action.payload.currentLineIndexes, - currentLineIndexes: action.payload.currentLineIndexes.map(index => index - bottom), - line: action.payload.line - } + } + case 'FETCH_OPCODES_ERROR': { + return { + ...state, + isRequesting: false, + isSuccessful: false, + hasError: action.payload } - default: - throw new Error() + } + case 'FETCH_INDEXES_FOR_NEW_LINE': { + let bottom = state.initialIndex - 10 + bottom = bottom < 0 ? 0 : bottom + return { + ...state, + absoluteCurrentLineIndexes: action.payload.currentLineIndexes, + currentLineIndexes: action.payload.currentLineIndexes.map(index => index - bottom), + line: action.payload.line } + } + default: + throw new Error() + } } diff --git a/libs/remix-ui/debugger-ui/src/reducers/calldata.ts b/libs/remix-ui/debugger-ui/src/reducers/calldata.ts index de5dcc1c0a..26fb259c60 100644 --- a/libs/remix-ui/debugger-ui/src/reducers/calldata.ts +++ b/libs/remix-ui/debugger-ui/src/reducers/calldata.ts @@ -4,69 +4,69 @@ interface Action { } export const initialState = { - calldata: {}, - isRequesting: false, - isSuccessful: false, - hasError: null + calldata: {}, + isRequesting: false, + isSuccessful: false, + hasError: null } export const reducer = (state = initialState, action: Action) => { - switch (action.type) { - case 'FETCH_CALLDATA_REQUEST': - return { - ...state, - isRequesting: true, - isSuccessful: false, - hasError: null - } - case 'FETCH_CALLDATA_SUCCESS': - return { - calldata: action.payload, - isRequesting: false, - isSuccessful: true, - hasError: null - } - case 'FETCH_CALLDATA_ERROR': - return { - ...state, - isRequesting: false, - isSuccessful: false, - hasError: action.payload - } - case 'UPDATE_CALLDATA_REQUEST': - return { - ...state, - isRequesting: true, - isSuccessful: false, - hasError: null - } - case 'UPDATE_CALLDATA_SUCCESS': - return { - calldata: mergeLocals(action.payload, state.calldata), - isRequesting: false, - isSuccessful: true, - hasError: null - } - case 'UPDATE_CALLDATA_ERROR': - return { - ...state, - isRequesting: false, - isSuccessful: false, - hasError: action.payload - } - default: - throw new Error() + switch (action.type) { + case 'FETCH_CALLDATA_REQUEST': + return { + ...state, + isRequesting: true, + isSuccessful: false, + hasError: null } + case 'FETCH_CALLDATA_SUCCESS': + return { + calldata: action.payload, + isRequesting: false, + isSuccessful: true, + hasError: null + } + case 'FETCH_CALLDATA_ERROR': + return { + ...state, + isRequesting: false, + isSuccessful: false, + hasError: action.payload + } + case 'UPDATE_CALLDATA_REQUEST': + return { + ...state, + isRequesting: true, + isSuccessful: false, + hasError: null + } + case 'UPDATE_CALLDATA_SUCCESS': + return { + calldata: mergeLocals(action.payload, state.calldata), + isRequesting: false, + isSuccessful: true, + hasError: null + } + case 'UPDATE_CALLDATA_ERROR': + return { + ...state, + isRequesting: false, + isSuccessful: false, + hasError: action.payload + } + default: + throw new Error() + } } function mergeLocals (locals1, locals2) { - Object.keys(locals2).map(item => { - if (locals2[item].cursor && (parseInt(locals2[item].cursor) < parseInt(locals1[item].cursor))) { - locals2[item] = { - ...locals1[item], - value: [...locals2[item].value, ...locals1[item].value] - } - } - }) - return locals2 + Object.keys(locals2).map(item => { + if (locals2[item].cursor && (parseInt(locals2[item].cursor) < parseInt(locals1[item].cursor))) { + locals2[item] = { + ...locals1[item], + value: [...locals2[item].value, ...locals1[item].value] + } + } + }) + return locals2 } diff --git a/libs/remix-ui/debugger-ui/src/utils/solidityTypeFormatter.ts b/libs/remix-ui/debugger-ui/src/utils/solidityTypeFormatter.ts index cc1e82f8c1..c85026c499 100644 --- a/libs/remix-ui/debugger-ui/src/utils/solidityTypeFormatter.ts +++ b/libs/remix-ui/debugger-ui/src/utils/solidityTypeFormatter.ts @@ -2,47 +2,47 @@ import { BN } from 'bn.js' import { ExtractData } from '../types' // eslint-disable-line export function extractData (item, parent): ExtractData { - const ret: ExtractData = {} + const ret: ExtractData = {} - if (item.isProperty || !item.type) { - return item - } - try { - if (item.type.lastIndexOf(']') === item.type.length - 1) { - ret.children = (item.value || []).map(function (item, index) { - return { key: index, value: item } - }) - ret.children.unshift({ - key: 'length', - value: { - self: (new BN(item.length.replace('0x', ''), 16)).toString(10), - type: 'uint', - isProperty: true - } - }) - ret.isArray = true - ret.self = parent.isArray ? '' : item.type - ret.cursor = item.cursor - ret.hasNext = item.hasNext - } else if (item.type.indexOf('struct') === 0) { - ret.children = Object.keys((item.value || {})).map(function (key) { - return { key: key, value: item.value[key] } - }) - ret.self = item.type - ret.isStruct = true - } else if (item.type.indexOf('mapping') === 0) { - ret.children = Object.keys((item.value || {})).map(function (key) { - return { key: key, value: item.value[key] } - }) - ret.isMapping = true - ret.self = item.type - } else { - ret.children = null - ret.self = item.value - ret.type = item.type + if (item.isProperty || !item.type) { + return item + } + try { + if (item.type.lastIndexOf(']') === item.type.length - 1) { + ret.children = (item.value || []).map(function (item, index) { + return { key: index, value: item } + }) + ret.children.unshift({ + key: 'length', + value: { + self: (new BN(item.length.replace('0x', ''), 16)).toString(10), + type: 'uint', + isProperty: true } - } catch (e) { - console.log(e) + }) + ret.isArray = true + ret.self = parent.isArray ? '' : item.type + ret.cursor = item.cursor + ret.hasNext = item.hasNext + } else if (item.type.indexOf('struct') === 0) { + ret.children = Object.keys((item.value || {})).map(function (key) { + return { key: key, value: item.value[key] } + }) + ret.self = item.type + ret.isStruct = true + } else if (item.type.indexOf('mapping') === 0) { + ret.children = Object.keys((item.value || {})).map(function (key) { + return { key: key, value: item.value[key] } + }) + ret.isMapping = true + ret.self = item.type + } else { + ret.children = null + ret.self = item.value + ret.type = item.type } - return ret + } catch (e) { + console.log(e) + } + return ret } diff --git a/libs/remix-ui/drag-n-drop/src/lib/context/moveContext.ts b/libs/remix-ui/drag-n-drop/src/lib/context/moveContext.ts index 4eb31c87a7..e6f091dd47 100644 --- a/libs/remix-ui/drag-n-drop/src/lib/context/moveContext.ts +++ b/libs/remix-ui/drag-n-drop/src/lib/context/moveContext.ts @@ -2,8 +2,8 @@ import { createContext } from "react"; import { MoveContextType } from "../types"; export const MoveContext = createContext({ - dragged: {} as { path: string, isDirectory: boolean }, - moveFile: () => null, - moveFolder: () => null, - currentlyMoved: () => null + dragged: {} as { path: string, isDirectory: boolean }, + moveFile: () => null, + moveFolder: () => null, + currentlyMoved: () => null }) diff --git a/libs/remix-ui/drag-n-drop/src/lib/drag-n-drop.tsx b/libs/remix-ui/drag-n-drop/src/lib/drag-n-drop.tsx index ff74b3f360..b49b8d391a 100644 --- a/libs/remix-ui/drag-n-drop/src/lib/drag-n-drop.tsx +++ b/libs/remix-ui/drag-n-drop/src/lib/drag-n-drop.tsx @@ -4,90 +4,90 @@ import { MoveContext } from "./context/moveContext" import { DraggableType, DragType } from "./types" export const Drag = (props: DragType) => { - const [dragged, setDragged] = useState<{ path: string, isDirectory: boolean }>({} as { path: string, isDirectory: boolean }) + const [dragged, setDragged] = useState<{ path: string, isDirectory: boolean }>({} as { path: string, isDirectory: boolean }) - return ( - { - setDragged(() => path) - }, - }} - > - {props.children} - - ) + return ( + { + setDragged(() => path) + }, + }} + > + {props.children} + + ) } export const Draggable = (props: DraggableType) => { - const dragRef = useRef(null), - destination = props.file, - context = useContext(MoveContext) + const dragRef = useRef(null), + destination = props.file, + context = useContext(MoveContext) - const handleDrop = (event: React.DragEvent) => { - event.preventDefault() + const handleDrop = (event: React.DragEvent) => { + event.preventDefault() - if (destination.isDirectory) { - if (context.dragged.isDirectory) { - context.moveFolder(destination.path, context.dragged.path) - } else { - context.moveFile(destination.path, context.dragged.path) - } - } else { - const path = extractParentFromKey(destination.path) || '/' + if (destination.isDirectory) { + if (context.dragged.isDirectory) { + context.moveFolder(destination.path, context.dragged.path) + } else { + context.moveFile(destination.path, context.dragged.path) + } + } else { + const path = extractParentFromKey(destination.path) || '/' - if (context.dragged.isDirectory) { - context.moveFolder(path, context.dragged.path) - } else { - context.moveFile(path, context.dragged.path) - } - } + if (context.dragged.isDirectory) { + context.moveFolder(path, context.dragged.path) + } else { + context.moveFile(path, context.dragged.path) + } } + } - const handleDragover = (event: React.DragEvent) => { - //Checks if the folder is opened - event.preventDefault() - if (destination.isDirectory && !props.expandedPath.includes(destination.path)) { - props.handleClickFolder(destination.path, destination.type) - } + const handleDragover = (event: React.DragEvent) => { + //Checks if the folder is opened + event.preventDefault() + if (destination.isDirectory && !props.expandedPath.includes(destination.path)) { + props.handleClickFolder(destination.path, destination.type) } + } - const handleDrag = () => { - if (context.dragged.path !== destination.path) { - context.currentlyMoved({ - path: destination.path, - isDirectory: destination.isDirectory - }) - } + const handleDrag = () => { + if (context.dragged.path !== destination.path) { + context.currentlyMoved({ + path: destination.path, + isDirectory: destination.isDirectory + }) } + } - return ( - <> - { - props.isDraggable ? props.children : - { - handleDrop(event) - }} - onDragStart={() => { - if (destination) { - handleDrag() - } - }} - onDragOver={(event) => { - if (destination) { - handleDragover(event) - } - }} - > - {props.children} - - } - - ) + return ( + <> + { + props.isDraggable ? props.children : + { + handleDrop(event) + }} + onDragStart={() => { + if (destination) { + handleDrag() + } + }} + onDragOver={(event) => { + if (destination) { + handleDragover(event) + } + }} + > + {props.children} + + } + + ) } diff --git a/libs/remix-ui/editor/src/lib/actions/editor.ts b/libs/remix-ui/editor/src/lib/actions/editor.ts index d71d731292..6eddc1420a 100644 --- a/libs/remix-ui/editor/src/lib/actions/editor.ts +++ b/libs/remix-ui/editor/src/lib/actions/editor.ts @@ -10,168 +10,168 @@ export interface Action { export const initialState = {} export const reducerActions = (models = initialState, action: Action) => { - const monaco = action.monaco - const editor = action.editor - switch (action.type) { - case 'ADD_MODEL': { - if (!editor) return models - const uri = action.payload.uri - const value = action.payload.value - const language = action.payload.language - const readOnly = action.payload.readOnly - if (models[uri]) return models // already existing - models[uri] = { language, uri, readOnly } - let model - try { - model = monaco.editor.createModel(value, language, monaco.Uri.parse(uri)) - } catch (e) { + const monaco = action.monaco + const editor = action.editor + switch (action.type) { + case 'ADD_MODEL': { + if (!editor) return models + const uri = action.payload.uri + const value = action.payload.value + const language = action.payload.language + const readOnly = action.payload.readOnly + if (models[uri]) return models // already existing + models[uri] = { language, uri, readOnly } + let model + try { + model = monaco.editor.createModel(value, language, monaco.Uri.parse(uri)) + } catch (e) { - } - models[uri].model = model - model.onDidChangeContent(() => action.payload.events.onDidChangeContent(uri)) - return models } - case 'DISPOSE_MODEL': { - const uri = action.payload.uri - const model = models[uri]?.model - if (model) model.dispose() - delete models[uri] - return models + models[uri].model = model + model.onDidChangeContent(() => action.payload.events.onDidChangeContent(uri)) + return models + } + case 'DISPOSE_MODEL': { + const uri = action.payload.uri + const model = models[uri]?.model + if (model) model.dispose() + delete models[uri] + return models + } + case 'SET_VALUE': { + if (!editor) return models + const uri = action.payload.uri + const value = action.payload.value + const model = models[uri]?.model + if (model) { + model.setValue(value) } - case 'SET_VALUE': { - if (!editor) return models - const uri = action.payload.uri - const value = action.payload.value - const model = models[uri]?.model - if (model) { - model.setValue(value) - } - return models - } - case 'REVEAL_LINE': { - if (!editor) return models - const line = action.payload.line - const column = action.payload.column - editor.revealLine(line) - editor.setPosition({ column, lineNumber: line }) - return models - } - case 'REVEAL_RANGE': { - if (!editor) return models - const range: monacoTypes.IRange = { - startLineNumber: action.payload.startLineNumber + 1, - startColumn: action.payload.startColumn, - endLineNumber: action.payload.endLineNumber + 1, - endColumn: action.payload.endColumn - } - // reset to start of line - if (action.payload.startColumn < 100) { - editor.revealRange({ - startLineNumber: range.startLineNumber, - startColumn: 1, - endLineNumber: range.endLineNumber, - endColumn: 1 - }) - } else { - editor.revealRangeInCenter(range) - } - return models - } - case 'FOCUS': { - if (!editor) return models - editor.focus() - return models - } - case 'SET_FONTSIZE': { - if (!editor) return models - const size = action.payload.size - editor.updateOptions({ fontSize: size }) - return models - } - case 'SET_WORDWRAP': { - if (!editor) return models - const wrap = action.payload.wrap - editor.updateOptions({ wordWrap: wrap ? 'on' : 'off' }) - return models + return models + } + case 'REVEAL_LINE': { + if (!editor) return models + const line = action.payload.line + const column = action.payload.column + editor.revealLine(line) + editor.setPosition({ column, lineNumber: line }) + return models + } + case 'REVEAL_RANGE': { + if (!editor) return models + const range: monacoTypes.IRange = { + startLineNumber: action.payload.startLineNumber + 1, + startColumn: action.payload.startColumn, + endLineNumber: action.payload.endLineNumber + 1, + endColumn: action.payload.endColumn } + // reset to start of line + if (action.payload.startColumn < 100) { + editor.revealRange({ + startLineNumber: range.startLineNumber, + startColumn: 1, + endLineNumber: range.endLineNumber, + endColumn: 1 + }) + } else { + editor.revealRangeInCenter(range) } + return models + } + case 'FOCUS': { + if (!editor) return models + editor.focus() + return models + } + case 'SET_FONTSIZE': { + if (!editor) return models + const size = action.payload.size + editor.updateOptions({ fontSize: size }) + return models + } + case 'SET_WORDWRAP': { + if (!editor) return models + const wrap = action.payload.wrap + editor.updateOptions({ wordWrap: wrap ? 'on' : 'off' }) + return models + } + } } export const reducerListener = (plugin, dispatch, monaco, editor, events) => { - plugin.on('editor', 'addModel', (value, language, uri, readOnly) => { - dispatch({ - type: 'ADD_MODEL', - payload: { uri, value, language, readOnly, events }, - monaco, - editor - }) + plugin.on('editor', 'addModel', (value, language, uri, readOnly) => { + dispatch({ + type: 'ADD_MODEL', + payload: { uri, value, language, readOnly, events }, + monaco, + editor }) + }) - plugin.on('editor', 'disposeModel', (uri) => { - dispatch({ - type: 'DISPOSE_MODEL', - payload: { uri }, - monaco, - editor - }) + plugin.on('editor', 'disposeModel', (uri) => { + dispatch({ + type: 'DISPOSE_MODEL', + payload: { uri }, + monaco, + editor }) + }) - plugin.on('editor', 'setValue', (uri, value) => { - dispatch({ - type: 'SET_VALUE', - payload: { uri, value }, - monaco, - editor - }) + plugin.on('editor', 'setValue', (uri, value) => { + dispatch({ + type: 'SET_VALUE', + payload: { uri, value }, + monaco, + editor }) + }) - plugin.on('editor', 'revealLine', (line, column) => { - dispatch({ - type: 'REVEAL_LINE', - payload: { line, column }, - monaco, - editor - }) + plugin.on('editor', 'revealLine', (line, column) => { + dispatch({ + type: 'REVEAL_LINE', + payload: { line, column }, + monaco, + editor }) + }) - plugin.on('editor', 'revealRange', (startLineNumber, startColumn, endLineNumber, endColumn) => { - dispatch({ - type: 'REVEAL_RANGE', - payload: { - startLineNumber, - startColumn, - endLineNumber, - endColumn - }, - monaco, - editor - }) + plugin.on('editor', 'revealRange', (startLineNumber, startColumn, endLineNumber, endColumn) => { + dispatch({ + type: 'REVEAL_RANGE', + payload: { + startLineNumber, + startColumn, + endLineNumber, + endColumn + }, + monaco, + editor }) + }) - plugin.on('editor', 'focus', () => { - dispatch({ - type: 'FOCUS', - payload: {}, - monaco, - editor - }) + plugin.on('editor', 'focus', () => { + dispatch({ + type: 'FOCUS', + payload: {}, + monaco, + editor }) + }) - plugin.on('editor', 'setFontSize', (size) => { - dispatch({ - type: 'SET_FONTSIZE', - payload: { size }, - monaco, - editor - }) + plugin.on('editor', 'setFontSize', (size) => { + dispatch({ + type: 'SET_FONTSIZE', + payload: { size }, + monaco, + editor }) + }) - plugin.on('editor', 'setWordWrap', (wrap) => { - dispatch({ - type: 'SET_WORDWRAP', - payload: { wrap }, - monaco, - editor - }) + plugin.on('editor', 'setWordWrap', (wrap) => { + dispatch({ + type: 'SET_WORDWRAP', + payload: { wrap }, + monaco, + editor }) + }) } diff --git a/libs/remix-ui/editor/src/lib/helpers/retrieveNodesAtPosition.ts b/libs/remix-ui/editor/src/lib/helpers/retrieveNodesAtPosition.ts index 0d61f990bf..e4af01531e 100644 --- a/libs/remix-ui/editor/src/lib/helpers/retrieveNodesAtPosition.ts +++ b/libs/remix-ui/editor/src/lib/helpers/retrieveNodesAtPosition.ts @@ -1,15 +1,15 @@ import { EditorAPIType, PluginType } from "../remix-ui-editor" export const retrieveNodesAtPosition = async (editorAPI: EditorAPIType, plugin: PluginType) => { - const cursorPosition = editorAPI.getCursorPosition() - let nodesAtPosition = await plugin.call('codeParser', 'nodesAtPosition', cursorPosition) - // if no nodes exits at position, try to get the block of which the position is in - const block = await plugin.call('codeParser', 'getANTLRBlockAtPosition', cursorPosition, null) + const cursorPosition = editorAPI.getCursorPosition() + let nodesAtPosition = await plugin.call('codeParser', 'nodesAtPosition', cursorPosition) + // if no nodes exits at position, try to get the block of which the position is in + const block = await plugin.call('codeParser', 'getANTLRBlockAtPosition', cursorPosition, null) - if (!nodesAtPosition.length) { - if (block) { - nodesAtPosition = await plugin.call('codeParser', 'nodesAtPosition', block.start) - } + if (!nodesAtPosition.length) { + if (block) { + nodesAtPosition = await plugin.call('codeParser', 'nodesAtPosition', block.start) } - return { nodesAtPosition, block } + } + return { nodesAtPosition, block } } \ No newline at end of file diff --git a/libs/remix-ui/editor/src/lib/providers/completion/completionGlobals.ts b/libs/remix-ui/editor/src/lib/providers/completion/completionGlobals.ts index 21436a1998..08dd16d7ff 100644 --- a/libs/remix-ui/editor/src/lib/providers/completion/completionGlobals.ts +++ b/libs/remix-ui/editor/src/lib/providers/completion/completionGlobals.ts @@ -8,758 +8,758 @@ type CodeParserImportsData = { } export function getStringCompletionItems(range: monacoTypes.IRange, monaco): monacoTypes.languages.CompletionItem[] { - return [ - { - detail: 'concatenate an arbitrary number of string values', - kind: monaco.languages.CompletionItemKind.Property, - insertText: 'concat(${1:string})', - insertTextRules: monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet, - label: 'concat()', - range, - }, - ] + return [ + { + detail: 'concatenate an arbitrary number of string values', + kind: monaco.languages.CompletionItemKind.Property, + insertText: 'concat(${1:string})', + insertTextRules: monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet, + label: 'concat()', + range, + }, + ] } export function getBytesCompletionItems(range: monacoTypes.IRange, monaco): monacoTypes.languages.CompletionItem[] { - return [ - { - detail: 'concatenate an arbitrary number of values', - kind: monaco.languages.CompletionItemKind.Property, - insertText: 'concat(${1:bytes})', - insertTextRules: monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet, - label: 'concat()', - range, - }, - ] + return [ + { + detail: 'concatenate an arbitrary number of values', + kind: monaco.languages.CompletionItemKind.Property, + insertText: 'concat(${1:bytes})', + insertTextRules: monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet, + label: 'concat()', + range, + }, + ] } export function getBlockCompletionItems(range: monacoTypes.IRange, monaco): monacoTypes.languages.CompletionItem[] { - return [ - { - detail: '(address): Current block miner’s address', - kind: monaco.languages.CompletionItemKind.Property, - insertText: 'coinbase', - label: 'coinbase', - range, - }, - { - detail: '(uint): Current block’s base fee', - kind: monaco.languages.CompletionItemKind.Property, - insertText: 'basefee', - label: 'basefee', - range, - }, - { - detail: '(uint): Current chain id', - kind: monaco.languages.CompletionItemKind.Property, - insertText: 'chainid', - label: 'chainid', - range, - }, - { - detail: '(bytes32): DEPRICATED In 0.4.22 use blockhash(uint) instead. Hash of the given block - only works for 256 most recent blocks excluding current', - insertText: 'blockhash(${1:blockNumber});', - kind: monaco.languages.CompletionItemKind.Method, - insertTextRules: monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet, - label: 'blockhash', - range - }, - { - detail: '(uint): current block difficulty', - kind: monaco.languages.CompletionItemKind.Property, - label: 'difficulty', - insertText: 'difficulty', - range - }, - { - detail: '(uint): current block gaslimit', - kind: monaco.languages.CompletionItemKind.Property, - label: 'gaslimit', - insertText: 'gaslimit', - range - }, - { - detail: '(uint): current block number', - kind: monaco.languages.CompletionItemKind.Property, - label: 'number', - insertText: 'number', - range - }, - { - detail: '(uint): current block timestamp as seconds since unix epoch', - kind: monaco.languages.CompletionItemKind.Property, - label: 'timestamp', - insertText: 'timestamp', - range - }, - ]; + return [ + { + detail: '(address): Current block miner’s address', + kind: monaco.languages.CompletionItemKind.Property, + insertText: 'coinbase', + label: 'coinbase', + range, + }, + { + detail: '(uint): Current block’s base fee', + kind: monaco.languages.CompletionItemKind.Property, + insertText: 'basefee', + label: 'basefee', + range, + }, + { + detail: '(uint): Current chain id', + kind: monaco.languages.CompletionItemKind.Property, + insertText: 'chainid', + label: 'chainid', + range, + }, + { + detail: '(bytes32): DEPRICATED In 0.4.22 use blockhash(uint) instead. Hash of the given block - only works for 256 most recent blocks excluding current', + insertText: 'blockhash(${1:blockNumber});', + kind: monaco.languages.CompletionItemKind.Method, + insertTextRules: monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet, + label: 'blockhash', + range + }, + { + detail: '(uint): current block difficulty', + kind: monaco.languages.CompletionItemKind.Property, + label: 'difficulty', + insertText: 'difficulty', + range + }, + { + detail: '(uint): current block gaslimit', + kind: monaco.languages.CompletionItemKind.Property, + label: 'gaslimit', + insertText: 'gaslimit', + range + }, + { + detail: '(uint): current block number', + kind: monaco.languages.CompletionItemKind.Property, + label: 'number', + insertText: 'number', + range + }, + { + detail: '(uint): current block timestamp as seconds since unix epoch', + kind: monaco.languages.CompletionItemKind.Property, + label: 'timestamp', + insertText: 'timestamp', + range + }, + ]; } export function getCompletionSnippets(range: monacoTypes.IRange, monaco): monacoTypes.languages.CompletionItem[] { - return [ - { - label: 'contract', - kind: monaco.languages.CompletionItemKind.Snippet, - insertText: 'contract ${1:Name} {\n\t$0\n}', - insertTextRules: monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet, - range - }, - { - label: 'library', - kind: monaco.languages.CompletionItemKind.Snippet, - insertText: 'library ${1:Name} {\n\t$0\n}', - insertTextRules: monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet, - range - }, - { - label: 'interface', - kind: monaco.languages.CompletionItemKind.Snippet, - insertText: 'interface ${1:Name} {\n\t$0\n}', - insertTextRules: monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet, - range - }, - { - label: 'enum', - kind: monaco.languages.CompletionItemKind.Snippet, - insertText: 'enum ${1:Name} {${2:item1}, ${3:item2} }', - insertTextRules: monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet, - range - }, - { - label: 'function', - kind: monaco.languages.CompletionItemKind.Snippet, - insertText: 'function ${1:name}(${2:params}) {\n\t${3:code}\n}', - insertTextRules: monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet, - range - }, - { - label: 'constructor', - kind: monaco.languages.CompletionItemKind.Snippet, - insertText: 'constructor(${1:params}) {\n\t${2:code}\n}', - insertTextRules: monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet, - range - }, - { - label: 'ifstatement', - kind: monaco.languages.CompletionItemKind.Snippet, - insertText: 'if (${1:condition}) {\n\t${2:code}\n}', - insertTextRules: monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet, - range - }, - { - label: 'ifstatementelse', - kind: monaco.languages.CompletionItemKind.Snippet, - insertText: 'if (${1:condition}) {\n\t${2:code}\n} else {\n\t${3:code}\n}', - insertTextRules: monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet, - range - }, - { - label: 'while loop', - kind: monaco.languages.CompletionItemKind.Snippet, - insertText: 'while (${1:condition}) \n{\n\t${2:code}\n};', - insertTextRules: monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet, - range - }, - { - label: 'do while loop', - kind: monaco.languages.CompletionItemKind.Snippet, - insertText: 'do {\n\t${2:code}\n} \nwhile (${1:condition});', - insertTextRules: monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet, - range - }, - { - label: 'for loop', - kind: monaco.languages.CompletionItemKind.Snippet, - insertText: 'for (${1:init}; ${2:condition}; ${3:increment}) \n{\n\t${4:code}\n};', - insertTextRules: monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet, - range - }, - { - label: 'pragma', - kind: monaco.languages.CompletionItemKind.Snippet, - insertText: '// SPDX-License-Identifier: MIT\npragma solidity ${1:version};', - insertTextRules: monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet, - range - }, - { - label: 'SPDX-License-Identifier', - kind: monaco.languages.CompletionItemKind.Snippet, - insertText: '// SPDX-License-Identifier: MIT', - insertTextRules: monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet, - range - } - ] + return [ + { + label: 'contract', + kind: monaco.languages.CompletionItemKind.Snippet, + insertText: 'contract ${1:Name} {\n\t$0\n}', + insertTextRules: monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet, + range + }, + { + label: 'library', + kind: monaco.languages.CompletionItemKind.Snippet, + insertText: 'library ${1:Name} {\n\t$0\n}', + insertTextRules: monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet, + range + }, + { + label: 'interface', + kind: monaco.languages.CompletionItemKind.Snippet, + insertText: 'interface ${1:Name} {\n\t$0\n}', + insertTextRules: monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet, + range + }, + { + label: 'enum', + kind: monaco.languages.CompletionItemKind.Snippet, + insertText: 'enum ${1:Name} {${2:item1}, ${3:item2} }', + insertTextRules: monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet, + range + }, + { + label: 'function', + kind: monaco.languages.CompletionItemKind.Snippet, + insertText: 'function ${1:name}(${2:params}) {\n\t${3:code}\n}', + insertTextRules: monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet, + range + }, + { + label: 'constructor', + kind: monaco.languages.CompletionItemKind.Snippet, + insertText: 'constructor(${1:params}) {\n\t${2:code}\n}', + insertTextRules: monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet, + range + }, + { + label: 'ifstatement', + kind: monaco.languages.CompletionItemKind.Snippet, + insertText: 'if (${1:condition}) {\n\t${2:code}\n}', + insertTextRules: monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet, + range + }, + { + label: 'ifstatementelse', + kind: monaco.languages.CompletionItemKind.Snippet, + insertText: 'if (${1:condition}) {\n\t${2:code}\n} else {\n\t${3:code}\n}', + insertTextRules: monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet, + range + }, + { + label: 'while loop', + kind: monaco.languages.CompletionItemKind.Snippet, + insertText: 'while (${1:condition}) \n{\n\t${2:code}\n};', + insertTextRules: monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet, + range + }, + { + label: 'do while loop', + kind: monaco.languages.CompletionItemKind.Snippet, + insertText: 'do {\n\t${2:code}\n} \nwhile (${1:condition});', + insertTextRules: monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet, + range + }, + { + label: 'for loop', + kind: monaco.languages.CompletionItemKind.Snippet, + insertText: 'for (${1:init}; ${2:condition}; ${3:increment}) \n{\n\t${4:code}\n};', + insertTextRules: monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet, + range + }, + { + label: 'pragma', + kind: monaco.languages.CompletionItemKind.Snippet, + insertText: '// SPDX-License-Identifier: MIT\npragma solidity ${1:version};', + insertTextRules: monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet, + range + }, + { + label: 'SPDX-License-Identifier', + kind: monaco.languages.CompletionItemKind.Snippet, + insertText: '// SPDX-License-Identifier: MIT', + insertTextRules: monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet, + range + } + ] } export function getTxCompletionItems(range: monacoTypes.IRange, monaco): monacoTypes.languages.CompletionItem[] { - return [ - { - detail: '(uint): gas price of the transaction', - kind: monaco.languages.CompletionItemKind.Property, - insertText: 'gas', - label: 'gas', - range - }, - { - detail: '(address): sender of the transaction (full call chain)', - kind: monaco.languages.CompletionItemKind.Property, - insertText: 'origin', - label: 'origin', - range - }, - ]; + return [ + { + detail: '(uint): gas price of the transaction', + kind: monaco.languages.CompletionItemKind.Property, + insertText: 'gas', + label: 'gas', + range + }, + { + detail: '(address): sender of the transaction (full call chain)', + kind: monaco.languages.CompletionItemKind.Property, + insertText: 'origin', + label: 'origin', + range + }, + ]; } export function getMsgCompletionItems(range: monacoTypes.IRange, monaco): monacoTypes.languages.CompletionItem[] { - return [ - { - detail: '(bytes): complete calldata', - kind: monaco.languages.CompletionItemKind.Property, - insertText: 'data', - label: 'data', - range - }, - { - detail: '(uint): remaining gas DEPRICATED in 0.4.21 use gasleft()', - kind: monaco.languages.CompletionItemKind.Property, - insertText: 'gas', - label: 'gas', - range - }, - { - detail: '(address): sender of the message (current call)', - kind: monaco.languages.CompletionItemKind.Property, - insertText: 'sender', - label: 'sender', - range - }, - { - detail: '(bytes4): first four bytes of the calldata (i.e. export function identifier)', - kind: monaco.languages.CompletionItemKind.Property, - insertText: 'sig', - label: 'sig', - range - }, - { - detail: '(uint): number of wei sent with the message', - kind: monaco.languages.CompletionItemKind.Property, - insertText: 'value', - label: 'value', - range - }, - ]; + return [ + { + detail: '(bytes): complete calldata', + kind: monaco.languages.CompletionItemKind.Property, + insertText: 'data', + label: 'data', + range + }, + { + detail: '(uint): remaining gas DEPRICATED in 0.4.21 use gasleft()', + kind: monaco.languages.CompletionItemKind.Property, + insertText: 'gas', + label: 'gas', + range + }, + { + detail: '(address): sender of the message (current call)', + kind: monaco.languages.CompletionItemKind.Property, + insertText: 'sender', + label: 'sender', + range + }, + { + detail: '(bytes4): first four bytes of the calldata (i.e. export function identifier)', + kind: monaco.languages.CompletionItemKind.Property, + insertText: 'sig', + label: 'sig', + range + }, + { + detail: '(uint): number of wei sent with the message', + kind: monaco.languages.CompletionItemKind.Property, + insertText: 'value', + label: 'value', + range + }, + ]; } export function getAbiCompletionItems(range: monacoTypes.IRange, monaco): monacoTypes.languages.CompletionItem[] { - return [ - { - detail: 'encode(..) returs (bytes): ABI-encodes the given arguments', - insertText: 'encode(${1:arg});', - kind: monaco.languages.CompletionItemKind.Method, - insertTextRules: monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet, - label: 'encode', - range - }, - { - detail: 'encodeCall(function functionPointer, (...)) returns (bytes memory) ABI-encodes a call to functionPointer with the arguments found in the tuple', - insertText: 'encode(${1:arg});', - kind: monaco.languages.CompletionItemKind.Method, - insertTextRules: monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet, - label: 'encodecall', - range - }, - { - detail: 'encodePacked(..) returns (bytes): Performes packed encoding of the given arguments', - insertText: 'encodePacked(${1:arg});', - kind: monaco.languages.CompletionItemKind.Method, - insertTextRules: monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet, - label: 'encodePacked', - range - }, - { - detail: 'encodeWithSelector(bytes4,...) returns (bytes): ABI-encodes the given arguments starting from the second and prepends the given four-byte selector', - insertText: 'encodeWithSelector(${1:bytes4}, ${2:arg});', - kind: monaco.languages.CompletionItemKind.Method, - insertTextRules: monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet, - label: 'encodeWithSelector', - range - }, - { - detail: 'encodeWithSignature(string,...) returns (bytes): Equivalent to abi.encodeWithSelector(bytes4(keccak256(signature), ...)`', - insertText: 'encodeWithSignature(${1:signatureString}, ${2:arg});', - kind: monaco.languages.CompletionItemKind.Method, - insertTextRules: monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet, - label: 'encodeWithSignature', - range - }, - ]; + return [ + { + detail: 'encode(..) returs (bytes): ABI-encodes the given arguments', + insertText: 'encode(${1:arg});', + kind: monaco.languages.CompletionItemKind.Method, + insertTextRules: monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet, + label: 'encode', + range + }, + { + detail: 'encodeCall(function functionPointer, (...)) returns (bytes memory) ABI-encodes a call to functionPointer with the arguments found in the tuple', + insertText: 'encode(${1:arg});', + kind: monaco.languages.CompletionItemKind.Method, + insertTextRules: monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet, + label: 'encodecall', + range + }, + { + detail: 'encodePacked(..) returns (bytes): Performes packed encoding of the given arguments', + insertText: 'encodePacked(${1:arg});', + kind: monaco.languages.CompletionItemKind.Method, + insertTextRules: monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet, + label: 'encodePacked', + range + }, + { + detail: 'encodeWithSelector(bytes4,...) returns (bytes): ABI-encodes the given arguments starting from the second and prepends the given four-byte selector', + insertText: 'encodeWithSelector(${1:bytes4}, ${2:arg});', + kind: monaco.languages.CompletionItemKind.Method, + insertTextRules: monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet, + label: 'encodeWithSelector', + range + }, + { + detail: 'encodeWithSignature(string,...) returns (bytes): Equivalent to abi.encodeWithSelector(bytes4(keccak256(signature), ...)`', + insertText: 'encodeWithSignature(${1:signatureString}, ${2:arg});', + kind: monaco.languages.CompletionItemKind.Method, + insertTextRules: monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet, + label: 'encodeWithSignature', + range + }, + ]; } export function GetCompletionTypes(range: monacoTypes.IRange, monaco): monacoTypes.languages.CompletionItem[] { - const completionItems = []; - const types = ['address', 'string', 'bytes', 'byte', 'int', 'uint', 'bool', 'hash']; - for (let index = 8; index <= 256; index += 8) { - types.push('int' + index); - types.push('uint' + index); - types.push('bytes' + index / 8); - } - types.forEach(type => { - const completionItem = CreateCompletionItem(type, monaco.languages.CompletionItemKind.Keyword, type + ' type', range); - completionItems.push(completionItem); - }); - // add mapping - return completionItems; + const completionItems = []; + const types = ['address', 'string', 'bytes', 'byte', 'int', 'uint', 'bool', 'hash']; + for (let index = 8; index <= 256; index += 8) { + types.push('int' + index); + types.push('uint' + index); + types.push('bytes' + index / 8); + } + types.forEach(type => { + const completionItem = CreateCompletionItem(type, monaco.languages.CompletionItemKind.Keyword, type + ' type', range); + completionItems.push(completionItem); + }); + // add mapping + return completionItems; } function CreateCompletionItem(label: string, kind: monacoTypes.languages.CompletionItemKind, detail: string, range: monacoTypes.IRange) { - const completionItem: monacoTypes.languages.CompletionItem = { - label, - kind, - detail, - insertText: label, - range - } - completionItem.kind = kind; - completionItem.detail = detail; - return completionItem; + const completionItem: monacoTypes.languages.CompletionItem = { + label, + kind, + detail, + insertText: label, + range + } + completionItem.kind = kind; + completionItem.detail = detail; + return completionItem; } export function GetCompletionKeywords(range: monacoTypes.IRange, monaco): monacoTypes.languages.CompletionItem[] { - const completionItems = []; - const keywords = ['modifier', 'mapping', 'break', 'continue', 'delete', 'else', 'for', - 'after', 'promise', 'alias', 'apply', 'auto', 'copyof', 'default', 'define', 'final', 'implements', - 'inline', 'let', 'macro', 'match', 'mutable', 'null', 'of', 'partial', 'reference', 'relocatable', - 'sealed', 'sizeof', 'static', 'supports', 'switch', 'typedef', - 'if', 'new', 'return', 'returns', 'while', 'using', 'emit', 'anonymous', 'indexed', - 'private', 'public', 'external', 'internal', 'payable', 'nonpayable', 'view', 'pure', 'case', 'do', 'else', 'finally', - 'in', 'instanceof', 'return', 'throw', 'try', 'catch', 'typeof', 'yield', 'void', 'virtual', 'override']; - keywords.forEach(unit => { - const completionItem: monacoTypes.languages.CompletionItem = { - label: unit, - kind: monaco.languages.CompletionItemKind.Keyword, - detail: unit + ' keyword', - insertText: `${unit} `, - range - } - completionItems.push(completionItem); - }); - - completionItems.push(CreateCompletionItem('contract', monaco.languages.CompletionItemKind.Class, null, range)); - completionItems.push(CreateCompletionItem('library', monaco.languages.CompletionItemKind.Class, null, range)); - completionItems.push(CreateCompletionItem('storage', monaco.languages.CompletionItemKind.Field, null, range)); - completionItems.push(CreateCompletionItem('calldata', monaco.languages.CompletionItemKind.Field, null, range)); - completionItems.push(CreateCompletionItem('memory', monaco.languages.CompletionItemKind.Field, null, range)); - completionItems.push(CreateCompletionItem('var', monaco.languages.CompletionItemKind.Field, null, range)); - completionItems.push(CreateCompletionItem('constant', monaco.languages.CompletionItemKind.Constant, null, range)); - completionItems.push(CreateCompletionItem('immutable', monaco.languages.CompletionItemKind.Keyword, null, range)); - completionItems.push(CreateCompletionItem('constructor', monaco.languages.CompletionItemKind.Constructor, null, range)); - completionItems.push(CreateCompletionItem('event', monaco.languages.CompletionItemKind.Event, null, range)); - completionItems.push(CreateCompletionItem('import', monaco.languages.CompletionItemKind.Module, null, range)); - completionItems.push(CreateCompletionItem('enum', monaco.languages.CompletionItemKind.Enum, null, range)); - completionItems.push(CreateCompletionItem('struct', monaco.languages.CompletionItemKind.Struct, null, range)); - completionItems.push(CreateCompletionItem('function', monaco.languages.CompletionItemKind.Function, null, range)); - - return completionItems; + const completionItems = []; + const keywords = ['modifier', 'mapping', 'break', 'continue', 'delete', 'else', 'for', + 'after', 'promise', 'alias', 'apply', 'auto', 'copyof', 'default', 'define', 'final', 'implements', + 'inline', 'let', 'macro', 'match', 'mutable', 'null', 'of', 'partial', 'reference', 'relocatable', + 'sealed', 'sizeof', 'static', 'supports', 'switch', 'typedef', + 'if', 'new', 'return', 'returns', 'while', 'using', 'emit', 'anonymous', 'indexed', + 'private', 'public', 'external', 'internal', 'payable', 'nonpayable', 'view', 'pure', 'case', 'do', 'else', 'finally', + 'in', 'instanceof', 'return', 'throw', 'try', 'catch', 'typeof', 'yield', 'void', 'virtual', 'override']; + keywords.forEach(unit => { + const completionItem: monacoTypes.languages.CompletionItem = { + label: unit, + kind: monaco.languages.CompletionItemKind.Keyword, + detail: unit + ' keyword', + insertText: `${unit} `, + range + } + completionItems.push(completionItem); + }); + + completionItems.push(CreateCompletionItem('contract', monaco.languages.CompletionItemKind.Class, null, range)); + completionItems.push(CreateCompletionItem('library', monaco.languages.CompletionItemKind.Class, null, range)); + completionItems.push(CreateCompletionItem('storage', monaco.languages.CompletionItemKind.Field, null, range)); + completionItems.push(CreateCompletionItem('calldata', monaco.languages.CompletionItemKind.Field, null, range)); + completionItems.push(CreateCompletionItem('memory', monaco.languages.CompletionItemKind.Field, null, range)); + completionItems.push(CreateCompletionItem('var', monaco.languages.CompletionItemKind.Field, null, range)); + completionItems.push(CreateCompletionItem('constant', monaco.languages.CompletionItemKind.Constant, null, range)); + completionItems.push(CreateCompletionItem('immutable', monaco.languages.CompletionItemKind.Keyword, null, range)); + completionItems.push(CreateCompletionItem('constructor', monaco.languages.CompletionItemKind.Constructor, null, range)); + completionItems.push(CreateCompletionItem('event', monaco.languages.CompletionItemKind.Event, null, range)); + completionItems.push(CreateCompletionItem('import', monaco.languages.CompletionItemKind.Module, null, range)); + completionItems.push(CreateCompletionItem('enum', monaco.languages.CompletionItemKind.Enum, null, range)); + completionItems.push(CreateCompletionItem('struct', monaco.languages.CompletionItemKind.Struct, null, range)); + completionItems.push(CreateCompletionItem('function', monaco.languages.CompletionItemKind.Function, null, range)); + + return completionItems; } export function GeCompletionUnits(range: monacoTypes.IRange, monaco): monacoTypes.languages.CompletionItem[] { - const completionItems = []; - const etherUnits = ['wei', 'gwei', 'finney', 'szabo', 'ether']; - etherUnits.forEach(unit => { - const completionItem = CreateCompletionItem(unit, monaco.languages.CompletionItemKind.Unit, unit + ': ether unit', range); - completionItems.push(completionItem); - }); - - const timeUnits = ['seconds', 'minutes', 'hours', 'days', 'weeks', 'years']; - timeUnits.forEach(unit => { - const completionItem = CreateCompletionItem(unit, monaco.languages.CompletionItemKind.Unit, unit + ': time unit', range); - completionItem.kind = monaco.languages.CompletionItemKind.Unit; - - if (unit !== 'years') { - completionItem.detail = unit + ': time unit'; - } else { - completionItem.detail = 'DEPRECATED: ' + unit + ': time unit'; - } - completionItems.push(completionItem); - }); + const completionItems = []; + const etherUnits = ['wei', 'gwei', 'finney', 'szabo', 'ether']; + etherUnits.forEach(unit => { + const completionItem = CreateCompletionItem(unit, monaco.languages.CompletionItemKind.Unit, unit + ': ether unit', range); + completionItems.push(completionItem); + }); + + const timeUnits = ['seconds', 'minutes', 'hours', 'days', 'weeks', 'years']; + timeUnits.forEach(unit => { + const completionItem = CreateCompletionItem(unit, monaco.languages.CompletionItemKind.Unit, unit + ': time unit', range); + completionItem.kind = monaco.languages.CompletionItemKind.Unit; + + if (unit !== 'years') { + completionItem.detail = unit + ': time unit'; + } else { + completionItem.detail = 'DEPRECATED: ' + unit + ': time unit'; + } + completionItems.push(completionItem); + }); - return completionItems; + return completionItems; } export function GetImports(range: monacoTypes.IRange - , monaco, data: CodeParserImportsData - , word: string + , monaco, data: CodeParserImportsData + , word: string ): monacoTypes.languages.CompletionItem[] { - let list = [] - if (!word.startsWith('@')) { - word = word.replace('"', ''); - const nextPaths = [...new Set(data.files - .filter((item) => item.startsWith(word)) - .map((item) => item.replace(word, '').split('/')[0]))] - - list = [...list, ...nextPaths - .filter((item) => !item.endsWith('.sol')) - .map((item) => { - return { - kind: monaco.languages.CompletionItemKind.Folder, - range: range, - label: `${item}`, - insertText: `${item}`, - } - })] - - - list = [...list, - ...data.files - .filter((item) => item.startsWith(word)) - .map((item) => { - return { - kind: monaco.languages.CompletionItemKind.File, - range: range, - label: `${item}`, - insertText: `${item.replace(word, '')}`, - } - })] - } - if (word === '@' || word === '') { - list = [...list, ...data.packages.map((item) => { - return { - kind: monaco.languages.CompletionItemKind.Module, - range: range, - label: `${item}`, - insertText: word === '@' ? `${item.replace('@', '')}` : `${item}`, - } + let list = [] + if (!word.startsWith('@')) { + word = word.replace('"', ''); + const nextPaths = [...new Set(data.files + .filter((item) => item.startsWith(word)) + .map((item) => item.replace(word, '').split('/')[0]))] + + list = [...list, ...nextPaths + .filter((item) => !item.endsWith('.sol')) + .map((item) => { + return { + kind: monaco.languages.CompletionItemKind.Folder, + range: range, + label: `${item}`, + insertText: `${item}`, + } + })] + + + list = [...list, + ...data.files + .filter((item) => item.startsWith(word)) + .map((item) => { + return { + kind: monaco.languages.CompletionItemKind.File, + range: range, + label: `${item}`, + insertText: `${item.replace(word, '')}`, + } })] - } - if (word.startsWith('@') && word.length > 1) { - const nextPaths = [...new Set(data.modules - .filter((item) => item.startsWith(word)) - .map((item) => item.replace(word, '').split('/')[0]))] - - list = [...list, ...nextPaths - .filter((item) => !item.endsWith('.sol')) - .map((item) => { - return { - kind: monaco.languages.CompletionItemKind.Folder, - range: range, - label: `${item}`, - insertText: `${item}`, - } - })] - - list = [...list - , ...data.modules - .filter((item) => item.startsWith(word)) - .map((item) => { - // remove the first part if it starts with @ - let label = item; - if (label.startsWith('@')) { - label = label.substring(label.indexOf('/') + 1); - } - const filename = path.basename(label) - return { - kind: monaco.languages.CompletionItemKind.Reference, - range: range, - label: `${filename}: ${label}`, - insertText: `${item.replace(word, '')}`, - } - }) - ] - } - return list; + } + if (word === '@' || word === '') { + list = [...list, ...data.packages.map((item) => { + return { + kind: monaco.languages.CompletionItemKind.Module, + range: range, + label: `${item}`, + insertText: word === '@' ? `${item.replace('@', '')}` : `${item}`, + } + })] + } + if (word.startsWith('@') && word.length > 1) { + const nextPaths = [...new Set(data.modules + .filter((item) => item.startsWith(word)) + .map((item) => item.replace(word, '').split('/')[0]))] + + list = [...list, ...nextPaths + .filter((item) => !item.endsWith('.sol')) + .map((item) => { + return { + kind: monaco.languages.CompletionItemKind.Folder, + range: range, + label: `${item}`, + insertText: `${item}`, + } + })] + + list = [...list + , ...data.modules + .filter((item) => item.startsWith(word)) + .map((item) => { + // remove the first part if it starts with @ + let label = item; + if (label.startsWith('@')) { + label = label.substring(label.indexOf('/') + 1); + } + const filename = path.basename(label) + return { + kind: monaco.languages.CompletionItemKind.Reference, + range: range, + label: `${filename}: ${label}`, + insertText: `${item.replace(word, '')}`, + } + }) + ] + } + return list; }; export function GetGlobalVariable(range: monacoTypes.IRange, monaco): monacoTypes.languages.CompletionItem[] { - return [ - { - detail: 'Current block', - kind: monaco.languages.CompletionItemKind.Variable, - insertText: 'block', - label: 'block', - range - }, - { - detail: 'Current Message', - kind: monaco.languages.CompletionItemKind.Variable, - insertText: 'msg', - label: 'msg', - range - }, - { - detail: '(uint): current block timestamp (alias for block.timestamp)', - kind: monaco.languages.CompletionItemKind.Variable, - insertText: 'now', - label: 'now', - range - }, - { - detail: 'Current transaction', - kind: monaco.languages.CompletionItemKind.Variable, - label: 'tx', - insertText: 'tx', - range - }, - { - detail: 'ABI encoding / decoding', - kind: monaco.languages.CompletionItemKind.Variable, - label: 'abi', - insertText: 'abi', - range - }, - { - detail: '', - kind: monaco.languages.CompletionItemKind.Variable, - label: 'this', - insertText: 'this', - range - }, - ]; + return [ + { + detail: 'Current block', + kind: monaco.languages.CompletionItemKind.Variable, + insertText: 'block', + label: 'block', + range + }, + { + detail: 'Current Message', + kind: monaco.languages.CompletionItemKind.Variable, + insertText: 'msg', + label: 'msg', + range + }, + { + detail: '(uint): current block timestamp (alias for block.timestamp)', + kind: monaco.languages.CompletionItemKind.Variable, + insertText: 'now', + label: 'now', + range + }, + { + detail: 'Current transaction', + kind: monaco.languages.CompletionItemKind.Variable, + label: 'tx', + insertText: 'tx', + range + }, + { + detail: 'ABI encoding / decoding', + kind: monaco.languages.CompletionItemKind.Variable, + label: 'abi', + insertText: 'abi', + range + }, + { + detail: '', + kind: monaco.languages.CompletionItemKind.Variable, + label: 'this', + insertText: 'this', + range + }, + ]; } export function GetGlobalFunctions(range: monacoTypes.IRange, monaco): monacoTypes.languages.CompletionItem[] { - return [ - { - detail: 'assert(bool condition): throws if the condition is not met - to be used for internal errors.', - insertText: 'assert(${1:condition});', - kind: monaco.languages.CompletionItemKind.Function, - insertTextRules: monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet, - label: 'assert', - range - }, - { - detail: 'gasleft(): returns the remaining gas', - insertText: 'gasleft();', - kind: monaco.languages.CompletionItemKind.Function, - label: 'gasleft', - range - }, - { - detail: 'unicode: converts string into unicode', - insertText: 'unicode"${1:text}"', - kind: monaco.languages.CompletionItemKind.Function, - insertTextRules: monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet, - label: 'unicode', - range - }, - { - detail: 'blockhash(uint blockNumber): hash of the given block - only works for 256 most recent, excluding current, blocks', - insertText: 'blockhash(${1:blockNumber});', - kind: monaco.languages.CompletionItemKind.Function, - insertTextRules: monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet, - label: 'blockhash', - range - }, - { - detail: 'require(bool condition): reverts if the condition is not met - to be used for errors in inputs or external components.', - insertText: 'require(${1:condition});', - kind: monaco.languages.CompletionItemKind.Method, - insertTextRules: monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet, - label: 'require', - range - }, - { - // tslint:disable-next-line:max-line-length - detail: 'require(bool condition, string message): reverts if the condition is not met - to be used for errors in inputs or external components. Also provides an error message.', - insertText: 'require(${1:condition}, ${2:message});', - kind: monaco.languages.CompletionItemKind.Method, - insertTextRules: monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet, - label: 'require', - range - }, - { - detail: 'revert(): abort execution and revert state changes', - insertText: 'revert();', - kind: monaco.languages.CompletionItemKind.Method, - label: 'revert', - range - }, - { - detail: 'addmod(uint x, uint y, uint k) returns (uint):' + + return [ + { + detail: 'assert(bool condition): throws if the condition is not met - to be used for internal errors.', + insertText: 'assert(${1:condition});', + kind: monaco.languages.CompletionItemKind.Function, + insertTextRules: monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet, + label: 'assert', + range + }, + { + detail: 'gasleft(): returns the remaining gas', + insertText: 'gasleft();', + kind: monaco.languages.CompletionItemKind.Function, + label: 'gasleft', + range + }, + { + detail: 'unicode: converts string into unicode', + insertText: 'unicode"${1:text}"', + kind: monaco.languages.CompletionItemKind.Function, + insertTextRules: monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet, + label: 'unicode', + range + }, + { + detail: 'blockhash(uint blockNumber): hash of the given block - only works for 256 most recent, excluding current, blocks', + insertText: 'blockhash(${1:blockNumber});', + kind: monaco.languages.CompletionItemKind.Function, + insertTextRules: monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet, + label: 'blockhash', + range + }, + { + detail: 'require(bool condition): reverts if the condition is not met - to be used for errors in inputs or external components.', + insertText: 'require(${1:condition});', + kind: monaco.languages.CompletionItemKind.Method, + insertTextRules: monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet, + label: 'require', + range + }, + { + // tslint:disable-next-line:max-line-length + detail: 'require(bool condition, string message): reverts if the condition is not met - to be used for errors in inputs or external components. Also provides an error message.', + insertText: 'require(${1:condition}, ${2:message});', + kind: monaco.languages.CompletionItemKind.Method, + insertTextRules: monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet, + label: 'require', + range + }, + { + detail: 'revert(): abort execution and revert state changes', + insertText: 'revert();', + kind: monaco.languages.CompletionItemKind.Method, + label: 'revert', + range + }, + { + detail: 'addmod(uint x, uint y, uint k) returns (uint):' + 'compute (x + y) % k where the addition is performed with arbitrary precision and does not wrap around at 2**256', - insertText: 'addmod(${1:x}, ${2:y}, ${3:k})', - kind: monaco.languages.CompletionItemKind.Method, - insertTextRules: monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet, - label: 'addmod', - range - }, - { - detail: 'mulmod(uint x, uint y, uint k) returns (uint):' + + insertText: 'addmod(${1:x}, ${2:y}, ${3:k})', + kind: monaco.languages.CompletionItemKind.Method, + insertTextRules: monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet, + label: 'addmod', + range + }, + { + detail: 'mulmod(uint x, uint y, uint k) returns (uint):' + 'compute (x * y) % k where the multiplication is performed with arbitrary precision and does not wrap around at 2**256', - insertText: 'mulmod(${1:x}, ${2:y}, ${3:k})', - kind: monaco.languages.CompletionItemKind.Method, - insertTextRules: monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet, - label: 'mulmod', - range - }, - { - detail: 'keccak256(...) returns (bytes32):' + + insertText: 'mulmod(${1:x}, ${2:y}, ${3:k})', + kind: monaco.languages.CompletionItemKind.Method, + insertTextRules: monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet, + label: 'mulmod', + range + }, + { + detail: 'keccak256(...) returns (bytes32):' + 'compute the Ethereum-SHA-3 (Keccak-256) hash of the (tightly packed) arguments', - insertText: 'keccak256(${1:x})', - kind: monaco.languages.CompletionItemKind.Method, - insertTextRules: monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet, - label: 'keccak256', - range - }, - { - detail: 'sha256(...) returns (bytes32):' + + insertText: 'keccak256(${1:x})', + kind: monaco.languages.CompletionItemKind.Method, + insertTextRules: monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet, + label: 'keccak256', + range + }, + { + detail: 'sha256(...) returns (bytes32):' + 'compute the SHA-256 hash of the (tightly packed) arguments', - insertText: 'sha256(${1:x})', - insertTextRules: monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet, - kind: monaco.languages.CompletionItemKind.Method, - label: 'sha256', - range - }, - { - detail: 'sha3(...) returns (bytes32):' + + insertText: 'sha256(${1:x})', + insertTextRules: monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet, + kind: monaco.languages.CompletionItemKind.Method, + label: 'sha256', + range + }, + { + detail: 'sha3(...) returns (bytes32):' + 'alias to keccak256', - insertText: 'sha3(${1:x})', - insertTextRules: monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet, - kind: monaco.languages.CompletionItemKind.Method, - label: 'sha3', - range - }, - { - detail: 'ripemd160(...) returns (bytes20):' + + insertText: 'sha3(${1:x})', + insertTextRules: monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet, + kind: monaco.languages.CompletionItemKind.Method, + label: 'sha3', + range + }, + { + detail: 'ripemd160(...) returns (bytes20):' + 'compute RIPEMD-160 hash of the (tightly packed) arguments', - insertText: 'ripemd160(${1:x})', - insertTextRules: monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet, - kind: monaco.languages.CompletionItemKind.Method, - label: 'ripemd160', - range - }, - { - detail: 'ecrecover(bytes32 hash, uint8 v, bytes32 r, bytes32 s) returns (address):' + + insertText: 'ripemd160(${1:x})', + insertTextRules: monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet, + kind: monaco.languages.CompletionItemKind.Method, + label: 'ripemd160', + range + }, + { + detail: 'ecrecover(bytes32 hash, uint8 v, bytes32 r, bytes32 s) returns (address):' + 'recover the address associated with the public key from elliptic curve signature or return zero on error', - insertText: 'ecrecover(${1:hash}, ${2:v}, ${3:r}, ${4:s})', - insertTextRules: monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet, - kind: monaco.languages.CompletionItemKind.Method, - label: 'ecrecover', - range - }, - - ]; + insertText: 'ecrecover(${1:hash}, ${2:v}, ${3:r}, ${4:s})', + insertTextRules: monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet, + kind: monaco.languages.CompletionItemKind.Method, + label: 'ecrecover', + range + }, + + ]; } export function getContextualAutoCompleteByGlobalVariable(word: string, range: monacoTypes.IRange, monaco): monacoTypes.languages.CompletionItem[] { - if (word === 'block') { - return getBlockCompletionItems(range, monaco); - } - if (word === 'string') { - return getStringCompletionItems(range, monaco); - } - if (word === 'bytes') { - return getBytesCompletionItems(range, monaco); - } - if (word === 'msg') { - return getMsgCompletionItems(range, monaco); - } - if (word === 'tx') { - return getTxCompletionItems(range, monaco); - } - if (word === 'abi') { - return getAbiCompletionItems(range, monaco); - } - if (word === 'sender') { - return getAddressCompletionItems(range, monaco); - } - return null; + if (word === 'block') { + return getBlockCompletionItems(range, monaco); + } + if (word === 'string') { + return getStringCompletionItems(range, monaco); + } + if (word === 'bytes') { + return getBytesCompletionItems(range, monaco); + } + if (word === 'msg') { + return getMsgCompletionItems(range, monaco); + } + if (word === 'tx') { + return getTxCompletionItems(range, monaco); + } + if (word === 'abi') { + return getAbiCompletionItems(range, monaco); + } + if (word === 'sender') { + return getAddressCompletionItems(range, monaco); + } + return null; } export function getArrayCompletionItems(range: monacoTypes.IRange, monaco): monacoTypes.languages.CompletionItem[] { - return [ - { - detail: '', - kind: monaco.languages.CompletionItemKind.Method, - insertText: 'length;', - insertTextRules: monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet, - label: 'length', - range, - }, - { - detail: '', - kind: monaco.languages.CompletionItemKind.Method, - insertText: 'push(${1:value});', - insertTextRules: monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet, - label: 'push(value)', - range, - }, - { - detail: '', - kind: monaco.languages.CompletionItemKind.Method, - insertText: 'push();', - insertTextRules: monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet, - label: 'push()', - range, - }, - { - detail: '', - kind: monaco.languages.CompletionItemKind.Method, - insertText: 'pop();', - insertTextRules: monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet, - label: 'pop()', - range, - }, - ] + return [ + { + detail: '', + kind: monaco.languages.CompletionItemKind.Method, + insertText: 'length;', + insertTextRules: monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet, + label: 'length', + range, + }, + { + detail: '', + kind: monaco.languages.CompletionItemKind.Method, + insertText: 'push(${1:value});', + insertTextRules: monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet, + label: 'push(value)', + range, + }, + { + detail: '', + kind: monaco.languages.CompletionItemKind.Method, + insertText: 'push();', + insertTextRules: monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet, + label: 'push()', + range, + }, + { + detail: '', + kind: monaco.languages.CompletionItemKind.Method, + insertText: 'pop();', + insertTextRules: monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet, + label: 'pop()', + range, + }, + ] } export function getAddressCompletionItems(range: monacoTypes.IRange, monaco): monacoTypes.languages.CompletionItem[] { - return [ - { - detail: '(uint256): balance of the Address in Wei', - kind: monaco.languages.CompletionItemKind.Method, - insertText: 'balance;', - insertTextRules: monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet, - label: 'balance', - range, - }, - { - detail: '(bytes memory): code at the Address (can be empty)', - kind: monaco.languages.CompletionItemKind.Method, - insertText: 'code;', - insertTextRules: monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet, - label: 'code', - range, - }, - { - detail: '(bytes32): the codehash of the Address', - kind: monaco.languages.CompletionItemKind.Method, - insertText: 'codehash;', - insertTextRules: monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet, - label: 'codehash', - range, - }, - { - detail: '(uint256 amount) returns (bool): send given amount of Wei to Address, returns false on failure', - kind: monaco.languages.CompletionItemKind.Method, - insertText: 'send(${1:value});', - insertTextRules: monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet, - label: 'send()', - range, - }, - { - detail: '(uint256 amount): send given amount of Wei to Address, throws on failure', - kind: monaco.languages.CompletionItemKind.Method, - insertText: 'transfer(${1:value});', - insertTextRules: monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet, - label: 'transfer()', - range, - }, - ] + return [ + { + detail: '(uint256): balance of the Address in Wei', + kind: monaco.languages.CompletionItemKind.Method, + insertText: 'balance;', + insertTextRules: monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet, + label: 'balance', + range, + }, + { + detail: '(bytes memory): code at the Address (can be empty)', + kind: monaco.languages.CompletionItemKind.Method, + insertText: 'code;', + insertTextRules: monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet, + label: 'code', + range, + }, + { + detail: '(bytes32): the codehash of the Address', + kind: monaco.languages.CompletionItemKind.Method, + insertText: 'codehash;', + insertTextRules: monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet, + label: 'codehash', + range, + }, + { + detail: '(uint256 amount) returns (bool): send given amount of Wei to Address, returns false on failure', + kind: monaco.languages.CompletionItemKind.Method, + insertText: 'send(${1:value});', + insertTextRules: monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet, + label: 'send()', + range, + }, + { + detail: '(uint256 amount): send given amount of Wei to Address, throws on failure', + kind: monaco.languages.CompletionItemKind.Method, + insertText: 'transfer(${1:value});', + insertTextRules: monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet, + label: 'transfer()', + range, + }, + ] } export function getContextualAutoCompleteBTypeName(word: string, range: monacoTypes.IRange, monaco): monacoTypes.languages.CompletionItem[] { - if (word === 'ArrayTypeName') { - return getArrayCompletionItems(range, monaco); - } - if (word === 'bytes') { - return getBytesCompletionItems(range, monaco); - } - if (word === 'address') { - return getAddressCompletionItems(range, monaco); - } - return []; + if (word === 'ArrayTypeName') { + return getArrayCompletionItems(range, monaco); + } + if (word === 'bytes') { + return getBytesCompletionItems(range, monaco); + } + if (word === 'address') { + return getAddressCompletionItems(range, monaco); + } + return []; } diff --git a/libs/remix-ui/editor/src/lib/providers/completionProvider.ts b/libs/remix-ui/editor/src/lib/providers/completionProvider.ts index d375748413..e4c3097872 100644 --- a/libs/remix-ui/editor/src/lib/providers/completionProvider.ts +++ b/libs/remix-ui/editor/src/lib/providers/completionProvider.ts @@ -6,431 +6,431 @@ import { monacoTypes } from '@remix-ui/editor'; import { retrieveNodesAtPosition } from "../helpers/retrieveNodesAtPosition"; export class RemixCompletionProvider implements monacoTypes.languages.CompletionItemProvider { - props: EditorUIProps - monaco: any - maximumItemsForContractCompletion = 100 - - constructor(props: any, monaco: any) { - this.props = props - this.monaco = monaco - } + props: EditorUIProps + monaco: any + maximumItemsForContractCompletion = 100 + + constructor(props: any, monaco: any) { + this.props = props + this.monaco = monaco + } + + triggerCharacters = ['.', '', '"', '@', '/'] + async provideCompletionItems(model: monacoTypes.editor.ITextModel, position: monacoTypes.Position, context: monacoTypes.languages.CompletionContext): Promise { + + const completionSettings = await this.props.plugin.call('config', 'getAppParameter', 'settings/auto-completion') + if (!completionSettings) return + + const word = model.getWordUntilPosition(position); + const range = { + startLineNumber: position.lineNumber, + endLineNumber: position.lineNumber, + startColumn: word.startColumn, + endColumn: word.endColumn + }; + + const line = model.getLineContent(position.lineNumber) + let nodes: AstNode[] = [] + let suggestions: monacoTypes.languages.CompletionItem[] = [] + if (context.triggerCharacter === '"' || context.triggerCharacter === '@' || context.triggerCharacter === '/') { + + const lastpart = line.substring(0, position.column - 1).split(';').pop() + if (lastpart.startsWith('import')) { + const imports = await this.props.plugin.call('codeParser', 'getImports') + if (context.triggerCharacter === '"' || context.triggerCharacter === '@') { + suggestions = [...suggestions, + ...GetImports(range, this.monaco, imports, context.triggerCharacter), + ] + } else if (context.triggerCharacter === '/') { + const word = line.split('"')[1] + suggestions = [...suggestions, + ...GetImports(range, this.monaco, imports, word), + ] + } else { + return + } + } + + } else if (context.triggerCharacter === '.') { + const lineTextBeforeCursor: string = line.substring(0, position.column - 1) + const lastNodeInExpression = await this.getLastNodeInExpression(lineTextBeforeCursor) + const expressionElements = lineTextBeforeCursor.split('.') + + let dotCompleted = false + + // handles completion from for builtin types + if (lastNodeInExpression.memberName === 'sender') { // exception for this member + lastNodeInExpression.name = 'sender' + } + const globalCompletion = getContextualAutoCompleteByGlobalVariable(lastNodeInExpression.name, range, this.monaco) + if (globalCompletion) { + dotCompleted = true + suggestions = [...suggestions, ...globalCompletion] + } + // handle completion for global THIS. + if (lastNodeInExpression.name === 'this') { + dotCompleted = true + nodes = [...nodes, ...await this.getThisCompletions()] + } + // handle completion for other dot completions + if (expressionElements.length > 1 && !dotCompleted) { + + const nameOfLastTypedExpression = lastNodeInExpression.name || lastNodeInExpression.memberName + const dotCompletions = await this.getDotCompletions(nameOfLastTypedExpression, range) + nodes = [...nodes, ...dotCompletions.nodes] + suggestions = [...suggestions, ...dotCompletions.suggestions] + } + } else { + + // handles contract completions and other suggestions + suggestions = [...suggestions, + ...GetGlobalVariable(range, this.monaco), + ...getCompletionSnippets(range, this.monaco), + ...GetCompletionTypes(range, this.monaco), + ...GetCompletionKeywords(range, this.monaco), + ...GetGlobalFunctions(range, this.monaco), + ...GeCompletionUnits(range, this.monaco), + ] + + let contractCompletions = await this.getContractCompletions() + + // we can't have external nodes without using this. + contractCompletions = contractCompletions.filter(node => { + if (node.visibility && node.visibility === 'external') { + return false + } + return true + }) - triggerCharacters = ['.', '', '"', '@', '/'] - async provideCompletionItems(model: monacoTypes.editor.ITextModel, position: monacoTypes.Position, context: monacoTypes.languages.CompletionContext): Promise { - - const completionSettings = await this.props.plugin.call('config', 'getAppParameter', 'settings/auto-completion') - if (!completionSettings) return - - const word = model.getWordUntilPosition(position); - const range = { - startLineNumber: position.lineNumber, - endLineNumber: position.lineNumber, - startColumn: word.startColumn, - endColumn: word.endColumn - }; - - const line = model.getLineContent(position.lineNumber) - let nodes: AstNode[] = [] - let suggestions: monacoTypes.languages.CompletionItem[] = [] - if (context.triggerCharacter === '"' || context.triggerCharacter === '@' || context.triggerCharacter === '/') { - - const lastpart = line.substring(0, position.column - 1).split(';').pop() - if (lastpart.startsWith('import')) { - const imports = await this.props.plugin.call('codeParser', 'getImports') - if (context.triggerCharacter === '"' || context.triggerCharacter === '@') { - suggestions = [...suggestions, - ...GetImports(range, this.monaco, imports, context.triggerCharacter), - ] - } else if (context.triggerCharacter === '/') { - const word = line.split('"')[1] - suggestions = [...suggestions, - ...GetImports(range, this.monaco, imports, word), - ] - } else { - return - } - } + nodes = [...nodes, ...contractCompletions] - } else if (context.triggerCharacter === '.') { - const lineTextBeforeCursor: string = line.substring(0, position.column - 1) - const lastNodeInExpression = await this.getLastNodeInExpression(lineTextBeforeCursor) - const expressionElements = lineTextBeforeCursor.split('.') + } - let dotCompleted = false + // remove duplicates + const nodeIds = {}; + let filteredNodes = nodes.filter((node) => { + if (node.id) { + if (nodeIds[node.id]) { + return false; + } + nodeIds[node.id] = true; + } + return true; + }); + + // truncate for performance + if (filteredNodes.length > this.maximumItemsForContractCompletion) { + // await this.props.plugin.call('notification', 'toast', `Too many completion items. Only ${this.maximumItemsForContractCompletion} items will be shown.`) + filteredNodes = filteredNodes.slice(0, this.maximumItemsForContractCompletion) + } - // handles completion from for builtin types - if (lastNodeInExpression.memberName === 'sender') { // exception for this member - lastNodeInExpression.name = 'sender' - } - const globalCompletion = getContextualAutoCompleteByGlobalVariable(lastNodeInExpression.name, range, this.monaco) - if (globalCompletion) { - dotCompleted = true - suggestions = [...suggestions, ...globalCompletion] - } - // handle completion for global THIS. - if (lastNodeInExpression.name === 'this') { - dotCompleted = true - nodes = [...nodes, ...await this.getThisCompletions()] - } - // handle completion for other dot completions - if (expressionElements.length > 1 && !dotCompleted) { + const getNodeLink = async (node: any) => { + return await this.props.plugin.call('codeParser', 'getNodeLink', node) + } - const nameOfLastTypedExpression = lastNodeInExpression.name || lastNodeInExpression.memberName - const dotCompletions = await this.getDotCompletions(nameOfLastTypedExpression, range) - nodes = [...nodes, ...dotCompletions.nodes] - suggestions = [...suggestions, ...dotCompletions.suggestions] - } - } else { + const getDocs = async (node: any) => { + return await this.props.plugin.call('codeParser', 'getNodeDocumentation', node) + } - // handles contract completions and other suggestions - suggestions = [...suggestions, - ...GetGlobalVariable(range, this.monaco), - ...getCompletionSnippets(range, this.monaco), - ...GetCompletionTypes(range, this.monaco), - ...GetCompletionKeywords(range, this.monaco), - ...GetGlobalFunctions(range, this.monaco), - ...GeCompletionUnits(range, this.monaco), - ] - - let contractCompletions = await this.getContractCompletions() - - // we can't have external nodes without using this. - contractCompletions = contractCompletions.filter(node => { - if (node.visibility && node.visibility === 'external') { - return false - } - return true - }) + const getParamaters = async (node: any) => { + return await this.props.plugin.call('codeParser', 'getFunctionParamaters', node) + } - nodes = [...nodes, ...contractCompletions] + const completeParameters = async (parameters: any) => { + const localParam = (parameters && parameters.parameters) || (parameters) + if (localParam) { + const params = [] + for (const key in localParam) { + params.push('${' + (key + 1) + ':' + localParam[key].name + '}') + } + return `(${params.join(', ')})` + } + } + const getVariableDeclaration = async (node: any) => { + let variableDeclaration = await this.props.plugin.call('codeParser', 'getVariableDeclaration', node) + if (node.scope) { + const scopeNode = await this.props.plugin.call('codeParser', 'getNodeById', node.scope) + if (scopeNode) { + variableDeclaration = `${scopeNode.name}.${variableDeclaration}` } + } + return variableDeclaration + } - // remove duplicates - const nodeIds = {}; - let filteredNodes = nodes.filter((node) => { - if (node.id) { - if (nodeIds[node.id]) { - return false; - } - nodeIds[node.id] = true; - } - return true; - }); - // truncate for performance - if (filteredNodes.length > this.maximumItemsForContractCompletion) { - // await this.props.plugin.call('notification', 'toast', `Too many completion items. Only ${this.maximumItemsForContractCompletion} items will be shown.`) - filteredNodes = filteredNodes.slice(0, this.maximumItemsForContractCompletion) + for (const node of Object.values(filteredNodes) as any[]) { + if (!node.name) continue + if (node.nodeType === 'VariableDeclaration') { + const completion = { + label: { label: `"${node.name}"`, description: await getNodeLink(node), detail: ` ${await getVariableDeclaration(node)}` }, + kind: this.monaco.languages.CompletionItemKind.Variable, + insertText: node.name, + range: range, + documentation: await getDocs(node) } - - const getNodeLink = async (node: any) => { - return await this.props.plugin.call('codeParser', 'getNodeLink', node) + suggestions.push(completion) + } else if (node.nodeType === 'FunctionDefinition') { + const completion = { + label: { label: `"${node.name}"`, description: await getNodeLink(node), detail: ` -> ${node.name} ${await getParamaters(node)}` }, + kind: this.monaco.languages.CompletionItemKind.Function, + insertText: `${node.name}${await completeParameters(node.parameters)};`, + insertTextRules: this.monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet, + range: range, + documentation: await getDocs(node) } - - const getDocs = async (node: any) => { - return await this.props.plugin.call('codeParser', 'getNodeDocumentation', node) + suggestions.push(completion) + } else if + (node.nodeType === 'ContractDefinition') { + const completion = { + label: { label: `"${node.name}"`, description: await getNodeLink(node), detail: ` ${node.name}` }, + kind: this.monaco.languages.CompletionItemKind.Interface, + insertText: node.name, + range: range, + documentation: await getDocs(node) } - - const getParamaters = async (node: any) => { - return await this.props.plugin.call('codeParser', 'getFunctionParamaters', node) + suggestions.push(completion) + } else if + (node.nodeType === 'StructDefinition') { + const completion = { + label: { label: `"${node.name}"`, description: await getNodeLink(node), detail: ` ${node.name}` }, + kind: this.monaco.languages.CompletionItemKind.Struct, + insertText: node.name, + range: range, + documentation: await getDocs(node) } - - const completeParameters = async (parameters: any) => { - const localParam = (parameters && parameters.parameters) || (parameters) - if (localParam) { - const params = [] - for (const key in localParam) { - params.push('${' + (key + 1) + ':' + localParam[key].name + '}') - } - return `(${params.join(', ')})` - } + suggestions.push(completion) + } else if + (node.nodeType === 'EnumDefinition') { + const completion = { + label: { label: `"${node.name}"`, description: await getNodeLink(node), detail: ` ${node.name}` }, + kind: this.monaco.languages.CompletionItemKind.Enum, + insertText: node.name, + range: range, + documentation: await getDocs(node) } - - const getVariableDeclaration = async (node: any) => { - let variableDeclaration = await this.props.plugin.call('codeParser', 'getVariableDeclaration', node) - if (node.scope) { - const scopeNode = await this.props.plugin.call('codeParser', 'getNodeById', node.scope) - if (scopeNode) { - variableDeclaration = `${scopeNode.name}.${variableDeclaration}` - } - } - return variableDeclaration + suggestions.push(completion) + } else if + (node.nodeType === 'EventDefinition') { + const completion = { + label: { label: `"${node.name}"`, description: await getNodeLink(node), detail: ` -> ${node.name} ${await getParamaters(node)}` }, + kind: this.monaco.languages.CompletionItemKind.Event, + insertText: `${node.name}${await completeParameters(node.parameters)};`, + insertTextRules: this.monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet, + range: range, + documentation: await getDocs(node) } - - - for (const node of Object.values(filteredNodes) as any[]) { - if (!node.name) continue - if (node.nodeType === 'VariableDeclaration') { - const completion = { - label: { label: `"${node.name}"`, description: await getNodeLink(node), detail: ` ${await getVariableDeclaration(node)}` }, - kind: this.monaco.languages.CompletionItemKind.Variable, - insertText: node.name, - range: range, - documentation: await getDocs(node) - } - suggestions.push(completion) - } else if (node.nodeType === 'FunctionDefinition') { - const completion = { - label: { label: `"${node.name}"`, description: await getNodeLink(node), detail: ` -> ${node.name} ${await getParamaters(node)}` }, - kind: this.monaco.languages.CompletionItemKind.Function, - insertText: `${node.name}${await completeParameters(node.parameters)};`, - insertTextRules: this.monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet, - range: range, - documentation: await getDocs(node) - } - suggestions.push(completion) - } else if - (node.nodeType === 'ContractDefinition') { - const completion = { - label: { label: `"${node.name}"`, description: await getNodeLink(node), detail: ` ${node.name}` }, - kind: this.monaco.languages.CompletionItemKind.Interface, - insertText: node.name, - range: range, - documentation: await getDocs(node) - } - suggestions.push(completion) - } else if - (node.nodeType === 'StructDefinition') { - const completion = { - label: { label: `"${node.name}"`, description: await getNodeLink(node), detail: ` ${node.name}` }, - kind: this.monaco.languages.CompletionItemKind.Struct, - insertText: node.name, - range: range, - documentation: await getDocs(node) - } - suggestions.push(completion) - } else if - (node.nodeType === 'EnumDefinition') { - const completion = { - label: { label: `"${node.name}"`, description: await getNodeLink(node), detail: ` ${node.name}` }, - kind: this.monaco.languages.CompletionItemKind.Enum, - insertText: node.name, - range: range, - documentation: await getDocs(node) - } - suggestions.push(completion) - } else if - (node.nodeType === 'EventDefinition') { - const completion = { - label: { label: `"${node.name}"`, description: await getNodeLink(node), detail: ` -> ${node.name} ${await getParamaters(node)}` }, - kind: this.monaco.languages.CompletionItemKind.Event, - insertText: `${node.name}${await completeParameters(node.parameters)};`, - insertTextRules: this.monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet, - range: range, - documentation: await getDocs(node) - } - suggestions.push(completion) - } else if - (node.nodeType === 'ModifierDefinition') { - const completion = { - label: { label: `"${node.name}"`, description: await getNodeLink(node), detail: ` ${node.name}` }, - kind: this.monaco.languages.CompletionItemKind.Method, - insertText: node.name, - range: range, - documentation: await getDocs(node) - } - suggestions.push(completion) - } else if - (node.nodeType === 'EnumValue' || node.type === 'EnumValue') { - const completion = { - label: { label: `"${node.name}"` }, - kind: this.monaco.languages.CompletionItemKind.EnumMember, - insertText: node.name, - range: range, - documentation: null - } - suggestions.push(completion) - - } + suggestions.push(completion) + } else if + (node.nodeType === 'ModifierDefinition') { + const completion = { + label: { label: `"${node.name}"`, description: await getNodeLink(node), detail: ` ${node.name}` }, + kind: this.monaco.languages.CompletionItemKind.Method, + insertText: node.name, + range: range, + documentation: await getDocs(node) } - - return { - suggestions + suggestions.push(completion) + } else if + (node.nodeType === 'EnumValue' || node.type === 'EnumValue') { + const completion = { + label: { label: `"${node.name}"` }, + kind: this.monaco.languages.CompletionItemKind.EnumMember, + insertText: node.name, + range: range, + documentation: null } + suggestions.push(completion) + + } } - private getContractCompletions = async () => { - let nodes: any[] = [] - const { nodesAtPosition, block } = await retrieveNodesAtPosition(this.props.editorAPI, this.props.plugin) - const fileNodes = await this.props.plugin.call('codeParser', 'getCurrentFileNodes') - // find the contract and get the nodes of the contract and the base contracts and imports - if (isArray(nodesAtPosition) && nodesAtPosition.length) { - let contractNode: any = {} - for (const node of nodesAtPosition) { - if (node.nodeType === 'ContractDefinition') { - contractNode = node - const contractNodes = fileNodes.contracts[node.name] - nodes = [...Object.values(contractNodes.contractScopeNodes), ...nodes] - nodes = [...Object.values(contractNodes.baseNodesWithBaseContractScope), ...nodes] - nodes = [...Object.values(fileNodes.imports), ...nodes] - // add the nodes at the block itself - if (block && block.name) { - const contractNodes = fileNodes.contracts[node.name].contractNodes - for (const contractNode of Object.values(contractNodes)) { - if (contractNode['name'] === block.name + return { + suggestions + } + } + + private getContractCompletions = async () => { + let nodes: any[] = [] + const { nodesAtPosition, block } = await retrieveNodesAtPosition(this.props.editorAPI, this.props.plugin) + const fileNodes = await this.props.plugin.call('codeParser', 'getCurrentFileNodes') + // find the contract and get the nodes of the contract and the base contracts and imports + if (isArray(nodesAtPosition) && nodesAtPosition.length) { + let contractNode: any = {} + for (const node of nodesAtPosition) { + if (node.nodeType === 'ContractDefinition') { + contractNode = node + const contractNodes = fileNodes.contracts[node.name] + nodes = [...Object.values(contractNodes.contractScopeNodes), ...nodes] + nodes = [...Object.values(contractNodes.baseNodesWithBaseContractScope), ...nodes] + nodes = [...Object.values(fileNodes.imports), ...nodes] + // add the nodes at the block itself + if (block && block.name) { + const contractNodes = fileNodes.contracts[node.name].contractNodes + for (const contractNode of Object.values(contractNodes)) { + if (contractNode['name'] === block.name || (contractNode['kind'] === 'constructor' && block.name === 'constructor') - ) { - let nodeOfScope = await this.props.plugin.call('codeParser', 'getNodesWithScope', (contractNode as any).id) - nodes = [...nodes, ...nodeOfScope] - if (contractNode['body']) { - nodeOfScope = await this.props.plugin.call('codeParser', 'getNodesWithScope', (contractNode['body'] as any).id) - nodes = [...nodes, ...nodeOfScope] - } - } - } - } else { // we use the block info from the nodesAtPosition - const contractNodes = fileNodes.contracts[node.name].contractNodes - for (const contractNode of Object.values(contractNodes)) { - if((contractNode as any).nodeType === 'Block'){ - const nodeOfScope = await this.props.plugin.call('codeParser', 'getNodesWithScope', (contractNode as any).id) - nodes = [...nodes, ...nodeOfScope] - } - } - } - // filter private nodes, only allow them when contract ID is the same as the current contract - nodes = nodes.filter(node => { - if (node.visibility) { - if (node.visibility === 'private') { - return (node.contractId ? node.contractId === contractNode.id : false) || false - } - } - return true - }) - break; + ) { + let nodeOfScope = await this.props.plugin.call('codeParser', 'getNodesWithScope', (contractNode as any).id) + nodes = [...nodes, ...nodeOfScope] + if (contractNode['body']) { + nodeOfScope = await this.props.plugin.call('codeParser', 'getNodesWithScope', (contractNode['body'] as any).id) + nodes = [...nodes, ...nodeOfScope] } - + } } - } else { - // get all the nodes from a simple code parser which only parses the current file - const allNodesFromAntlr = await this.props.plugin.call('codeParser', 'listAstNodes') - if (allNodesFromAntlr) { - nodes = [...nodes, ...allNodesFromAntlr] + } else { // we use the block info from the nodesAtPosition + const contractNodes = fileNodes.contracts[node.name].contractNodes + for (const contractNode of Object.values(contractNodes)) { + if((contractNode as any).nodeType === 'Block'){ + const nodeOfScope = await this.props.plugin.call('codeParser', 'getNodesWithScope', (contractNode as any).id) + nodes = [...nodes, ...nodeOfScope] + } } - } - return nodes - } - - private getThisCompletions = async () => { - let nodes: any[] = [] - let thisCompletionNodes = await this.getContractCompletions() - const allowedTypesForThisCompletion = ['VariableDeclaration', 'FunctionDefinition'] - // with this. you can't have internal nodes and no contractDefinitions - thisCompletionNodes = thisCompletionNodes.filter(node => { - if (node.visibility && (node.visibility === 'internal' || node.visibility === 'private')) { - return false - } - if (node.nodeType && !allowedTypesForThisCompletion.includes(node.nodeType)) { - return false + } + // filter private nodes, only allow them when contract ID is the same as the current contract + nodes = nodes.filter(node => { + if (node.visibility) { + if (node.visibility === 'private') { + return (node.contractId ? node.contractId === contractNode.id : false) || false + } } return true - }) - nodes = [...nodes, ...thisCompletionNodes] - return nodes - } + }) + break; + } - private getDotCompletions = async (nameOfLastTypedExpression: string, range) => { - const contractCompletions = await this.getContractCompletions() - let nodes: any[] = [] - let suggestions: monacoTypes.languages.CompletionItem[] = [] - - const filterNodes = (nodes: any[], parentNode: any, declarationOf: any = null) => { - return nodes && nodes.filter(node => { - if (node.visibility) { - if (declarationOf && declarationOf.nodeType && declarationOf.nodeType === 'StructDefinition') { - return true - } - if ((node.visibility === 'internal' && !parentNode.isBaseNode) || node.visibility === 'private') { - return false - } - } - return true - }) + } + } else { + // get all the nodes from a simple code parser which only parses the current file + const allNodesFromAntlr = await this.props.plugin.call('codeParser', 'listAstNodes') + if (allNodesFromAntlr) { + nodes = [...nodes, ...allNodesFromAntlr] + } + } + return nodes + } + + private getThisCompletions = async () => { + let nodes: any[] = [] + let thisCompletionNodes = await this.getContractCompletions() + const allowedTypesForThisCompletion = ['VariableDeclaration', 'FunctionDefinition'] + // with this. you can't have internal nodes and no contractDefinitions + thisCompletionNodes = thisCompletionNodes.filter(node => { + if (node.visibility && (node.visibility === 'internal' || node.visibility === 'private')) { + return false + } + if (node.nodeType && !allowedTypesForThisCompletion.includes(node.nodeType)) { + return false + } + return true + }) + nodes = [...nodes, ...thisCompletionNodes] + return nodes + } + + private getDotCompletions = async (nameOfLastTypedExpression: string, range) => { + const contractCompletions = await this.getContractCompletions() + let nodes: any[] = [] + let suggestions: monacoTypes.languages.CompletionItem[] = [] + + const filterNodes = (nodes: any[], parentNode: any, declarationOf: any = null) => { + return nodes && nodes.filter(node => { + if (node.visibility) { + if (declarationOf && declarationOf.nodeType && declarationOf.nodeType === 'StructDefinition') { + return true + } + if ((node.visibility === 'internal' && !parentNode.isBaseNode) || node.visibility === 'private') { + return false + } } + return true + }) + } - for (const nodeOfScope of contractCompletions) { - if (nodeOfScope.name === nameOfLastTypedExpression) { - if (nodeOfScope.typeName && nodeOfScope.typeName.nodeType === 'UserDefinedTypeName') { - const declarationOf: AstNode = await this.props.plugin.call('codeParser', 'declarationOf', nodeOfScope.typeName) - nodes = [...nodes, - ...filterNodes(declarationOf.nodes, nodeOfScope, declarationOf) + for (const nodeOfScope of contractCompletions) { + if (nodeOfScope.name === nameOfLastTypedExpression) { + if (nodeOfScope.typeName && nodeOfScope.typeName.nodeType === 'UserDefinedTypeName') { + const declarationOf: AstNode = await this.props.plugin.call('codeParser', 'declarationOf', nodeOfScope.typeName) + nodes = [...nodes, + ...filterNodes(declarationOf.nodes, nodeOfScope, declarationOf) || filterNodes(declarationOf.members, nodeOfScope, declarationOf)] - const baseContracts = await this.getlinearizedBaseContracts(declarationOf) - for (const baseContract of baseContracts) { - nodes = [...nodes, ...filterNodes(baseContract.nodes, nodeOfScope)] - } - } else if (nodeOfScope.members) { - nodes = [...nodes, ...filterNodes(nodeOfScope.members, nodeOfScope)] - } else if (nodeOfScope.typeName && nodeOfScope.typeName.nodeType === 'ArrayTypeName') { - suggestions = [...suggestions, ...getContextualAutoCompleteBTypeName('ArrayTypeName', range, this.monaco)] - } else if (nodeOfScope.typeName && nodeOfScope.typeName.nodeType === 'ElementaryTypeName' && nodeOfScope.typeName.name === 'bytes') { - suggestions = [...suggestions, ...getContextualAutoCompleteBTypeName('bytes', range, this.monaco)] - } else if (nodeOfScope.typeName && nodeOfScope.typeName.nodeType === 'ElementaryTypeName' && nodeOfScope.typeName.name === 'address') { - suggestions = [...suggestions, ...getContextualAutoCompleteBTypeName('address', range, this.monaco)] - } - } - + const baseContracts = await this.getlinearizedBaseContracts(declarationOf) + for (const baseContract of baseContracts) { + nodes = [...nodes, ...filterNodes(baseContract.nodes, nodeOfScope)] + } + } else if (nodeOfScope.members) { + nodes = [...nodes, ...filterNodes(nodeOfScope.members, nodeOfScope)] + } else if (nodeOfScope.typeName && nodeOfScope.typeName.nodeType === 'ArrayTypeName') { + suggestions = [...suggestions, ...getContextualAutoCompleteBTypeName('ArrayTypeName', range, this.monaco)] + } else if (nodeOfScope.typeName && nodeOfScope.typeName.nodeType === 'ElementaryTypeName' && nodeOfScope.typeName.name === 'bytes') { + suggestions = [...suggestions, ...getContextualAutoCompleteBTypeName('bytes', range, this.monaco)] + } else if (nodeOfScope.typeName && nodeOfScope.typeName.nodeType === 'ElementaryTypeName' && nodeOfScope.typeName.name === 'address') { + suggestions = [...suggestions, ...getContextualAutoCompleteBTypeName('address', range, this.monaco)] } + } + } - return { nodes, suggestions } - } - private getlinearizedBaseContracts = async (node: any) => { - let params = [] - if (node.linearizedBaseContracts) { - for (const id of node.linearizedBaseContracts) { - if (id !== node.id) { - const baseContract = await this.props.plugin.call('codeParser', 'getNodeById', id) - params = [...params, ...[baseContract]] - } - } + return { nodes, suggestions } + } + + private getlinearizedBaseContracts = async (node: any) => { + let params = [] + if (node.linearizedBaseContracts) { + for (const id of node.linearizedBaseContracts) { + if (id !== node.id) { + const baseContract = await this.props.plugin.call('codeParser', 'getNodeById', id) + params = [...params, ...[baseContract]] } - return params + } } + return params + } - /** + /** * * @param lineTextBeforeCursor * @returns */ - private async getLastNodeInExpression(lineTextBeforeCursor: string) { + private async getLastNodeInExpression(lineTextBeforeCursor: string) { - const wrapLineInFunction = async (text: string) => { - return `function() { + const wrapLineInFunction = async (text: string) => { + return `function() { ${text} }` - } + } - let lastNodeInExpression + let lastNodeInExpression - const linesToCheck = + const linesToCheck = [ - lineTextBeforeCursor.substring(0, lineTextBeforeCursor.lastIndexOf('.')) + ".lastnode;", - lineTextBeforeCursor.substring(0, lineTextBeforeCursor.lastIndexOf('.')) + ".lastnode;}", - lineTextBeforeCursor.substring(0, lineTextBeforeCursor.lastIndexOf('.')) + ".lastnode);", - await wrapLineInFunction(lineTextBeforeCursor.substring(0, lineTextBeforeCursor.lastIndexOf('.')) + ".lastnode;"), - await wrapLineInFunction(lineTextBeforeCursor.substring(0, lineTextBeforeCursor.lastIndexOf('.')) + ".lastnode;}"), - await wrapLineInFunction(lineTextBeforeCursor.substring(0, lineTextBeforeCursor.lastIndexOf('.')) + ".lastnode;)"), - await wrapLineInFunction(lineTextBeforeCursor.substring(0, lineTextBeforeCursor.lastIndexOf('.')) + ".lastnode)"), - await wrapLineInFunction(lineTextBeforeCursor.substring(0, lineTextBeforeCursor.lastIndexOf('.')) + ".lastnode);"), + lineTextBeforeCursor.substring(0, lineTextBeforeCursor.lastIndexOf('.')) + ".lastnode;", + lineTextBeforeCursor.substring(0, lineTextBeforeCursor.lastIndexOf('.')) + ".lastnode;}", + lineTextBeforeCursor.substring(0, lineTextBeforeCursor.lastIndexOf('.')) + ".lastnode);", + await wrapLineInFunction(lineTextBeforeCursor.substring(0, lineTextBeforeCursor.lastIndexOf('.')) + ".lastnode;"), + await wrapLineInFunction(lineTextBeforeCursor.substring(0, lineTextBeforeCursor.lastIndexOf('.')) + ".lastnode;}"), + await wrapLineInFunction(lineTextBeforeCursor.substring(0, lineTextBeforeCursor.lastIndexOf('.')) + ".lastnode;)"), + await wrapLineInFunction(lineTextBeforeCursor.substring(0, lineTextBeforeCursor.lastIndexOf('.')) + ".lastnode)"), + await wrapLineInFunction(lineTextBeforeCursor.substring(0, lineTextBeforeCursor.lastIndexOf('.')) + ".lastnode);"), ] - for (const line of linesToCheck) { - try { - const lineAst = await this.props.plugin.call('codeParser', 'parseSolidity', line) - const lastNode = await this.props.plugin.call('codeParser', 'getLastNodeInLine', lineAst) - if (lastNode) { - lastNodeInExpression = lastNode - break - } + for (const line of linesToCheck) { + try { + const lineAst = await this.props.plugin.call('codeParser', 'parseSolidity', line) + const lastNode = await this.props.plugin.call('codeParser', 'getLastNodeInLine', lineAst) + if (lastNode) { + lastNodeInExpression = lastNode + break + } - } catch (e) { + } catch (e) { - } - } - return lastNodeInExpression + } } + return lastNodeInExpression + } } \ No newline at end of file diff --git a/libs/remix-ui/editor/src/lib/providers/definitionProvider.ts b/libs/remix-ui/editor/src/lib/providers/definitionProvider.ts index 247a215eab..aef31b98fb 100644 --- a/libs/remix-ui/editor/src/lib/providers/definitionProvider.ts +++ b/libs/remix-ui/editor/src/lib/providers/definitionProvider.ts @@ -3,86 +3,86 @@ import monaco from "../../types/monaco" import { EditorUIProps } from "../remix-ui-editor" export class RemixDefinitionProvider implements monaco.languages.DefinitionProvider { - props: EditorUIProps - monaco: Monaco - constructor(props: any, monaco: any) { - this.props = props - this.monaco = monaco - } + props: EditorUIProps + monaco: Monaco + constructor(props: any, monaco: any) { + this.props = props + this.monaco = monaco + } - // eslint-disable-next-line @typescript-eslint/no-unused-vars - async provideDefinition(model: monaco.editor.ITextModel, position: monaco.Position, token: monaco.CancellationToken): Promise { - const cursorPosition = this.props.editorAPI.getCursorPosition() - let jumpLocation = await this.jumpToDefinition(cursorPosition) - if (!jumpLocation || !jumpLocation.fileName) { - const line = model.getLineContent(position.lineNumber) - const lastpart = line.substring(0, position.column - 1).split(';').pop() - if (lastpart.startsWith('import')) { - const importPath = line.substring(lastpart.indexOf('"') + 1) - const importPath2 = importPath.substring(0, importPath.indexOf('"')) - jumpLocation = { - startLineNumber: 1, - startColumn: 1, - endColumn: 1, - endLineNumber: 1, - fileName: importPath2 - } - } + // eslint-disable-next-line @typescript-eslint/no-unused-vars + async provideDefinition(model: monaco.editor.ITextModel, position: monaco.Position, token: monaco.CancellationToken): Promise { + const cursorPosition = this.props.editorAPI.getCursorPosition() + let jumpLocation = await this.jumpToDefinition(cursorPosition) + if (!jumpLocation || !jumpLocation.fileName) { + const line = model.getLineContent(position.lineNumber) + const lastpart = line.substring(0, position.column - 1).split(';').pop() + if (lastpart.startsWith('import')) { + const importPath = line.substring(lastpart.indexOf('"') + 1) + const importPath2 = importPath.substring(0, importPath.indexOf('"')) + jumpLocation = { + startLineNumber: 1, + startColumn: 1, + endColumn: 1, + endLineNumber: 1, + fileName: importPath2 } - if (jumpLocation && jumpLocation.fileName) { - return [{ - uri: this.monaco.Uri.parse(jumpLocation.fileName), - range: { - startLineNumber: jumpLocation.startLineNumber, - startColumn: jumpLocation.startColumn, - endLineNumber: jumpLocation.endLineNumber, - endColumn: jumpLocation.endColumn - } - }] + } + } + if (jumpLocation && jumpLocation.fileName) { + return [{ + uri: this.monaco.Uri.parse(jumpLocation.fileName), + range: { + startLineNumber: jumpLocation.startLineNumber, + startColumn: jumpLocation.startColumn, + endLineNumber: jumpLocation.endLineNumber, + endColumn: jumpLocation.endColumn } - return [] + }] } + return [] + } - async jumpToDefinition(position: any) { - const node = await this.props.plugin.call('codeParser', 'definitionAtPosition', position) - const sourcePosition = await this.props.plugin.call('codeParser', 'positionOfDefinition', node) - if (sourcePosition) { - return await this.jumpToPosition(sourcePosition) - } + async jumpToDefinition(position: any) { + const node = await this.props.plugin.call('codeParser', 'definitionAtPosition', position) + const sourcePosition = await this.props.plugin.call('codeParser', 'positionOfDefinition', node) + if (sourcePosition) { + return await this.jumpToPosition(sourcePosition) } + } - /* + /* * onClick jump to position of ast node in the editor */ - async jumpToPosition(position: any) { - const jumpToLine = async (fileName: string, lineColumn: any) => { - const fileTarget = await this.props.plugin.call('fileManager', 'getPathFromUrl', fileName) - if (fileName !== await this.props.plugin.call('fileManager', 'file')) { - await this.props.plugin.call('contentImport', 'resolveAndSave', fileName, null) - const fileContent = await this.props.plugin.call('fileManager', 'readFile', fileName) - try { - await this.props.plugin.call('editor', 'addModel', fileTarget.file, fileContent) - } catch (e) { + async jumpToPosition(position: any) { + const jumpToLine = async (fileName: string, lineColumn: any) => { + const fileTarget = await this.props.plugin.call('fileManager', 'getPathFromUrl', fileName) + if (fileName !== await this.props.plugin.call('fileManager', 'file')) { + await this.props.plugin.call('contentImport', 'resolveAndSave', fileName, null) + const fileContent = await this.props.plugin.call('fileManager', 'readFile', fileName) + try { + await this.props.plugin.call('editor', 'addModel', fileTarget.file, fileContent) + } catch (e) { - } - } - if (lineColumn.start && lineColumn.start.line >= 0 && lineColumn.start.column >= 0) { - const pos = { - startLineNumber: lineColumn.start.line + 1, - startColumn: lineColumn.start.column + 1, - endColumn: lineColumn.end.column + 1, - endLineNumber: lineColumn.end.line + 1, - fileName: (fileTarget && fileTarget.file) || fileName - } - return pos - } } - const lastCompilationResult = await this.props.plugin.call('codeParser', 'getLastCompilationResult') // await this.props.plugin.call('compilerArtefacts', 'getLastCompilationResult') - if (lastCompilationResult && lastCompilationResult.languageversion.indexOf('soljson') === 0 && lastCompilationResult.data) { - - const lineColumn = await this.props.plugin.call('codeParser', 'getLineColumnOfPosition', position) - const filename = lastCompilationResult.getSourceName(position.file) - return await jumpToLine(filename, lineColumn) + } + if (lineColumn.start && lineColumn.start.line >= 0 && lineColumn.start.column >= 0) { + const pos = { + startLineNumber: lineColumn.start.line + 1, + startColumn: lineColumn.start.column + 1, + endColumn: lineColumn.end.column + 1, + endLineNumber: lineColumn.end.line + 1, + fileName: (fileTarget && fileTarget.file) || fileName } + return pos + } + } + const lastCompilationResult = await this.props.plugin.call('codeParser', 'getLastCompilationResult') // await this.props.plugin.call('compilerArtefacts', 'getLastCompilationResult') + if (lastCompilationResult && lastCompilationResult.languageversion.indexOf('soljson') === 0 && lastCompilationResult.data) { + + const lineColumn = await this.props.plugin.call('codeParser', 'getLineColumnOfPosition', position) + const filename = lastCompilationResult.getSourceName(position.file) + return await jumpToLine(filename, lineColumn) } + } } \ No newline at end of file diff --git a/libs/remix-ui/editor/src/lib/providers/highlightProvider.ts b/libs/remix-ui/editor/src/lib/providers/highlightProvider.ts index b1f3305df6..7ae209b7e2 100644 --- a/libs/remix-ui/editor/src/lib/providers/highlightProvider.ts +++ b/libs/remix-ui/editor/src/lib/providers/highlightProvider.ts @@ -4,35 +4,35 @@ import monaco from "../../types/monaco" import { EditorUIProps } from "../remix-ui-editor" export class RemixHighLightProvider implements monaco.languages.DocumentHighlightProvider { - props: EditorUIProps - monaco: Monaco - constructor(props: any, monaco: any) { - this.props = props - this.monaco = monaco - } + props: EditorUIProps + monaco: Monaco + constructor(props: any, monaco: any) { + this.props = props + this.monaco = monaco + } - // eslint-disable-next-line @typescript-eslint/no-unused-vars - async provideDocumentHighlights(model: monaco.editor.ITextModel, position: monaco.Position, token: monaco.CancellationToken): Promise { - const cursorPosition = this.props.editorAPI.getCursorPosition() - const nodes = await this.props.plugin.call('codeParser', 'referrencesAtPosition', cursorPosition) - const highlights: monaco.languages.DocumentHighlight[] = [] - if (nodes && nodes.length) { - const compilationResult = await this.props.plugin.call('codeParser', 'getLastCompilationResult') - const file = await this.props.plugin.call('fileManager', 'file') - if (compilationResult && compilationResult.data && compilationResult.data.sources[file]) { - for (const node of nodes) { - const position = sourceMappingDecoder.decode(node.src) - const fileInNode = compilationResult.getSourceName(position.file) - if (fileInNode === file) { - const lineColumn = await this.props.plugin.call('codeParser', 'getLineColumnOfPosition', position) - const range = new this.monaco.Range(lineColumn.start.line + 1, lineColumn.start.column + 1, lineColumn.end.line + 1, lineColumn.end.column + 1) - highlights.push({ - range, - }) - } - } - } + // eslint-disable-next-line @typescript-eslint/no-unused-vars + async provideDocumentHighlights(model: monaco.editor.ITextModel, position: monaco.Position, token: monaco.CancellationToken): Promise { + const cursorPosition = this.props.editorAPI.getCursorPosition() + const nodes = await this.props.plugin.call('codeParser', 'referrencesAtPosition', cursorPosition) + const highlights: monaco.languages.DocumentHighlight[] = [] + if (nodes && nodes.length) { + const compilationResult = await this.props.plugin.call('codeParser', 'getLastCompilationResult') + const file = await this.props.plugin.call('fileManager', 'file') + if (compilationResult && compilationResult.data && compilationResult.data.sources[file]) { + for (const node of nodes) { + const position = sourceMappingDecoder.decode(node.src) + const fileInNode = compilationResult.getSourceName(position.file) + if (fileInNode === file) { + const lineColumn = await this.props.plugin.call('codeParser', 'getLineColumnOfPosition', position) + const range = new this.monaco.Range(lineColumn.start.line + 1, lineColumn.start.column + 1, lineColumn.end.line + 1, lineColumn.end.column + 1) + highlights.push({ + range, + }) + } } - return highlights + } } + return highlights + } } \ No newline at end of file diff --git a/libs/remix-ui/editor/src/lib/providers/hoverProvider.ts b/libs/remix-ui/editor/src/lib/providers/hoverProvider.ts index 035eca6f4f..67bce2b268 100644 --- a/libs/remix-ui/editor/src/lib/providers/hoverProvider.ts +++ b/libs/remix-ui/editor/src/lib/providers/hoverProvider.ts @@ -4,203 +4,203 @@ import { EditorUIProps } from '../remix-ui-editor' import { monacoTypes } from '@remix-ui/editor'; export class RemixHoverProvider implements monacoTypes.languages.HoverProvider { - props: EditorUIProps - monaco: Monaco - constructor(props: any, monaco: any) { - this.props = props - this.monaco = monaco + props: EditorUIProps + monaco: Monaco + constructor(props: any, monaco: any) { + this.props = props + this.monaco = monaco + } + + provideHover = async function (model: monacoTypes.editor.ITextModel, position: monacoTypes.Position): Promise { + const cursorPosition = this.props.editorAPI.getHoverPosition(position) + const nodeAtPosition = await this.props.plugin.call('codeParser', 'definitionAtPosition', cursorPosition) + const contents = [] + + const getDocs = async (node: any) => { + contents.push({ + value: await this.props.plugin.call('codeParser', 'getNodeDocumentation', node) + }) } - provideHover = async function (model: monacoTypes.editor.ITextModel, position: monacoTypes.Position): Promise { - const cursorPosition = this.props.editorAPI.getHoverPosition(position) - const nodeAtPosition = await this.props.plugin.call('codeParser', 'definitionAtPosition', cursorPosition) - const contents = [] + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const getScope = async (node: any) => { + if (node.id) { + contents.push({ + value: `id: ${node.id}` + }) + } + if (node.scope) { + contents.push({ + value: `scope: ${node.scope}` + }) + } + } - const getDocs = async (node: any) => { - contents.push({ - value: await this.props.plugin.call('codeParser', 'getNodeDocumentation', node) - }) - } + const getLinks = async (node: any) => { + contents.push({ + value: await this.props.plugin.call('codeParser', 'getNodeLink', node) + }) + } - // eslint-disable-next-line @typescript-eslint/no-unused-vars - const getScope = async (node: any) => { - if (node.id) { - contents.push({ - value: `id: ${node.id}` - }) - } - if (node.scope) { - contents.push({ - value: `scope: ${node.scope}` - }) - } - } + const getVariableDeclaration = async (node: any) => { + return await this.props.plugin.call('codeParser', 'getVariableDeclaration', node) + } - const getLinks = async (node: any) => { - contents.push({ - value: await this.props.plugin.call('codeParser', 'getNodeLink', node) - }) - } - const getVariableDeclaration = async (node: any) => { - return await this.props.plugin.call('codeParser', 'getVariableDeclaration', node) - } + const getParamaters = async (node: any) => { + return await this.props.plugin.call('codeParser', 'getFunctionParamaters', node) + } + + const getReturnParameters = async (node: any) => { + return await this.props.plugin.call('codeParser', 'getFunctionReturnParameters', node) + } - const getParamaters = async (node: any) => { - return await this.props.plugin.call('codeParser', 'getFunctionParamaters', node) + const getOverrides = async (node: any) => { + if (node.overrides) { + const overrides = [] + for (const override of node.overrides.overrides) { + overrides.push(override.name) } + if (overrides.length) + return ` overrides (${overrides.join(', ')})` + } + return '' + } - const getReturnParameters = async (node: any) => { - return await this.props.plugin.call('codeParser', 'getFunctionReturnParameters', node) + const getlinearizedBaseContracts = async (node: any) => { + const params = [] + if (node.linearizedBaseContracts) { + for (const id of node.linearizedBaseContracts) { + const baseContract = await this.props.plugin.call('codeParser', 'getNodeById', id) + params.push( + baseContract.name + ) } + if (params.length) + return `is ${params.join(', ')}` + } + return '' + } - - const getOverrides = async (node: any) => { - if (node.overrides) { - const overrides = [] - for (const override of node.overrides.overrides) { - overrides.push(override.name) - } - if (overrides.length) - return ` overrides (${overrides.join(', ')})` - } - return '' + if (nodeAtPosition) { + if (nodeAtPosition.absolutePath) { + const target = await this.props.plugin.call('fileManager', 'getPathFromUrl', nodeAtPosition.absolutePath) + if (target.file !== nodeAtPosition.absolutePath) { + contents.push({ + value: `${target.file}` + }) } - - const getlinearizedBaseContracts = async (node: any) => { - const params = [] - if (node.linearizedBaseContracts) { - for (const id of node.linearizedBaseContracts) { - const baseContract = await this.props.plugin.call('codeParser', 'getNodeById', id) - params.push( - baseContract.name - ) - } - if (params.length) - return `is ${params.join(', ')}` - } - return '' + contents.push({ + value: `${nodeAtPosition.absolutePath}` + }) + } + if (nodeAtPosition.nodeType === 'VariableDeclaration') { + contents.push({ + value: await getVariableDeclaration(nodeAtPosition) + }) + + } + else if (nodeAtPosition.nodeType === 'ElementaryTypeName') { + contents.push({ + value: `${nodeAtPosition.typeDescriptions.typeString}` + }) + + } else if (nodeAtPosition.nodeType === 'FunctionDefinition') { + if (!nodeAtPosition.name) return + const returns = await getReturnParameters(nodeAtPosition) + contents.push({ + value: `function ${nodeAtPosition.name} ${await getParamaters(nodeAtPosition)} ${nodeAtPosition.visibility} ${nodeAtPosition.stateMutability}${await getOverrides(nodeAtPosition)} ${returns ? `returns ${returns}` : ''}` + }) + + } else if (nodeAtPosition.nodeType === 'ModifierDefinition') { + contents.push({ + value: `modifier ${nodeAtPosition.name} ${await getParamaters(nodeAtPosition)}` + }) + } else if (nodeAtPosition.nodeType === 'EventDefinition') { + contents.push({ + value: `modifier ${nodeAtPosition.name} ${await getParamaters(nodeAtPosition)}` + }) + } else if (nodeAtPosition.nodeType === 'ContractDefinition') { + contents.push({ + value: `${nodeAtPosition.contractKind || nodeAtPosition.kind} ${nodeAtPosition.name} ${await getlinearizedBaseContracts(nodeAtPosition)}` + }) + + } else if (nodeAtPosition.nodeType === 'InvalidNode') { + contents.push({ + value: `There are errors in the code.` + }) + } else if (nodeAtPosition.nodeType === 'Block') { + + } else { + contents.push({ + value: `${nodeAtPosition.nodeType}` + }) + + } + + for (const key in contents) { + contents[key].value = '```remix-solidity\n' + contents[key].value + '\n```' + } + getLinks(nodeAtPosition) + getDocs(nodeAtPosition) + // getScope(nodeAtPosition) + + try { + if (nodeAtPosition?.name === 'msg') { + const global = await this.props.plugin.call('debugger', 'globalContext') + if (global !== null && global[nodeAtPosition?.name]) { + contents.push({ + value: `GLOBAL VARIABLE ${nodeAtPosition.name}: ${JSON.stringify(global[nodeAtPosition?.name], null, '\t')}` + }) + } } + } catch (e) {} - if (nodeAtPosition) { - if (nodeAtPosition.absolutePath) { - const target = await this.props.plugin.call('fileManager', 'getPathFromUrl', nodeAtPosition.absolutePath) - if (target.file !== nodeAtPosition.absolutePath) { - contents.push({ - value: `${target.file}` - }) - } - contents.push({ - value: `${nodeAtPosition.absolutePath}` - }) - } - if (nodeAtPosition.nodeType === 'VariableDeclaration') { - contents.push({ - value: await getVariableDeclaration(nodeAtPosition) - }) - - } - else if (nodeAtPosition.nodeType === 'ElementaryTypeName') { - contents.push({ - value: `${nodeAtPosition.typeDescriptions.typeString}` - }) - - } else if (nodeAtPosition.nodeType === 'FunctionDefinition') { - if (!nodeAtPosition.name) return - const returns = await getReturnParameters(nodeAtPosition) - contents.push({ - value: `function ${nodeAtPosition.name} ${await getParamaters(nodeAtPosition)} ${nodeAtPosition.visibility} ${nodeAtPosition.stateMutability}${await getOverrides(nodeAtPosition)} ${returns ? `returns ${returns}` : ''}` - }) - - } else if (nodeAtPosition.nodeType === 'ModifierDefinition') { - contents.push({ - value: `modifier ${nodeAtPosition.name} ${await getParamaters(nodeAtPosition)}` - }) - } else if (nodeAtPosition.nodeType === 'EventDefinition') { - contents.push({ - value: `modifier ${nodeAtPosition.name} ${await getParamaters(nodeAtPosition)}` - }) - } else if (nodeAtPosition.nodeType === 'ContractDefinition') { - contents.push({ - value: `${nodeAtPosition.contractKind || nodeAtPosition.kind} ${nodeAtPosition.name} ${await getlinearizedBaseContracts(nodeAtPosition)}` - }) - - } else if (nodeAtPosition.nodeType === 'InvalidNode') { - contents.push({ - value: `There are errors in the code.` - }) - } else if (nodeAtPosition.nodeType === 'Block') { - - } else { - contents.push({ - value: `${nodeAtPosition.nodeType}` - }) - - } - - for (const key in contents) { - contents[key].value = '```remix-solidity\n' + contents[key].value + '\n```' - } - getLinks(nodeAtPosition) - getDocs(nodeAtPosition) - // getScope(nodeAtPosition) - - try { - if (nodeAtPosition?.name === 'msg') { - const global = await this.props.plugin.call('debugger', 'globalContext') - if (global !== null && global[nodeAtPosition?.name]) { - contents.push({ - value: `GLOBAL VARIABLE ${nodeAtPosition.name}: ${JSON.stringify(global[nodeAtPosition?.name], null, '\t')}` - }) - } - } - } catch (e) {} - - try { - if (nodeAtPosition?.expression?.name === 'msg' && nodeAtPosition?.memberName) { - const global = await this.props.plugin.call('debugger', 'globalContext') - if (global !== null && global[nodeAtPosition?.expression?.name][nodeAtPosition.memberName] && global[nodeAtPosition?.expression?.name][nodeAtPosition.memberName]) { - contents.push({ - value: `GLOBAL VARIABLE msg.${nodeAtPosition.memberName}: ${global[nodeAtPosition?.expression?.name][nodeAtPosition.memberName]}` - }) - } - } - } catch (e) {} - - try { - const decodedVar = await this.props.plugin.call('debugger', 'decodeLocalVariable', nodeAtPosition.id) - if (decodedVar !== null && decodedVar.type) { - contents.push({ - value: `LOCAL VARIABLE ${nodeAtPosition.name}: ${typeof(decodedVar.value) === 'string' ? decodedVar.value : JSON.stringify(decodedVar.value, null, '\t')}` - }) - } - } catch (e) {} - - try { - const decodedVar = await this.props.plugin.call('debugger', 'decodeStateVariable', nodeAtPosition.id) - if (decodedVar !== null && decodedVar.type) { - contents.push({ - value: `STATE VARIABLE ${nodeAtPosition.name}: ${typeof(decodedVar.value) === 'string' ? decodedVar.value : JSON.stringify(decodedVar.value, null, '\t')}` - }) - } - } catch (e) {} + try { + if (nodeAtPosition?.expression?.name === 'msg' && nodeAtPosition?.memberName) { + const global = await this.props.plugin.call('debugger', 'globalContext') + if (global !== null && global[nodeAtPosition?.expression?.name][nodeAtPosition.memberName] && global[nodeAtPosition?.expression?.name][nodeAtPosition.memberName]) { + contents.push({ + value: `GLOBAL VARIABLE msg.${nodeAtPosition.memberName}: ${global[nodeAtPosition?.expression?.name][nodeAtPosition.memberName]}` + }) + } } - - setTimeout(() => { - // eslint-disable-next-line no-debugger - // debugger - },1000) - - return { - range: new this.monaco.Range( - position.lineNumber, - position.column, - position.lineNumber, - model.getLineMaxColumn(position.lineNumber) - ), - contents: contents - }; + } catch (e) {} + + try { + const decodedVar = await this.props.plugin.call('debugger', 'decodeLocalVariable', nodeAtPosition.id) + if (decodedVar !== null && decodedVar.type) { + contents.push({ + value: `LOCAL VARIABLE ${nodeAtPosition.name}: ${typeof(decodedVar.value) === 'string' ? decodedVar.value : JSON.stringify(decodedVar.value, null, '\t')}` + }) + } + } catch (e) {} + + try { + const decodedVar = await this.props.plugin.call('debugger', 'decodeStateVariable', nodeAtPosition.id) + if (decodedVar !== null && decodedVar.type) { + contents.push({ + value: `STATE VARIABLE ${nodeAtPosition.name}: ${typeof(decodedVar.value) === 'string' ? decodedVar.value : JSON.stringify(decodedVar.value, null, '\t')}` + }) + } + } catch (e) {} } + setTimeout(() => { + // eslint-disable-next-line no-debugger + // debugger + },1000) + + return { + range: new this.monaco.Range( + position.lineNumber, + position.column, + position.lineNumber, + model.getLineMaxColumn(position.lineNumber) + ), + contents: contents + }; + } + } \ No newline at end of file diff --git a/libs/remix-ui/editor/src/lib/providers/referenceProvider.ts b/libs/remix-ui/editor/src/lib/providers/referenceProvider.ts index badee820e5..e7d32d12ef 100644 --- a/libs/remix-ui/editor/src/lib/providers/referenceProvider.ts +++ b/libs/remix-ui/editor/src/lib/providers/referenceProvider.ts @@ -4,44 +4,44 @@ import monaco from "../../types/monaco" import { EditorUIProps } from "../remix-ui-editor" export class RemixReferenceProvider implements monaco.languages.ReferenceProvider { - props: EditorUIProps - monaco: Monaco - constructor(props: any, monaco: any) { - this.props = props - this.monaco = monaco - } + props: EditorUIProps + monaco: Monaco + constructor(props: any, monaco: any) { + this.props = props + this.monaco = monaco + } - // eslint-disable-next-line @typescript-eslint/no-unused-vars - async provideReferences(model: monaco.editor.ITextModel, position: monaco.Position, context: monaco.languages.ReferenceContext, token: monaco.CancellationToken) { + // eslint-disable-next-line @typescript-eslint/no-unused-vars + async provideReferences(model: monaco.editor.ITextModel, position: monaco.Position, context: monaco.languages.ReferenceContext, token: monaco.CancellationToken) { - const cursorPosition = this.props.editorAPI.getCursorPosition() - const nodes = await this.props.plugin.call('codeParser', 'referrencesAtPosition', cursorPosition) - const references = [] - if (nodes && nodes.length) { - const compilationResult = await this.props.plugin.call('codeParser', 'getLastCompilationResult') - const file = await this.props.plugin.call('fileManager', 'file') - if (compilationResult && compilationResult.data && compilationResult.data.sources[file]) { - for (const node of nodes) { - const position = sourceMappingDecoder.decode(node.src) - const fileInNode = compilationResult.getSourceName(position.file) - let fileTarget = await this.props.plugin.call('fileManager', 'getPathFromUrl', fileInNode) - fileTarget = fileTarget.file - const fileContent = await this.props.plugin.call('fileManager', 'readFile', fileInNode) - const lineColumn = await this.props.plugin.call('codeParser', 'getLineColumnOfPosition', position) + const cursorPosition = this.props.editorAPI.getCursorPosition() + const nodes = await this.props.plugin.call('codeParser', 'referrencesAtPosition', cursorPosition) + const references = [] + if (nodes && nodes.length) { + const compilationResult = await this.props.plugin.call('codeParser', 'getLastCompilationResult') + const file = await this.props.plugin.call('fileManager', 'file') + if (compilationResult && compilationResult.data && compilationResult.data.sources[file]) { + for (const node of nodes) { + const position = sourceMappingDecoder.decode(node.src) + const fileInNode = compilationResult.getSourceName(position.file) + let fileTarget = await this.props.plugin.call('fileManager', 'getPathFromUrl', fileInNode) + fileTarget = fileTarget.file + const fileContent = await this.props.plugin.call('fileManager', 'readFile', fileInNode) + const lineColumn = await this.props.plugin.call('codeParser', 'getLineColumnOfPosition', position) - try { - this.props.plugin.call('editor', 'addModel', fileTarget, fileContent) - } catch (e) { + try { + this.props.plugin.call('editor', 'addModel', fileTarget, fileContent) + } catch (e) { - } - const range = new this.monaco.Range(lineColumn.start.line + 1, lineColumn.start.column + 1, lineColumn.end.line + 1, lineColumn.end.column + 1) - references.push({ - range, - uri: this.monaco.Uri.parse(fileTarget) - }) - } - } + } + const range = new this.monaco.Range(lineColumn.start.line + 1, lineColumn.start.column + 1, lineColumn.end.line + 1, lineColumn.end.column + 1) + references.push({ + range, + uri: this.monaco.Uri.parse(fileTarget) + }) } - return references + } } + return references + } } \ No newline at end of file diff --git a/libs/remix-ui/editor/src/lib/remix-ui-editor.tsx b/libs/remix-ui/editor/src/lib/remix-ui-editor.tsx index f88cdeb9ee..d1f660ebb3 100644 --- a/libs/remix-ui/editor/src/lib/remix-ui-editor.tsx +++ b/libs/remix-ui/editor/src/lib/remix-ui-editor.tsx @@ -126,8 +126,8 @@ export interface EditorUIProps { editorAPI: EditorAPIType } export const EditorUI = (props: EditorUIProps) => { - const [, setCurrentBreakpoints] = useState({}) - const defaultEditorValue = ` + const [, setCurrentBreakpoints] = useState({}) + const defaultEditorValue = ` \t\t\t\t\t\t\t ____ _____ __ __ ___ __ __ ___ ____ _____ \t\t\t\t\t\t\t| _ \\ | ____| | \\/ | |_ _| \\ \\/ / |_ _| | _ \\ | ____| \t\t\t\t\t\t\t| |_) | | _| | |\\/| | | | \\ / | | | | | | | _| @@ -148,629 +148,629 @@ export const EditorUI = (props: EditorUIProps) => { \t\t\t\t\t\t\t\tMedium: https://medium.com/remix-ide\n \t\t\t\t\t\t\t\tTwitter: https://twitter.com/ethereumremix\n ` - const pasteCodeRef = useRef(false) - const editorRef = useRef(null) - const monacoRef = useRef(null) - const currentFileRef = useRef('') - const currentUrlRef = useRef('') - // const currentDecorations = useRef({ sourceAnnotationsPerFile: {}, markerPerFile: {} }) // decorations that are currently in use by the editor - // const registeredDecorations = useRef({}) // registered decorations - - const [editorModelsState, dispatch] = useReducer(reducerActions, initialState) - - const formatColor = (name) => { - let color = window.getComputedStyle(document.documentElement).getPropertyValue(name).trim() - if (color.length === 4) { - color = color.concat(color.substr(1)) - } - return color - } - const defineAndSetTheme = (monaco) => { - const themeType = props.themeType === 'dark' ? 'vs-dark' : 'vs' - const themeName = props.themeType === 'dark' ? 'remix-dark' : 'remix-light' - // see https://microsoft.github.io/monaco-editor/playground.html#customizing-the-appearence-exposed-colors - const lightColor = formatColor('--light') - const infoColor = formatColor('--info') - const darkColor = formatColor('--dark') - const secondaryColor = formatColor('--secondary') - const primaryColor = formatColor('--primary') - const textColor = formatColor('--text') || darkColor - const textbackground = formatColor('--text-background') || lightColor - - const blueColor = formatColor('--blue') - const successColor = formatColor('--success') - const warningColor = formatColor('--warning') - const yellowColor = formatColor('--yellow') - const pinkColor = formatColor('--pink') - const locationColor = '#9e7e08' - // const purpleColor = formatColor('--purple') - const dangerColor = formatColor('--danger') - const greenColor = formatColor('--green') - const orangeColor = formatColor('--orange') - const grayColor = formatColor('--gray') - - monaco.editor.defineTheme(themeName, { - base: themeType, - inherit: true, // can also be false to completely replace the builtin rules - rules: [ - { background: darkColor.replace('#', '') }, - { foreground: textColor.replace('#', '') }, - - // global variables - { token: 'keyword.abi', foreground: blueColor }, - { token: 'keyword.block', foreground: blueColor }, - { token: 'keyword.bytes', foreground: blueColor }, - { token: 'keyword.msg', foreground: blueColor }, - { token: 'keyword.tx', foreground: blueColor }, - - // global functions - { token: 'keyword.assert', foreground: blueColor }, - { token: 'keyword.require', foreground: blueColor }, - { token: 'keyword.revert', foreground: blueColor }, - { token: 'keyword.blockhash', foreground: blueColor }, - { token: 'keyword.keccak256', foreground: blueColor }, - { token: 'keyword.sha256', foreground: blueColor }, - { token: 'keyword.ripemd160', foreground: blueColor }, - { token: 'keyword.ecrecover', foreground: blueColor }, - { token: 'keyword.addmod', foreground: blueColor }, - { token: 'keyword.mulmod', foreground: blueColor }, - { token: 'keyword.selfdestruct', foreground: blueColor }, - { token: 'keyword.type ', foreground: blueColor }, - { token: 'keyword.gasleft', foreground: blueColor }, - - // specials - { token: 'keyword.super', foreground: infoColor }, - { token: 'keyword.this', foreground: infoColor }, - { token: 'keyword.virtual', foreground: infoColor }, - - // for state variables - { token: 'keyword.constants', foreground: grayColor }, - { token: 'keyword.override', foreground: grayColor }, - { token: 'keyword.immutable', foreground: grayColor }, - - // data location - { token: 'keyword.memory', foreground: locationColor }, - { token: 'keyword.storage', foreground: locationColor }, - { token: 'keyword.calldata', foreground: locationColor }, - - // for Events - { token: 'keyword.indexed', foreground: yellowColor }, - { token: 'keyword.anonymous', foreground: yellowColor }, - - // for functions - { token: 'keyword.external', foreground: successColor }, - { token: 'keyword.internal', foreground: successColor }, - { token: 'keyword.private', foreground: successColor }, - { token: 'keyword.public', foreground: successColor }, - { token: 'keyword.view', foreground: successColor }, - { token: 'keyword.pure', foreground: successColor }, - { token: 'keyword.payable', foreground: successColor }, - { token: 'keyword.nonpayable', foreground: successColor }, - - // Errors - { token: 'keyword.Error', foreground: dangerColor }, - { token: 'keyword.Panic', foreground: dangerColor }, - - // special functions - { token: 'keyword.fallback', foreground: pinkColor }, - { token: 'keyword.receive', foreground: pinkColor }, - { token: 'keyword.constructor', foreground: pinkColor }, - - // identifiers - { token: 'keyword.identifier', foreground: warningColor }, - { token: 'keyword.for', foreground: warningColor }, - { token: 'keyword.break', foreground: warningColor }, - { token: 'keyword.continue', foreground: warningColor }, - { token: 'keyword.while', foreground: warningColor }, - { token: 'keyword.do', foreground: warningColor }, - { token: 'keyword.delete', foreground: warningColor }, - - { token: 'keyword.if', foreground: yellowColor }, - { token: 'keyword.else', foreground: yellowColor }, - - { token: 'keyword.throw', foreground: orangeColor }, - { token: 'keyword.catch', foreground: orangeColor }, - { token: 'keyword.try', foreground: orangeColor }, - - // returns - { token: 'keyword.returns', foreground: greenColor }, - { token: 'keyword.return', foreground: greenColor } - - ], - colors: { - // see https://code.visualstudio.com/api/references/theme-color for more settings - 'editor.background': textbackground, - 'editorSuggestWidget.background': lightColor, - 'editorSuggestWidget.selectedBackground': secondaryColor, - 'editorSuggestWidget.selectedForeground': textColor, - 'editorSuggestWidget.highlightForeground': primaryColor, - 'editorSuggestWidget.focusHighlightForeground': infoColor, - 'editor.lineHighlightBorder': secondaryColor, - 'editor.lineHighlightBackground': textbackground === darkColor ? lightColor : secondaryColor, - 'editorGutter.background': lightColor, - //'editor.selectionHighlightBackground': secondaryColor, - 'minimap.background': lightColor, - 'menu.foreground': textColor, - 'menu.background': textbackground, - 'menu.selectionBackground': secondaryColor, - 'menu.selectionForeground': textColor, - 'menu.selectionBorder': secondaryColor - } - }) - monacoRef.current.editor.setTheme(themeName) + const pasteCodeRef = useRef(false) + const editorRef = useRef(null) + const monacoRef = useRef(null) + const currentFileRef = useRef('') + const currentUrlRef = useRef('') + // const currentDecorations = useRef({ sourceAnnotationsPerFile: {}, markerPerFile: {} }) // decorations that are currently in use by the editor + // const registeredDecorations = useRef({}) // registered decorations + + const [editorModelsState, dispatch] = useReducer(reducerActions, initialState) + + const formatColor = (name) => { + let color = window.getComputedStyle(document.documentElement).getPropertyValue(name).trim() + if (color.length === 4) { + color = color.concat(color.substr(1)) } - - useEffect(() => { - if (!monacoRef.current) return - defineAndSetTheme(monacoRef.current) + return color + } + const defineAndSetTheme = (monaco) => { + const themeType = props.themeType === 'dark' ? 'vs-dark' : 'vs' + const themeName = props.themeType === 'dark' ? 'remix-dark' : 'remix-light' + // see https://microsoft.github.io/monaco-editor/playground.html#customizing-the-appearence-exposed-colors + const lightColor = formatColor('--light') + const infoColor = formatColor('--info') + const darkColor = formatColor('--dark') + const secondaryColor = formatColor('--secondary') + const primaryColor = formatColor('--primary') + const textColor = formatColor('--text') || darkColor + const textbackground = formatColor('--text-background') || lightColor + + const blueColor = formatColor('--blue') + const successColor = formatColor('--success') + const warningColor = formatColor('--warning') + const yellowColor = formatColor('--yellow') + const pinkColor = formatColor('--pink') + const locationColor = '#9e7e08' + // const purpleColor = formatColor('--purple') + const dangerColor = formatColor('--danger') + const greenColor = formatColor('--green') + const orangeColor = formatColor('--orange') + const grayColor = formatColor('--gray') + + monaco.editor.defineTheme(themeName, { + base: themeType, + inherit: true, // can also be false to completely replace the builtin rules + rules: [ + { background: darkColor.replace('#', '') }, + { foreground: textColor.replace('#', '') }, + + // global variables + { token: 'keyword.abi', foreground: blueColor }, + { token: 'keyword.block', foreground: blueColor }, + { token: 'keyword.bytes', foreground: blueColor }, + { token: 'keyword.msg', foreground: blueColor }, + { token: 'keyword.tx', foreground: blueColor }, + + // global functions + { token: 'keyword.assert', foreground: blueColor }, + { token: 'keyword.require', foreground: blueColor }, + { token: 'keyword.revert', foreground: blueColor }, + { token: 'keyword.blockhash', foreground: blueColor }, + { token: 'keyword.keccak256', foreground: blueColor }, + { token: 'keyword.sha256', foreground: blueColor }, + { token: 'keyword.ripemd160', foreground: blueColor }, + { token: 'keyword.ecrecover', foreground: blueColor }, + { token: 'keyword.addmod', foreground: blueColor }, + { token: 'keyword.mulmod', foreground: blueColor }, + { token: 'keyword.selfdestruct', foreground: blueColor }, + { token: 'keyword.type ', foreground: blueColor }, + { token: 'keyword.gasleft', foreground: blueColor }, + + // specials + { token: 'keyword.super', foreground: infoColor }, + { token: 'keyword.this', foreground: infoColor }, + { token: 'keyword.virtual', foreground: infoColor }, + + // for state variables + { token: 'keyword.constants', foreground: grayColor }, + { token: 'keyword.override', foreground: grayColor }, + { token: 'keyword.immutable', foreground: grayColor }, + + // data location + { token: 'keyword.memory', foreground: locationColor }, + { token: 'keyword.storage', foreground: locationColor }, + { token: 'keyword.calldata', foreground: locationColor }, + + // for Events + { token: 'keyword.indexed', foreground: yellowColor }, + { token: 'keyword.anonymous', foreground: yellowColor }, + + // for functions + { token: 'keyword.external', foreground: successColor }, + { token: 'keyword.internal', foreground: successColor }, + { token: 'keyword.private', foreground: successColor }, + { token: 'keyword.public', foreground: successColor }, + { token: 'keyword.view', foreground: successColor }, + { token: 'keyword.pure', foreground: successColor }, + { token: 'keyword.payable', foreground: successColor }, + { token: 'keyword.nonpayable', foreground: successColor }, + + // Errors + { token: 'keyword.Error', foreground: dangerColor }, + { token: 'keyword.Panic', foreground: dangerColor }, + + // special functions + { token: 'keyword.fallback', foreground: pinkColor }, + { token: 'keyword.receive', foreground: pinkColor }, + { token: 'keyword.constructor', foreground: pinkColor }, + + // identifiers + { token: 'keyword.identifier', foreground: warningColor }, + { token: 'keyword.for', foreground: warningColor }, + { token: 'keyword.break', foreground: warningColor }, + { token: 'keyword.continue', foreground: warningColor }, + { token: 'keyword.while', foreground: warningColor }, + { token: 'keyword.do', foreground: warningColor }, + { token: 'keyword.delete', foreground: warningColor }, + + { token: 'keyword.if', foreground: yellowColor }, + { token: 'keyword.else', foreground: yellowColor }, + + { token: 'keyword.throw', foreground: orangeColor }, + { token: 'keyword.catch', foreground: orangeColor }, + { token: 'keyword.try', foreground: orangeColor }, + + // returns + { token: 'keyword.returns', foreground: greenColor }, + { token: 'keyword.return', foreground: greenColor } + + ], + colors: { + // see https://code.visualstudio.com/api/references/theme-color for more settings + 'editor.background': textbackground, + 'editorSuggestWidget.background': lightColor, + 'editorSuggestWidget.selectedBackground': secondaryColor, + 'editorSuggestWidget.selectedForeground': textColor, + 'editorSuggestWidget.highlightForeground': primaryColor, + 'editorSuggestWidget.focusHighlightForeground': infoColor, + 'editor.lineHighlightBorder': secondaryColor, + 'editor.lineHighlightBackground': textbackground === darkColor ? lightColor : secondaryColor, + 'editorGutter.background': lightColor, + //'editor.selectionHighlightBackground': secondaryColor, + 'minimap.background': lightColor, + 'menu.foreground': textColor, + 'menu.background': textbackground, + 'menu.selectionBackground': secondaryColor, + 'menu.selectionForeground': textColor, + 'menu.selectionBorder': secondaryColor + } }) - - useEffect(() => { - if (!editorRef.current || !props.currentFile) return - currentFileRef.current = props.currentFile - props.plugin.call('fileManager', 'getUrlFromPath', currentFileRef.current).then((url) => currentUrlRef.current = url.file) - - const file = editorModelsState[props.currentFile] - editorRef.current.setModel(file.model) - editorRef.current.updateOptions({ readOnly: editorModelsState[props.currentFile].readOnly }) - if (file.language === 'sol') { - monacoRef.current.editor.setModelLanguage(file.model, 'remix-solidity') - } else if (file.language === 'cairo') { - monacoRef.current.editor.setModelLanguage(file.model, 'remix-cairo') - } else if (file.language === 'zokrates') { - monacoRef.current.editor.setModelLanguage(file.model, 'remix-zokrates') - } else if (file.language === 'move') { - monacoRef.current.editor.setModelLanguage(file.model, 'remix-move') - } - }, [props.currentFile]) - - const convertToMonacoDecoration = (decoration: lineText | sourceAnnotation | sourceMarker, typeOfDecoration: string) => { - if (typeOfDecoration === 'sourceAnnotationsPerFile') { - decoration = decoration as sourceAnnotation - return { - type: typeOfDecoration, - range: new monacoRef.current.Range(decoration.row + 1, 1, decoration.row + 1, 1), - options: { - isWholeLine: false, - glyphMarginHoverMessage: { value: (decoration.from ? `from ${decoration.from}:\n` : '') + decoration.text }, - glyphMarginClassName: `fal fa-exclamation-square text-${decoration.type === 'error' ? 'danger' : (decoration.type === 'warning' ? 'warning' : 'info')}` - } - } + monacoRef.current.editor.setTheme(themeName) + } + + useEffect(() => { + if (!monacoRef.current) return + defineAndSetTheme(monacoRef.current) + }) + + useEffect(() => { + if (!editorRef.current || !props.currentFile) return + currentFileRef.current = props.currentFile + props.plugin.call('fileManager', 'getUrlFromPath', currentFileRef.current).then((url) => currentUrlRef.current = url.file) + + const file = editorModelsState[props.currentFile] + editorRef.current.setModel(file.model) + editorRef.current.updateOptions({ readOnly: editorModelsState[props.currentFile].readOnly }) + if (file.language === 'sol') { + monacoRef.current.editor.setModelLanguage(file.model, 'remix-solidity') + } else if (file.language === 'cairo') { + monacoRef.current.editor.setModelLanguage(file.model, 'remix-cairo') + } else if (file.language === 'zokrates') { + monacoRef.current.editor.setModelLanguage(file.model, 'remix-zokrates') + } else if (file.language === 'move') { + monacoRef.current.editor.setModelLanguage(file.model, 'remix-move') + } + }, [props.currentFile]) + + const convertToMonacoDecoration = (decoration: lineText | sourceAnnotation | sourceMarker, typeOfDecoration: string) => { + if (typeOfDecoration === 'sourceAnnotationsPerFile') { + decoration = decoration as sourceAnnotation + return { + type: typeOfDecoration, + range: new monacoRef.current.Range(decoration.row + 1, 1, decoration.row + 1, 1), + options: { + isWholeLine: false, + glyphMarginHoverMessage: { value: (decoration.from ? `from ${decoration.from}:\n` : '') + decoration.text }, + glyphMarginClassName: `fal fa-exclamation-square text-${decoration.type === 'error' ? 'danger' : (decoration.type === 'warning' ? 'warning' : 'info')}` } - if (typeOfDecoration === 'markerPerFile') { - decoration = decoration as sourceMarker - let isWholeLine = false - if ((decoration.position.start.line === decoration.position.end.line && decoration.position.end.column - decoration.position.start.column < 2) || + } + } + if (typeOfDecoration === 'markerPerFile') { + decoration = decoration as sourceMarker + let isWholeLine = false + if ((decoration.position.start.line === decoration.position.end.line && decoration.position.end.column - decoration.position.start.column < 2) || (decoration.position.start.line !== decoration.position.end.line)) { - // in this case we force highlighting the whole line (doesn't make sense to highlight 2 chars) - isWholeLine = true - } - return { - type: typeOfDecoration, - range: new monacoRef.current.Range(decoration.position.start.line + 1, decoration.position.start.column + 1, decoration.position.end.line + 1, decoration.position.end.column + 1), - options: { - isWholeLine, - inlineClassName: `${isWholeLine ? 'alert-info' : 'inline-class'} border-0 highlightLine${decoration.position.start.line + 1}` - } - } - } - if (typeOfDecoration === 'lineTextPerFile') { - const lineTextDecoration = decoration as lineText - return { - type: typeOfDecoration, - range: new monacoRef.current.Range(lineTextDecoration.position.start.line + 1, lineTextDecoration.position.start.column + 1, lineTextDecoration.position.start.line + 1, 1024), - options: { - after: { content: ` ${lineTextDecoration.content}`, inlineClassName: `${lineTextDecoration.className}` }, - afterContentClassName: `${lineTextDecoration.afterContentClassName}`, - hoverMessage : lineTextDecoration.hoverMessage - }, - - } - } - if (typeOfDecoration === 'lineTextPerFile') { - const lineTextDecoration = decoration as lineText - return { - type: typeOfDecoration, - range: new monacoRef.current.Range(lineTextDecoration.position.start.line + 1, lineTextDecoration.position.start.column + 1, lineTextDecoration.position.start.line + 1, 1024), - options: { - after: { content: ` ${lineTextDecoration.content}`, inlineClassName: `${lineTextDecoration.className}` }, - afterContentClassName: `${lineTextDecoration.afterContentClassName}`, - hoverMessage : lineTextDecoration.hoverMessage - }, - } + // in this case we force highlighting the whole line (doesn't make sense to highlight 2 chars) + isWholeLine = true + } + return { + type: typeOfDecoration, + range: new monacoRef.current.Range(decoration.position.start.line + 1, decoration.position.start.column + 1, decoration.position.end.line + 1, decoration.position.end.column + 1), + options: { + isWholeLine, + inlineClassName: `${isWholeLine ? 'alert-info' : 'inline-class'} border-0 highlightLine${decoration.position.start.line + 1}` } + } } + if (typeOfDecoration === 'lineTextPerFile') { + const lineTextDecoration = decoration as lineText + return { + type: typeOfDecoration, + range: new monacoRef.current.Range(lineTextDecoration.position.start.line + 1, lineTextDecoration.position.start.column + 1, lineTextDecoration.position.start.line + 1, 1024), + options: { + after: { content: ` ${lineTextDecoration.content}`, inlineClassName: `${lineTextDecoration.className}` }, + afterContentClassName: `${lineTextDecoration.afterContentClassName}`, + hoverMessage : lineTextDecoration.hoverMessage + }, - props.editorAPI.clearDecorationsByPlugin = (filePath: string, plugin: string, typeOfDecoration: string, registeredDecorations: any, currentDecorations: any) => { - const model = editorModelsState[filePath]?.model - if (!model) return { - currentDecorations: [], - registeredDecorations: [] - } - const decorations = [] - const newRegisteredDecorations = [] - if (registeredDecorations) { - for (const decoration of registeredDecorations) { - if (decoration.type === typeOfDecoration && decoration.value.from !== plugin) { - decorations.push(convertToMonacoDecoration(decoration.value, typeOfDecoration)) - newRegisteredDecorations.push(decoration) - } - } - } - return { - currentDecorations: model.deltaDecorations(currentDecorations, decorations), - registeredDecorations: newRegisteredDecorations - } + } } - - props.editorAPI.keepDecorationsFor = (filePath: string, plugin: string, typeOfDecoration: string, registeredDecorations: any, currentDecorations: any) => { - const model = editorModelsState[filePath]?.model - if (!model) return { - currentDecorations: [] - } - const decorations = [] - if (registeredDecorations) { - for (const decoration of registeredDecorations) { - if (decoration.value.from === plugin) { - decorations.push(convertToMonacoDecoration(decoration.value, typeOfDecoration)) - } - } - } - return { - currentDecorations: model.deltaDecorations(currentDecorations, decorations) - } + if (typeOfDecoration === 'lineTextPerFile') { + const lineTextDecoration = decoration as lineText + return { + type: typeOfDecoration, + range: new monacoRef.current.Range(lineTextDecoration.position.start.line + 1, lineTextDecoration.position.start.column + 1, lineTextDecoration.position.start.line + 1, 1024), + options: { + after: { content: ` ${lineTextDecoration.content}`, inlineClassName: `${lineTextDecoration.className}` }, + afterContentClassName: `${lineTextDecoration.afterContentClassName}`, + hoverMessage : lineTextDecoration.hoverMessage + }, + } } + } - const addDecoration = (decoration: sourceAnnotation | sourceMarker, filePath: string, typeOfDecoration: string) => { - const model = editorModelsState[filePath]?.model - if (!model) return { currentDecorations: [] } - const monacoDecoration = convertToMonacoDecoration(decoration, typeOfDecoration) - return { - currentDecorations: model.deltaDecorations([], [monacoDecoration]), - registeredDecorations: [{ value: decoration, type: typeOfDecoration }] + props.editorAPI.clearDecorationsByPlugin = (filePath: string, plugin: string, typeOfDecoration: string, registeredDecorations: any, currentDecorations: any) => { + const model = editorModelsState[filePath]?.model + if (!model) return { + currentDecorations: [], + registeredDecorations: [] + } + const decorations = [] + const newRegisteredDecorations = [] + if (registeredDecorations) { + for (const decoration of registeredDecorations) { + if (decoration.type === typeOfDecoration && decoration.value.from !== plugin) { + decorations.push(convertToMonacoDecoration(decoration.value, typeOfDecoration)) + newRegisteredDecorations.push(decoration) } + } } - - props.editorAPI.addDecoration = (marker: sourceMarker, filePath: string, typeOfDecoration: string) => { - return addDecoration(marker, filePath, typeOfDecoration) + return { + currentDecorations: model.deltaDecorations(currentDecorations, decorations), + registeredDecorations: newRegisteredDecorations } + } - props.editorAPI.addErrorMarker = async (errors: errorMarker[], from: string) => { - - const allMarkersPerfile: Record> = {} - - for (const error of errors) { - let filePath = error.file - - if (!filePath) return - const fileFromUrl = await props.plugin.call('fileManager', 'getPathFromUrl', filePath) - filePath = fileFromUrl.file - const model = editorModelsState[filePath]?.model - const errorServerityMap = { - 'error': MarkerSeverity.Error, - 'warning': MarkerSeverity.Warning, - 'info': MarkerSeverity.Info - } - if (model) { - const markerData: monacoTypes.editor.IMarkerData = { - severity: (typeof error.severity === 'string') ? errorServerityMap[error.severity] : error.severity, - startLineNumber: ((error.position.start && error.position.start.line) || 0), - startColumn: ((error.position.start && error.position.start.column) || 0), - endLineNumber: ((error.position.end && error.position.end.line) || 0), - endColumn: ((error.position.end && error.position.end.column) || 0), - message: error.message, - } - if (!allMarkersPerfile[filePath]) { - allMarkersPerfile[filePath] = [] - } - allMarkersPerfile[filePath].push(markerData) - } - } - for (const filePath in allMarkersPerfile) { - const model = editorModelsState[filePath]?.model - if (model) { - monacoRef.current.editor.setModelMarkers(model, from, allMarkersPerfile[filePath]) - } - } + props.editorAPI.keepDecorationsFor = (filePath: string, plugin: string, typeOfDecoration: string, registeredDecorations: any, currentDecorations: any) => { + const model = editorModelsState[filePath]?.model + if (!model) return { + currentDecorations: [] } - - props.editorAPI.clearErrorMarkers = async (sources: string[] | {[fileName: string]: any}, from: string) => { - if (sources) { - for (const source of (Array.isArray(sources) ? sources : Object.keys(sources))) { - const filePath = source - const model = editorModelsState[filePath]?.model - if (model) { - monacoRef.current.editor.setModelMarkers(model, from, []) - } - } + const decorations = [] + if (registeredDecorations) { + for (const decoration of registeredDecorations) { + if (decoration.value.from === plugin) { + decorations.push(convertToMonacoDecoration(decoration.value, typeOfDecoration)) } + } } - - props.editorAPI.findMatches = (uri: string, value: string) => { - if (!editorRef.current) return - const model = editorModelsState[uri]?.model - if (model) return model.findMatches(value) + return { + currentDecorations: model.deltaDecorations(currentDecorations, decorations) } - - props.editorAPI.getValue = (uri: string) => { - if (!editorRef.current) return - const model = editorModelsState[uri]?.model - if (model) { - return model.getValue() - } + } + + const addDecoration = (decoration: sourceAnnotation | sourceMarker, filePath: string, typeOfDecoration: string) => { + const model = editorModelsState[filePath]?.model + if (!model) return { currentDecorations: [] } + const monacoDecoration = convertToMonacoDecoration(decoration, typeOfDecoration) + return { + currentDecorations: model.deltaDecorations([], [monacoDecoration]), + registeredDecorations: [{ value: decoration, type: typeOfDecoration }] } - - props.editorAPI.getCursorPosition = (offset:boolean = true) => { - if (!monacoRef.current) return - const model = editorModelsState[currentFileRef.current]?.model - if (model) { - return offset? model.getOffsetAt(editorRef.current.getPosition()): editorRef.current.getPosition() + } + + props.editorAPI.addDecoration = (marker: sourceMarker, filePath: string, typeOfDecoration: string) => { + return addDecoration(marker, filePath, typeOfDecoration) + } + + props.editorAPI.addErrorMarker = async (errors: errorMarker[], from: string) => { + + const allMarkersPerfile: Record> = {} + + for (const error of errors) { + let filePath = error.file + + if (!filePath) return + const fileFromUrl = await props.plugin.call('fileManager', 'getPathFromUrl', filePath) + filePath = fileFromUrl.file + const model = editorModelsState[filePath]?.model + const errorServerityMap = { + 'error': MarkerSeverity.Error, + 'warning': MarkerSeverity.Warning, + 'info': MarkerSeverity.Info + } + if (model) { + const markerData: monacoTypes.editor.IMarkerData = { + severity: (typeof error.severity === 'string') ? errorServerityMap[error.severity] : error.severity, + startLineNumber: ((error.position.start && error.position.start.line) || 0), + startColumn: ((error.position.start && error.position.start.column) || 0), + endLineNumber: ((error.position.end && error.position.end.line) || 0), + endColumn: ((error.position.end && error.position.end.column) || 0), + message: error.message, + } + if (!allMarkersPerfile[filePath]) { + allMarkersPerfile[filePath] = [] } + allMarkersPerfile[filePath].push(markerData) + } } + for (const filePath in allMarkersPerfile) { + const model = editorModelsState[filePath]?.model + if (model) { + monacoRef.current.editor.setModelMarkers(model, from, allMarkersPerfile[filePath]) + } + } + } - props.editorAPI.getHoverPosition = (position: monacoTypes.Position) => { - if (!monacoRef.current) return - const model = editorModelsState[currentFileRef.current]?.model + props.editorAPI.clearErrorMarkers = async (sources: string[] | {[fileName: string]: any}, from: string) => { + if (sources) { + for (const source of (Array.isArray(sources) ? sources : Object.keys(sources))) { + const filePath = source + const model = editorModelsState[filePath]?.model if (model) { - return model.getOffsetAt(position) - } else { - return 0 + monacoRef.current.editor.setModelMarkers(model, from, []) } + } } - - props.editorAPI.getFontSize = () => { - if (!editorRef.current) return - return editorRef.current.getOption(43).fontSize + } + + props.editorAPI.findMatches = (uri: string, value: string) => { + if (!editorRef.current) return + const model = editorModelsState[uri]?.model + if (model) return model.findMatches(value) + } + + props.editorAPI.getValue = (uri: string) => { + if (!editorRef.current) return + const model = editorModelsState[uri]?.model + if (model) { + return model.getValue() } + } - (window as any).addRemixBreakpoint = (position) => { // make it available from e2e testing... - const model = editorRef.current.getModel() - if (model) { - setCurrentBreakpoints(prevState => { - const currentFile = currentUrlRef.current - if (!prevState[currentFile]) prevState[currentFile] = {} - const decoration = Object.keys(prevState[currentFile]).filter((line) => parseInt(line) === position.lineNumber) - if (decoration.length) { - props.events.onBreakPointCleared(currentFile, position.lineNumber) - model.deltaDecorations([prevState[currentFile][position.lineNumber]], []) - delete prevState[currentFile][position.lineNumber] - } else { - props.events.onBreakPointAdded(currentFile, position.lineNumber) - const decorationIds = model.deltaDecorations([], [{ - range: new monacoRef.current.Range(position.lineNumber, 1, position.lineNumber, 1), - options: { - isWholeLine: false, - glyphMarginClassName: 'fas fa-circle text-info' - } - }]) - prevState[currentFile][position.lineNumber] = decorationIds[0] - } - return prevState - }) + props.editorAPI.getCursorPosition = (offset:boolean = true) => { + if (!monacoRef.current) return + const model = editorModelsState[currentFileRef.current]?.model + if (model) { + return offset? model.getOffsetAt(editorRef.current.getPosition()): editorRef.current.getPosition() + } + } + + props.editorAPI.getHoverPosition = (position: monacoTypes.Position) => { + if (!monacoRef.current) return + const model = editorModelsState[currentFileRef.current]?.model + if (model) { + return model.getOffsetAt(position) + } else { + return 0 + } + } + + props.editorAPI.getFontSize = () => { + if (!editorRef.current) return + return editorRef.current.getOption(43).fontSize + } + + (window as any).addRemixBreakpoint = (position) => { // make it available from e2e testing... + const model = editorRef.current.getModel() + if (model) { + setCurrentBreakpoints(prevState => { + const currentFile = currentUrlRef.current + if (!prevState[currentFile]) prevState[currentFile] = {} + const decoration = Object.keys(prevState[currentFile]).filter((line) => parseInt(line) === position.lineNumber) + if (decoration.length) { + props.events.onBreakPointCleared(currentFile, position.lineNumber) + model.deltaDecorations([prevState[currentFile][position.lineNumber]], []) + delete prevState[currentFile][position.lineNumber] + } else { + props.events.onBreakPointAdded(currentFile, position.lineNumber) + const decorationIds = model.deltaDecorations([], [{ + range: new monacoRef.current.Range(position.lineNumber, 1, position.lineNumber, 1), + options: { + isWholeLine: false, + glyphMarginClassName: 'fas fa-circle text-info' + } + }]) + prevState[currentFile][position.lineNumber] = decorationIds[0] } + return prevState + }) } + } + + function handleEditorDidMount(editor) { + editorRef.current = editor + defineAndSetTheme(monacoRef.current) + reducerListener(props.plugin, dispatch, monacoRef.current, editorRef.current, props.events) + props.events.onEditorMounted() + editor.onMouseUp((e) => { + // see https://microsoft.github.io/monaco-editor/typedoc/enums/editor.MouseTargetType.html + // 2 is GUTTER_GLYPH_MARGIN + // 3 is GUTTER_LINE_NUMBERS + if (e && e.target && (e.target.type === 2 || e.target.type === 3)) { + (window as any).addRemixBreakpoint(e.target.position) + } + }) - function handleEditorDidMount(editor) { - editorRef.current = editor - defineAndSetTheme(monacoRef.current) - reducerListener(props.plugin, dispatch, monacoRef.current, editorRef.current, props.events) - props.events.onEditorMounted() - editor.onMouseUp((e) => { - // see https://microsoft.github.io/monaco-editor/typedoc/enums/editor.MouseTargetType.html - // 2 is GUTTER_GLYPH_MARGIN - // 3 is GUTTER_LINE_NUMBERS - if (e && e.target && (e.target.type === 2 || e.target.type === 3)) { - (window as any).addRemixBreakpoint(e.target.position) - } - }) - - editor.onDidPaste((e) => { - if (!pasteCodeRef.current && e && e.range && e.range.startLineNumber >= 0 && e.range.endLineNumber >= 0 && e.range.endLineNumber - e.range.startLineNumber > 10) { - const modalContent: AlertModal = { - id: 'newCodePasted', - title: 'Pasted Code Alert', - message: ( -
+ editor.onDidPaste((e) => { + if (!pasteCodeRef.current && e && e.range && e.range.startLineNumber >= 0 && e.range.endLineNumber >= 0 && e.range.endLineNumber - e.range.startLineNumber > 10) { + const modalContent: AlertModal = { + id: 'newCodePasted', + title: 'Pasted Code Alert', + message: ( +
You have just pasted a code snippet or contract in the editor. -
+
Make sure you fully understand this code before deploying or interacting with it. Don't get scammed! -
+
Running untrusted code can put your wallet at risk . In a worst-case scenario, you could lose all your money. -
-
If you don't fully understand it, please don't run this code.
-
+
+
If you don't fully understand it, please don't run this code.
+
If you are not a smart contract developer, ask someone you trust who has the skills to determine if this code is safe to use. -
-
See these recommendations for more information.
-
-
- ), - } - props.plugin.call('notification', 'alert', modalContent) - pasteCodeRef.current = true - } - }) - - // zoomin zoomout - editor.addCommand(monacoRef.current.KeyMod.CtrlCmd | (monacoRef.current.KeyCode as any).US_EQUAL, () => { - editor.updateOptions({ fontSize: editor.getOption(43).fontSize + 1 }) - }) - editor.addCommand(monacoRef.current.KeyMod.CtrlCmd | (monacoRef.current.KeyCode as any).US_MINUS, () => { - editor.updateOptions({ fontSize: editor.getOption(43).fontSize - 1 }) - }) - - // add context menu items - const zoominAction = { - id: "zoomIn", - label: "Zoom In", - contextMenuOrder: 0, // choose the order - contextMenuGroupId: "zooming", // create a new grouping - keybindings: [ - // eslint-disable-next-line no-bitwise - monacoRef.current.KeyMod.CtrlCmd | monacoRef.current.KeyCode.Equal, - ], - run: () => { editor.updateOptions({ fontSize: editor.getOption(43).fontSize + 1 }) }, - } - const zoomOutAction = { - id: "zoomOut", - label: "Zoom Out", - contextMenuOrder: 0, // choose the order - contextMenuGroupId: "zooming", // create a new grouping - keybindings: [ - // eslint-disable-next-line no-bitwise - monacoRef.current.KeyMod.CtrlCmd | monacoRef.current.KeyCode.Minus, - ], - run: () => { editor.updateOptions({ fontSize: editor.getOption(43).fontSize - 1 }) }, - } - const formatAction = { - id: "autoFormat", - label: "Format Code", - contextMenuOrder: 0, // choose the order - contextMenuGroupId: "formatting", // create a new grouping - keybindings: [ - // eslint-disable-next-line no-bitwise - monacoRef.current.KeyMod.Shift | monacoRef.current.KeyMod.Alt | monacoRef.current.KeyCode.KeyF, - ], - run: async () => { - const file = await props.plugin.call('fileManager', 'getCurrentFile') - await props.plugin.call('codeFormatter', 'format', file) - }, +
+
See these recommendations for more information.
+
+
+ ), } + props.plugin.call('notification', 'alert', modalContent) + pasteCodeRef.current = true + } + }) - const freeFunctionCondition = editor.createContextKey('freeFunctionCondition', false); - let freeFunctionAction - const executeFreeFunctionAction = { - id: "executeFreeFunction", - label: "Run a free function in the Remix VM", - contextMenuOrder: 0, // choose the order - contextMenuGroupId: "execute", // create a new grouping - precondition: 'freeFunctionCondition', - keybindings: [ - // eslint-disable-next-line no-bitwise - monacoRef.current.KeyMod.Shift | monacoRef.current.KeyMod.Alt | monacoRef.current.KeyCode.KeyR, - ], - run: async () => { - const { nodesAtPosition } = await retrieveNodesAtPosition(props.editorAPI, props.plugin) - // find the contract and get the nodes of the contract and the base contracts and imports - if (nodesAtPosition && isArray(nodesAtPosition) && nodesAtPosition.length) { - const freeFunctionNode = nodesAtPosition.find((node) => node.kind === 'freeFunction') - if (freeFunctionNode) { - const file = await props.plugin.call('fileManager', 'getCurrentFile') - props.plugin.call('solidity-script', 'execute', file, freeFunctionNode.name) - } else { - props.plugin.call('notification', 'toast', 'This can only execute free function') - } - } else { - props.plugin.call('notification', 'toast', 'Please go to Remix settings and activate the code editor features or wait that the current editor context is loaded.') - } - }, - } - editor.addAction(formatAction) - editor.addAction(zoomOutAction) - editor.addAction(zoominAction) - freeFunctionAction = editor.addAction(executeFreeFunctionAction) + // zoomin zoomout + editor.addCommand(monacoRef.current.KeyMod.CtrlCmd | (monacoRef.current.KeyCode as any).US_EQUAL, () => { + editor.updateOptions({ fontSize: editor.getOption(43).fontSize + 1 }) + }) + editor.addCommand(monacoRef.current.KeyMod.CtrlCmd | (monacoRef.current.KeyCode as any).US_MINUS, () => { + editor.updateOptions({ fontSize: editor.getOption(43).fontSize - 1 }) + }) - // we have to add the command because the menu action isn't always available (see onContextMenuHandlerForFreeFunction) - editor.addCommand(monacoRef.current.KeyMod.Shift | monacoRef.current.KeyMod.Alt | monacoRef.current.KeyCode.KeyR, () => executeFreeFunctionAction.run()) + // add context menu items + const zoominAction = { + id: "zoomIn", + label: "Zoom In", + contextMenuOrder: 0, // choose the order + contextMenuGroupId: "zooming", // create a new grouping + keybindings: [ + // eslint-disable-next-line no-bitwise + monacoRef.current.KeyMod.CtrlCmd | monacoRef.current.KeyCode.Equal, + ], + run: () => { editor.updateOptions({ fontSize: editor.getOption(43).fontSize + 1 }) }, + } + const zoomOutAction = { + id: "zoomOut", + label: "Zoom Out", + contextMenuOrder: 0, // choose the order + contextMenuGroupId: "zooming", // create a new grouping + keybindings: [ + // eslint-disable-next-line no-bitwise + monacoRef.current.KeyMod.CtrlCmd | monacoRef.current.KeyCode.Minus, + ], + run: () => { editor.updateOptions({ fontSize: editor.getOption(43).fontSize - 1 }) }, + } + const formatAction = { + id: "autoFormat", + label: "Format Code", + contextMenuOrder: 0, // choose the order + contextMenuGroupId: "formatting", // create a new grouping + keybindings: [ + // eslint-disable-next-line no-bitwise + monacoRef.current.KeyMod.Shift | monacoRef.current.KeyMod.Alt | monacoRef.current.KeyCode.KeyF, + ], + run: async () => { + const file = await props.plugin.call('fileManager', 'getCurrentFile') + await props.plugin.call('codeFormatter', 'format', file) + }, + } - const contextmenu = editor.getContribution('editor.contrib.contextmenu') - const orgContextMenuMethod = contextmenu._onContextMenu; - const onContextMenuHandlerForFreeFunction = async () => { - if (freeFunctionAction) { - freeFunctionAction.dispose() - freeFunctionAction = null - } + const freeFunctionCondition = editor.createContextKey('freeFunctionCondition', false); + let freeFunctionAction + const executeFreeFunctionAction = { + id: "executeFreeFunction", + label: "Run a free function in the Remix VM", + contextMenuOrder: 0, // choose the order + contextMenuGroupId: "execute", // create a new grouping + precondition: 'freeFunctionCondition', + keybindings: [ + // eslint-disable-next-line no-bitwise + monacoRef.current.KeyMod.Shift | monacoRef.current.KeyMod.Alt | monacoRef.current.KeyCode.KeyR, + ], + run: async () => { + const { nodesAtPosition } = await retrieveNodesAtPosition(props.editorAPI, props.plugin) + // find the contract and get the nodes of the contract and the base contracts and imports + if (nodesAtPosition && isArray(nodesAtPosition) && nodesAtPosition.length) { + const freeFunctionNode = nodesAtPosition.find((node) => node.kind === 'freeFunction') + if (freeFunctionNode) { const file = await props.plugin.call('fileManager', 'getCurrentFile') - if (!file.endsWith('.sol')) { - freeFunctionCondition.set(false) - return - } - const { nodesAtPosition } = await retrieveNodesAtPosition(props.editorAPI, props.plugin) - const freeFunctionNode = nodesAtPosition.find((node) => node.kind === 'freeFunction') - if (freeFunctionNode) { - executeFreeFunctionAction.label = `Run the free function "${freeFunctionNode.name}" in the Remix VM` - freeFunctionAction = editor.addAction(executeFreeFunctionAction) - } - freeFunctionCondition.set(!!freeFunctionNode) - } - contextmenu._onContextMenu = (...args) => { - if (args[0]) args[0].event?.preventDefault() - onContextMenuHandlerForFreeFunction() - .then(() => orgContextMenuMethod.apply(contextmenu, args)) - .catch(() => orgContextMenuMethod.apply(contextmenu, args)) - } - - const editorService = editor._codeEditorService; - const openEditorBase = editorService.openCodeEditor.bind(editorService); - editorService.openCodeEditor = async (input , source) => { - const result = await openEditorBase(input, source) - if (input && input.resource && input.resource.path) { - try { - await props.plugin.call('fileManager', 'open', input.resource.path) - if (input.options && input.options.selection) { - editor.revealRange(input.options.selection) - editor.setPosition({ column: input.options.selection.startColumn, lineNumber: input.options.selection.startLineNumber }) - } - } catch (e) { - console.log(e) - } - } - return result + props.plugin.call('solidity-script', 'execute', file, freeFunctionNode.name) + } else { + props.plugin.call('notification', 'toast', 'This can only execute free function') + } + } else { + props.plugin.call('notification', 'toast', 'Please go to Remix settings and activate the code editor features or wait that the current editor context is loaded.') } - // just for e2e testing - const loadedElement = document.createElement('span') - loadedElement.setAttribute('data-id', 'editorloaded') - document.body.appendChild(loadedElement) + }, } - - function handleEditorWillMount(monaco) { - monacoRef.current = monaco - // Register a new language - monacoRef.current.languages.register({ id: 'remix-solidity' }) - monacoRef.current.languages.register({ id: 'remix-cairo' }) - monacoRef.current.languages.register({ id: 'remix-zokrates' }) - monacoRef.current.languages.register({ id: 'remix-move' }) - - // Register a tokens provider for the language - monacoRef.current.languages.setMonarchTokensProvider('remix-solidity', solidityTokensProvider as any) - monacoRef.current.languages.setLanguageConfiguration('remix-solidity', solidityLanguageConfig as any ) - - monacoRef.current.languages.setMonarchTokensProvider('remix-cairo', cairoTokensProvider as any) - monacoRef.current.languages.setLanguageConfiguration('remix-cairo', cairoLanguageConfig as any) - - monacoRef.current.languages.setMonarchTokensProvider('remix-zokrates', zokratesTokensProvider as any) - monacoRef.current.languages.setLanguageConfiguration('remix-zokrates', zokratesLanguageConfig as any) - - monacoRef.current.languages.setMonarchTokensProvider('remix-move', moveTokenProvider as any) - monacoRef.current.languages.setLanguageConfiguration('remix-move', moveLanguageConfig as any) - - monacoRef.current.languages.registerDefinitionProvider('remix-solidity', new RemixDefinitionProvider(props, monaco)) - monacoRef.current.languages.registerDocumentHighlightProvider('remix-solidity', new RemixHighLightProvider(props, monaco)) - monacoRef.current.languages.registerReferenceProvider('remix-solidity', new RemixReferenceProvider(props, monaco)) - monacoRef.current.languages.registerHoverProvider('remix-solidity', new RemixHoverProvider(props, monaco)) - monacoRef.current.languages.registerCompletionItemProvider('remix-solidity', new RemixCompletionProvider(props, monaco)) - - loadTypes(monacoRef.current) + editor.addAction(formatAction) + editor.addAction(zoomOutAction) + editor.addAction(zoominAction) + freeFunctionAction = editor.addAction(executeFreeFunctionAction) + + // we have to add the command because the menu action isn't always available (see onContextMenuHandlerForFreeFunction) + editor.addCommand(monacoRef.current.KeyMod.Shift | monacoRef.current.KeyMod.Alt | monacoRef.current.KeyCode.KeyR, () => executeFreeFunctionAction.run()) + + const contextmenu = editor.getContribution('editor.contrib.contextmenu') + const orgContextMenuMethod = contextmenu._onContextMenu; + const onContextMenuHandlerForFreeFunction = async () => { + if (freeFunctionAction) { + freeFunctionAction.dispose() + freeFunctionAction = null + } + const file = await props.plugin.call('fileManager', 'getCurrentFile') + if (!file.endsWith('.sol')) { + freeFunctionCondition.set(false) + return + } + const { nodesAtPosition } = await retrieveNodesAtPosition(props.editorAPI, props.plugin) + const freeFunctionNode = nodesAtPosition.find((node) => node.kind === 'freeFunction') + if (freeFunctionNode) { + executeFreeFunctionAction.label = `Run the free function "${freeFunctionNode.name}" in the Remix VM` + freeFunctionAction = editor.addAction(executeFreeFunctionAction) + } + freeFunctionCondition.set(!!freeFunctionNode) + } + contextmenu._onContextMenu = (...args) => { + if (args[0]) args[0].event?.preventDefault() + onContextMenuHandlerForFreeFunction() + .then(() => orgContextMenuMethod.apply(contextmenu, args)) + .catch(() => orgContextMenuMethod.apply(contextmenu, args)) } - return ( -
- - {editorModelsState[props.currentFile]?.readOnly && - + const editorService = editor._codeEditorService; + const openEditorBase = editorService.openCodeEditor.bind(editorService); + editorService.openCodeEditor = async (input , source) => { + const result = await openEditorBase(input, source) + if (input && input.resource && input.resource.path) { + try { + await props.plugin.call('fileManager', 'open', input.resource.path) + if (input.options && input.options.selection) { + editor.revealRange(input.options.selection) + editor.setPosition({ column: input.options.selection.startColumn, lineNumber: input.options.selection.startLineNumber }) + } + } catch (e) { + console.log(e) + } + } + return result + } + // just for e2e testing + const loadedElement = document.createElement('span') + loadedElement.setAttribute('data-id', 'editorloaded') + document.body.appendChild(loadedElement) + } + + function handleEditorWillMount(monaco) { + monacoRef.current = monaco + // Register a new language + monacoRef.current.languages.register({ id: 'remix-solidity' }) + monacoRef.current.languages.register({ id: 'remix-cairo' }) + monacoRef.current.languages.register({ id: 'remix-zokrates' }) + monacoRef.current.languages.register({ id: 'remix-move' }) + + // Register a tokens provider for the language + monacoRef.current.languages.setMonarchTokensProvider('remix-solidity', solidityTokensProvider as any) + monacoRef.current.languages.setLanguageConfiguration('remix-solidity', solidityLanguageConfig as any ) + + monacoRef.current.languages.setMonarchTokensProvider('remix-cairo', cairoTokensProvider as any) + monacoRef.current.languages.setLanguageConfiguration('remix-cairo', cairoLanguageConfig as any) + + monacoRef.current.languages.setMonarchTokensProvider('remix-zokrates', zokratesTokensProvider as any) + monacoRef.current.languages.setLanguageConfiguration('remix-zokrates', zokratesLanguageConfig as any) + + monacoRef.current.languages.setMonarchTokensProvider('remix-move', moveTokenProvider as any) + monacoRef.current.languages.setLanguageConfiguration('remix-move', moveLanguageConfig as any) + + monacoRef.current.languages.registerDefinitionProvider('remix-solidity', new RemixDefinitionProvider(props, monaco)) + monacoRef.current.languages.registerDocumentHighlightProvider('remix-solidity', new RemixHighLightProvider(props, monaco)) + monacoRef.current.languages.registerReferenceProvider('remix-solidity', new RemixReferenceProvider(props, monaco)) + monacoRef.current.languages.registerHoverProvider('remix-solidity', new RemixHoverProvider(props, monaco)) + monacoRef.current.languages.registerCompletionItemProvider('remix-solidity', new RemixCompletionProvider(props, monaco)) + + loadTypes(monacoRef.current) + } + + return ( +
+ + {editorModelsState[props.currentFile]?.readOnly && + The file is opened in read-only mode. - - } -
- ) +
+ } +
+ ) } export default EditorUI diff --git a/libs/remix-ui/editor/src/lib/syntaxes/move.ts b/libs/remix-ui/editor/src/lib/syntaxes/move.ts index 9f184937a0..a963641505 100644 --- a/libs/remix-ui/editor/src/lib/syntaxes/move.ts +++ b/libs/remix-ui/editor/src/lib/syntaxes/move.ts @@ -1,264 +1,264 @@ /* eslint-disable no-useless-escape */ export const moveLanguageConfig = { - comments: { - lineComment: "//", - blockComment: ["/*", "*/"], - }, - brackets: [ - ["{", "}"], - ["[", "]"], - ["(", ")"], - ], - autoClosingPairs: [ - { open: "[", close: "]" }, - { open: "{", close: "}" }, - { open: "(", close: ")" }, - { open: '"', close: '"', notIn: ["string"] }, - ], - surroundingPairs: [ - { open: "{", close: "}" }, - { open: "[", close: "]" }, - { open: "(", close: ")" }, - { open: '"', close: '"' }, - { open: "'", close: "'" }, - ], - folding: { - markers: { - start: new RegExp("^\\s*#pragma\\s+region\\b"), - end: new RegExp("^\\s*#pragma\\s+endregion\\b"), - }, + comments: { + lineComment: "//", + blockComment: ["/*", "*/"], + }, + brackets: [ + ["{", "}"], + ["[", "]"], + ["(", ")"], + ], + autoClosingPairs: [ + { open: "[", close: "]" }, + { open: "{", close: "}" }, + { open: "(", close: ")" }, + { open: '"', close: '"', notIn: ["string"] }, + ], + surroundingPairs: [ + { open: "{", close: "}" }, + { open: "[", close: "]" }, + { open: "(", close: ")" }, + { open: '"', close: '"' }, + { open: "'", close: "'" }, + ], + folding: { + markers: { + start: new RegExp("^\\s*#pragma\\s+region\\b"), + end: new RegExp("^\\s*#pragma\\s+endregion\\b"), }, + }, }; export const moveTokenProvider = { - // Set defaultToken to invalid to see what you do not tokenize yet - // defaultToken: 'invalid', - keywords: [ - "as", - "break", - "const", - "crate", - "enum", - "extern", - "false", - "fun", - "script", - "in", - "let", - "module", - "move", - "mut", - "pub", - "ref", - "return", - "self", - "Self", - "static", - "struct", - "super", - "trait", - "true", - "type", - "unsafe", - "use", - "where", - "use", - "macro_rules", - ], + // Set defaultToken to invalid to see what you do not tokenize yet + // defaultToken: 'invalid', + keywords: [ + "as", + "break", + "const", + "crate", + "enum", + "extern", + "false", + "fun", + "script", + "in", + "let", + "module", + "move", + "mut", + "pub", + "ref", + "return", + "self", + "Self", + "static", + "struct", + "super", + "trait", + "true", + "type", + "unsafe", + "use", + "where", + "use", + "macro_rules", + ], - controlFlowKeywords: [ - "continue", - "else", - "for", - "if", - "while", - "loop", - "match", - ], + controlFlowKeywords: [ + "continue", + "else", + "for", + "if", + "while", + "loop", + "match", + ], - typeKeywords: [ - "Self", - "m32", - "m64", - "m128", - "f80", - "f16", - "f128", - "int", - "uint", - "float", - "char", - "bool", - "u8", - "u16", - "u32", - "u64", - "f32", - "f64", - "i8", - "i16", - "i32", - "i64", - "str", - "Option", - "Either", - "c_float", - "c_double", - "c_void", - "FILE", - "fpos_t", - "DIR", - "dirent", - "c_char", - "c_schar", - "c_uchar", - "c_short", - "c_ushort", - "c_int", - "c_uint", - "c_long", - "c_ulong", - "size_t", - "ptrdiff_t", - "clock_t", - "time_t", - "c_longlong", - "c_ulonglong", - "intptr_t", - "uintptr_t", - "off_t", - "dev_t", - "ino_t", - "pid_t", - "mode_t", - "ssize_t", - ], + typeKeywords: [ + "Self", + "m32", + "m64", + "m128", + "f80", + "f16", + "f128", + "int", + "uint", + "float", + "char", + "bool", + "u8", + "u16", + "u32", + "u64", + "f32", + "f64", + "i8", + "i16", + "i32", + "i64", + "str", + "Option", + "Either", + "c_float", + "c_double", + "c_void", + "FILE", + "fpos_t", + "DIR", + "dirent", + "c_char", + "c_schar", + "c_uchar", + "c_short", + "c_ushort", + "c_int", + "c_uint", + "c_long", + "c_ulong", + "size_t", + "ptrdiff_t", + "clock_t", + "time_t", + "c_longlong", + "c_ulonglong", + "intptr_t", + "uintptr_t", + "off_t", + "dev_t", + "ino_t", + "pid_t", + "mode_t", + "ssize_t", + ], - operators: [ - "=", - ">", - "<", - "!", - "~", - "?", - ":", - "==", - "<=", - ">=", - "!=", - "&&", - "||", - "++", - "--", - "+", - "-", - "*", - "/", - "&", - "|", - "^", - "%", - "<<", - ">>", - ">>>", - "+=", - "-=", - "*=", - "/=", - "&=", - "|=", - "^=", - "%=", - "<<=", - ">>=", - ">>>=", - ], + operators: [ + "=", + ">", + "<", + "!", + "~", + "?", + ":", + "==", + "<=", + ">=", + "!=", + "&&", + "||", + "++", + "--", + "+", + "-", + "*", + "/", + "&", + "|", + "^", + "%", + "<<", + ">>", + ">>>", + "+=", + "-=", + "*=", + "/=", + "&=", + "|=", + "^=", + "%=", + "<<=", + ">>=", + ">>>=", + ], - // we include these common regular expressions - symbols: /[=>](?!@symbols)/, "@brackets"], - [/@symbols/, { cases: { "@operators": "operator", "@default": "" } }], + // delimiters and operators + [/[{}()\[\]]/, "@brackets"], + [/[<>](?!@symbols)/, "@brackets"], + [/@symbols/, { cases: { "@operators": "operator", "@default": "" } }], - // @ annotations. - // As an example, we emit a debugging log message on these tokens. - // Note: message are supressed during the first load -- change some lines to see them. - [ - /@\s*[a-zA-Z_\$][\w\$]*/, - { token: "annotation", log: "annotation token: $0" }, - ], + // @ annotations. + // As an example, we emit a debugging log message on these tokens. + // Note: message are supressed during the first load -- change some lines to see them. + [ + /@\s*[a-zA-Z_\$][\w\$]*/, + { token: "annotation", log: "annotation token: $0" }, + ], - // numbers - [/\d*\.\d+([eE][\-+]?\d+)?/, "number.float"], - [/0[xX][0-9a-fA-F]+/, "number.hex"], - [/\d+/, "number"], + // numbers + [/\d*\.\d+([eE][\-+]?\d+)?/, "number.float"], + [/0[xX][0-9a-fA-F]+/, "number.hex"], + [/\d+/, "number"], - // delimiter: after number because of .\d floats - [/[;,.]/, "delimiter"], + // delimiter: after number because of .\d floats + [/[;,.]/, "delimiter"], - // strings - [/"([^"\\]|\\.)*$/, "string.invalid"], // non-teminated string - [/"/, { token: "string.quote", bracket: "@open", next: "@string" }], + // strings + [/"([^"\\]|\\.)*$/, "string.invalid"], // non-teminated string + [/"/, { token: "string.quote", bracket: "@open", next: "@string" }], - // characters - [/'[^\\']'/, "string"], - [/(')(@escapes)(')/, ["string", "string.escape", "string"]], - [/'/, "string.invalid"], - ], + // characters + [/'[^\\']'/, "string"], + [/(')(@escapes)(')/, ["string", "string.escape", "string"]], + [/'/, "string.invalid"], + ], - comment: [ - [/[^\/*]+/, "comment"], - [/\/\*/, "comment", "@push"], // nested comment - ["\\*/", "comment", "@pop"], - [/[\/*]/, "comment"], - ], + comment: [ + [/[^\/*]+/, "comment"], + [/\/\*/, "comment", "@push"], // nested comment + ["\\*/", "comment", "@pop"], + [/[\/*]/, "comment"], + ], - string: [ - [/[^\\"]+/, "string"], - [/@escapes/, "string.escape"], - [/\\./, "string.escape.invalid"], - [/"/, { token: "string.quote", bracket: "@close", next: "@pop" }], - ], + string: [ + [/[^\\"]+/, "string"], + [/@escapes/, "string.escape"], + [/\\./, "string.escape.invalid"], + [/"/, { token: "string.quote", bracket: "@close", next: "@pop" }], + ], - whitespace: [ - [/[ \t\r\n]+/, "white"], - [/\/\*/, "comment", "@comment"], - [/\/\/.*$/, "comment"], - ], + whitespace: [ + [/[ \t\r\n]+/, "white"], + [/\/\*/, "comment", "@comment"], + [/\/\/.*$/, "comment"], + ], - func_decl: [[/[a-z_$][\w$]*/, "support.function", "@pop"]], - }, + func_decl: [[/[a-z_$][\w$]*/, "support.function", "@pop"]], + }, }; diff --git a/libs/remix-ui/editor/src/lib/web-types.ts b/libs/remix-ui/editor/src/lib/web-types.ts index dd70a43bc0..3664e1eef4 100644 --- a/libs/remix-ui/editor/src/lib/web-types.ts +++ b/libs/remix-ui/editor/src/lib/web-types.ts @@ -2,240 +2,240 @@ import { remixTypes } from './remix-plugin-types' import { hardhatEthersExtension } from './hardhat-ethers-extension' export const loadTypes = async (monaco) => { - // ethers.js - - // @ts-ignore - const ethersAbi = await import('raw-loader!@ethersproject/abi/lib/index.d.ts') - const ethersAbiDefault = ethersAbi.default.replace(/@ethersproject\//g, '@ethersproject_') - monaco.languages.typescript.typescriptDefaults.addExtraLib(ethersAbiDefault, `file:///node_modules/@types/@ethersproject_abi/index.d.ts`) - - // @ts-ignore - const ethersAbstract = await import('raw-loader!@ethersproject/abstract-provider/lib/index.d.ts') - const ethersAbstractDefault = ethersAbstract.default.replace(/@ethersproject\//g, '@ethersproject_') - monaco.languages.typescript.typescriptDefaults.addExtraLib(ethersAbstractDefault, `file:///node_modules/@types/@ethersproject_abstract-provider/index.d.ts`) - - // @ts-ignore - const ethersSigner = await import('raw-loader!@ethersproject/abstract-signer/lib/index.d.ts') - const ethersSignerDefault = ethersSigner.default.replace(/@ethersproject\//g, '@ethersproject_') - monaco.languages.typescript.typescriptDefaults.addExtraLib(ethersSignerDefault, `file:///node_modules/@types/@ethersproject_abstract-signer/index.d.ts`) - - // @ts-ignore - const ethersAddress = await import('raw-loader!@ethersproject/address/lib/index.d.ts') - const ethersAddressDefault = ethersAddress.default.replace(/@ethersproject\//g, '@ethersproject_') - monaco.languages.typescript.typescriptDefaults.addExtraLib(ethersAddressDefault, `file:///node_modules/@types/@ethersproject_address/index.d.ts`) - - // @ts-ignore - const ethersBase64 = await import('raw-loader!@ethersproject/base64/lib/index.d.ts') - const ethersBase64Default = ethersBase64.default.replace(/@ethersproject\//g, '@ethersproject_') - monaco.languages.typescript.typescriptDefaults.addExtraLib(ethersBase64Default, `file:///node_modules/@types/@ethersproject_base64/index.d.ts`) + // ethers.js + + // @ts-ignore + const ethersAbi = await import('raw-loader!@ethersproject/abi/lib/index.d.ts') + const ethersAbiDefault = ethersAbi.default.replace(/@ethersproject\//g, '@ethersproject_') + monaco.languages.typescript.typescriptDefaults.addExtraLib(ethersAbiDefault, `file:///node_modules/@types/@ethersproject_abi/index.d.ts`) + + // @ts-ignore + const ethersAbstract = await import('raw-loader!@ethersproject/abstract-provider/lib/index.d.ts') + const ethersAbstractDefault = ethersAbstract.default.replace(/@ethersproject\//g, '@ethersproject_') + monaco.languages.typescript.typescriptDefaults.addExtraLib(ethersAbstractDefault, `file:///node_modules/@types/@ethersproject_abstract-provider/index.d.ts`) + + // @ts-ignore + const ethersSigner = await import('raw-loader!@ethersproject/abstract-signer/lib/index.d.ts') + const ethersSignerDefault = ethersSigner.default.replace(/@ethersproject\//g, '@ethersproject_') + monaco.languages.typescript.typescriptDefaults.addExtraLib(ethersSignerDefault, `file:///node_modules/@types/@ethersproject_abstract-signer/index.d.ts`) + + // @ts-ignore + const ethersAddress = await import('raw-loader!@ethersproject/address/lib/index.d.ts') + const ethersAddressDefault = ethersAddress.default.replace(/@ethersproject\//g, '@ethersproject_') + monaco.languages.typescript.typescriptDefaults.addExtraLib(ethersAddressDefault, `file:///node_modules/@types/@ethersproject_address/index.d.ts`) + + // @ts-ignore + const ethersBase64 = await import('raw-loader!@ethersproject/base64/lib/index.d.ts') + const ethersBase64Default = ethersBase64.default.replace(/@ethersproject\//g, '@ethersproject_') + monaco.languages.typescript.typescriptDefaults.addExtraLib(ethersBase64Default, `file:///node_modules/@types/@ethersproject_base64/index.d.ts`) - // @ts-ignore - const ethersBasex = await import('raw-loader!@ethersproject/basex/lib/index.d.ts') - const ethersBasexDefault = ethersBasex.default.replace(/@ethersproject\//g, '@ethersproject_') - monaco.languages.typescript.typescriptDefaults.addExtraLib(ethersBasexDefault, `file:///node_modules/@types/@ethersproject_basex/index.d.ts`) - - // @ts-ignore - const ethersBignumber = await import('raw-loader!@ethersproject/bignumber/lib/index.d.ts') - const ethersBignumberDefault = ethersBignumber.default.replace(/@ethersproject\//g, '@ethersproject_') - monaco.languages.typescript.typescriptDefaults.addExtraLib(ethersBignumberDefault, `file:///node_modules/@types/@ethersproject_bignumber/index.d.ts`) - - // @ts-ignore - const ethersBytes = await import('raw-loader!@ethersproject/bytes/lib/index.d.ts') - const ethersBytesDefault = ethersBytes.default.replace(/@ethersproject\//g, '@ethersproject_') - monaco.languages.typescript.typescriptDefaults.addExtraLib(ethersBytesDefault, `file:///node_modules/@types/@ethersproject_bytes/index.d.ts`) - - // @ts-ignore - const ethersConstants = await import('raw-loader!@ethersproject/constants/lib/index.d.ts') - const ethersConstantsDefault = ethersConstants.default.replace(/@ethersproject\//g, '@ethersproject_') - monaco.languages.typescript.typescriptDefaults.addExtraLib(ethersConstantsDefault, `file:///node_modules/@types/@ethersproject_constants/index.d.ts`) - - // @ts-ignore - const ethersContracts = await import('raw-loader!@ethersproject/contracts/lib/index.d.ts') - const ethersContractsDefault = ethersContracts.default.replace(/@ethersproject\//g, '@ethersproject_') - monaco.languages.typescript.typescriptDefaults.addExtraLib(ethersContractsDefault, `file:///node_modules/@types/@ethersproject_contracts/index.d.ts`) - - // @ts-ignore - const ethersHash = await import('raw-loader!@ethersproject/hash/lib/index.d.ts') - const ethersHashDefault = ethersHash.default.replace(/@ethersproject\//g, '@ethersproject_') - monaco.languages.typescript.typescriptDefaults.addExtraLib(ethersHashDefault, `file:///node_modules/@types/@ethersproject_hash/index.d.ts`) - - // @ts-ignore - const ethersHdnode = await import('raw-loader!@ethersproject/hdnode/lib/index.d.ts') - const ethersHdnodeDefault = ethersHdnode.default.replace(/@ethersproject\//g, '@ethersproject_') - monaco.languages.typescript.typescriptDefaults.addExtraLib(ethersHdnodeDefault, `file:///node_modules/@types/@ethersproject_hdnode/index.d.ts`) - - // @ts-ignore - const ethersJsonWallets = await import('raw-loader!@ethersproject/json-wallets/lib/index.d.ts') - const ethersJsonWalletsDefault = ethersJsonWallets.default.replace(/@ethersproject\//g, '@ethersproject_') - monaco.languages.typescript.typescriptDefaults.addExtraLib(ethersJsonWalletsDefault, `file:///node_modules/@types/@ethersproject_json-wallets/index.d.ts`) - - // @ts-ignore - const ethersKeccak256 = await import('raw-loader!@ethersproject/keccak256/lib/index.d.ts') - const ethersKeccak256Default = ethersKeccak256.default.replace(/@ethersproject\//g, '@ethersproject_') - monaco.languages.typescript.typescriptDefaults.addExtraLib(ethersKeccak256Default, `file:///node_modules/@types/@ethersproject_keccak256/index.d.ts`) - - // @ts-ignore - const ethersLogger = await import('raw-loader!@ethersproject/logger/lib/index.d.ts') - const ethersLoggerDefault = ethersLogger.default.replace(/@ethersproject\//g, '@ethersproject_') - monaco.languages.typescript.typescriptDefaults.addExtraLib(ethersLoggerDefault, `file:///node_modules/@types/@ethersproject_logger/index.d.ts`) - - // @ts-ignore - const ethersNetworks = await import('raw-loader!@ethersproject/networks/lib/index.d.ts') - const ethersNetworksDefault = ethersNetworks.default.replace(/@ethersproject\//g, '@ethersproject_') - monaco.languages.typescript.typescriptDefaults.addExtraLib(ethersNetworksDefault, `file:///node_modules/@types/@ethersproject_networks/index.d.ts`) - - // @ts-ignore - const ethersPbkdf2 = await import('raw-loader!@ethersproject/pbkdf2/lib/index.d.ts') - const ethersPbkdf2Default = ethersPbkdf2.default.replace(/@ethersproject\//g, '@ethersproject_') - monaco.languages.typescript.typescriptDefaults.addExtraLib(ethersPbkdf2Default, `file:///node_modules/@types/@ethersproject_pbkdf2/index.d.ts`) - - // @ts-ignore - const ethersProperties = await import('raw-loader!@ethersproject/properties/lib/index.d.ts') - const ethersPropertiesDefault = ethersProperties.default.replace(/@ethersproject\//g, '@ethersproject_') - monaco.languages.typescript.typescriptDefaults.addExtraLib(ethersPropertiesDefault, `file:///node_modules/@types/@ethersproject_properties/index.d.ts`) - - // @ts-ignore - const ethersProviders = await import('raw-loader!@ethersproject/providers/lib/index.d.ts') - const ethersProvidersDefault = ethersProviders.default.replace(/@ethersproject\//g, '@ethersproject_') - monaco.languages.typescript.typescriptDefaults.addExtraLib(ethersProvidersDefault, `file:///node_modules/@types/@ethersproject_providers/index.d.ts`) - - // @ts-ignore - const ethersRandom = await import('raw-loader!@ethersproject/random/lib/index.d.ts') - const ethersRandomDefault = ethersRandom.default.replace(/@ethersproject\//g, '@ethersproject_') - monaco.languages.typescript.typescriptDefaults.addExtraLib(ethersRandomDefault, `file:///node_modules/@types/@ethersproject_random/index.d.ts`) - - // @ts-ignore - const ethersRlp = await import('raw-loader!@ethersproject/rlp/lib/index.d.ts') - const ethersRlpDefault = ethersRlp.default.replace(/@ethersproject\//g, '@ethersproject_') - monaco.languages.typescript.typescriptDefaults.addExtraLib(ethersRlpDefault, `file:///node_modules/@types/@ethersproject_rlp/index.d.ts`) - - // @ts-ignore - const ethersSha2 = await import('raw-loader!@ethersproject/sha2/lib/index.d.ts') - const ethersSha2Default = ethersSha2.default.replace(/@ethersproject\//g, '@ethersproject_') - monaco.languages.typescript.typescriptDefaults.addExtraLib(ethersSha2Default, `file:///node_modules/@types/@ethersproject_sha2/index.d.ts`) - - // @ts-ignore - const ethersSingningkey = await import('raw-loader!@ethersproject/signing-key/lib/index.d.ts') - const ethersSingningkeyDefault = ethersSingningkey.default.replace(/@ethersproject\//g, '@ethersproject_') - monaco.languages.typescript.typescriptDefaults.addExtraLib(ethersSingningkeyDefault, `file:///node_modules/@types/@ethersproject_signing-key/index.d.ts`) - - // @ts-ignore - const ethersSolidity = await import('raw-loader!@ethersproject/solidity/lib/index.d.ts') - const ethersSolidityDefault = ethersSolidity.default.replace(/@ethersproject\//g, '@ethersproject_') - monaco.languages.typescript.typescriptDefaults.addExtraLib(ethersSolidityDefault, `file:///node_modules/@types/@ethersproject_solidity/index.d.ts`) - - // @ts-ignore - const ethersStrings = await import('raw-loader!@ethersproject/strings/lib/index.d.ts') - const ethersStringsDefault = ethersStrings.default.replace(/@ethersproject\//g, '@ethersproject_') - monaco.languages.typescript.typescriptDefaults.addExtraLib(ethersStringsDefault, `file:///node_modules/@types/@ethersproject_strings/index.d.ts`) - - // @ts-ignore - const ethersTransactions = await import('raw-loader!@ethersproject/transactions/lib/index.d.ts') - const ethersTransactionsDefault = ethersTransactions.default.replace(/@ethersproject\//g, '@ethersproject_') - monaco.languages.typescript.typescriptDefaults.addExtraLib(ethersTransactionsDefault, `file:///node_modules/@types/@ethersproject_transactions/index.d.ts`) - - // @ts-ignore - const ethersUnits = await import('raw-loader!@ethersproject/units/lib/index.d.ts') - const ethersUnitsDefault = ethersUnits.default.replace(/@ethersproject\//g, '@ethersproject_') - monaco.languages.typescript.typescriptDefaults.addExtraLib(ethersUnitsDefault, `file:///node_modules/@types/@ethersproject_units/index.d.ts`) - - // @ts-ignore - const ethersWallet = await import('raw-loader!@ethersproject/wallet/lib/index.d.ts') - const ethersWalletDefault = ethersWallet.default.replace(/@ethersproject\//g, '@ethersproject_') - monaco.languages.typescript.typescriptDefaults.addExtraLib(ethersWalletDefault, `file:///node_modules/@types/@ethersproject_wallet/index.d.ts`) - - // @ts-ignore - const ethersWeb = await import('raw-loader!@ethersproject/web/lib/index.d.ts') - const ethersWebDefault = ethersWeb.default.replace(/@ethersproject\//g, '@ethersproject_') - monaco.languages.typescript.typescriptDefaults.addExtraLib(ethersWebDefault, `file:///node_modules/@types/@ethersproject_web/index.d.ts`) - - // @ts-ignore - const ethersWordlists = await import('raw-loader!@ethersproject/wordlists/lib/index.d.ts') - const ethersWordlistsDefault = ethersWordlists.default.replace(/@ethersproject\//g, '@ethersproject_') - monaco.languages.typescript.typescriptDefaults.addExtraLib(ethersWordlistsDefault, `file:///node_modules/@types/@ethersproject_wordlists/index.d.ts`) - - // @ts-ignore - const versionEthers = await import('raw-loader!ethers/lib/_version.d.ts') - const versionEthersDefault = versionEthers.default.replace(/@ethersproject\//g, '@ethersproject_') - monaco.languages.typescript.typescriptDefaults.addExtraLib(versionEthersDefault, `file:///node_modules/@types/_version-ethers-lib/index.d.ts`) - - // @ts-ignore - const utilEthers = await import('raw-loader!ethers/lib/utils.d.ts') - const utilEthersDefault = utilEthers.default.replace(/@ethersproject\//g, '@ethersproject_') - monaco.languages.typescript.typescriptDefaults.addExtraLib(utilEthersDefault, `file:///node_modules/@types/utils-ethers-lib/index.d.ts`) - - // @ts-ignore - const ethers = await import('raw-loader!ethers/lib/ethers.d.ts') - let ethersDefault = ethers.default - ethersDefault = ethersDefault.replace(/.\/utils/g, 'utils-ethers-lib') - ethersDefault = ethersDefault.replace(/.\/_version/g, '_version-ethers-lib') - ethersDefault = ethersDefault.replace(/.\/ethers/g, 'ethers-lib') - ethersDefault = ethersDefault.replace(/@ethersproject\//g, '@ethersproject_') - ethersDefault = ethersDefault + '\n' + hardhatEthersExtension - monaco.languages.typescript.typescriptDefaults.addExtraLib(ethersDefault, `file:///node_modules/@types/ethers-lib/index.d.ts`) - - // @ts-ignore - const indexEthers = await import('raw-loader!ethers/lib/index.d.ts') - let indexEthersDefault = indexEthers.default - indexEthersDefault = indexEthersDefault.replace(/.\/ethers/g, 'ethers-lib') - indexEthersDefault = indexEthersDefault.replace(/@ethersproject\//g, '@ethersproject_') - monaco.languages.typescript.typescriptDefaults.addExtraLib(indexEthersDefault, `file:///node_modules/@types/ethers/index.d.ts`) - - // Web3 - - // @ts-ignore - const indexWeb3 = await import('raw-loader!web3/types/index.d.ts') - monaco.languages.typescript.typescriptDefaults.addExtraLib(indexWeb3.default, `file:///node_modules/@types/web3/index.d.ts`) - - // @ts-ignore - const indexWeb3Bzz = await import('raw-loader!web3-bzz/types/index.d.ts') - monaco.languages.typescript.typescriptDefaults.addExtraLib(indexWeb3Bzz.default, `file:///node_modules/@types/web3-bzz/index.d.ts`) - - // @ts-ignore - const indexWeb3Core = await import('raw-loader!web3-core/types/index.d.ts') - monaco.languages.typescript.typescriptDefaults.addExtraLib(indexWeb3Core.default, `file:///node_modules/@types/web3-core/index.d.ts`) - - // @ts-ignore - const indexWeb3Eth = await import('raw-loader!web3-eth/types/index.d.ts') - monaco.languages.typescript.typescriptDefaults.addExtraLib(indexWeb3Eth.default, `file:///node_modules/@types/web3-eth/index.d.ts`) - - // @ts-ignore - const indexWeb3Personal = await import('raw-loader!web3-eth-personal/types/index.d.ts') - monaco.languages.typescript.typescriptDefaults.addExtraLib(indexWeb3Personal.default, `file:///node_modules/@types/web3-eth-personal/index.d.ts`) - - // @ts-ignore - const indexWeb3Contract = await import('raw-loader!web3-eth-contract/types/index.d.ts') - monaco.languages.typescript.typescriptDefaults.addExtraLib(indexWeb3Contract.default, `file:///node_modules/@types/web3-eth-contract/index.d.ts`) - - // @ts-ignore - const indexWeb3Net = await import('raw-loader!web3-net/types/index.d.ts') - monaco.languages.typescript.typescriptDefaults.addExtraLib(indexWeb3Net.default, `file:///node_modules/@types/web3-net/index.d.ts`) - - // @ts-ignore - const indexWeb3Shh = await import('raw-loader!web3-shh/types/index.d.ts') - monaco.languages.typescript.typescriptDefaults.addExtraLib(indexWeb3Shh.default, `file:///node_modules/@types/web3-shh/index.d.ts`) - - // @ts-ignore - const indexWeb3Util = await import('raw-loader!web3-utils/types/index.d.ts') - monaco.languages.typescript.typescriptDefaults.addExtraLib(indexWeb3Util.default, `file:///node_modules/@types/web3-utils/index.d.ts`) - // remix - const indexRemixApi = remixTypes + `\n + // @ts-ignore + const ethersBasex = await import('raw-loader!@ethersproject/basex/lib/index.d.ts') + const ethersBasexDefault = ethersBasex.default.replace(/@ethersproject\//g, '@ethersproject_') + monaco.languages.typescript.typescriptDefaults.addExtraLib(ethersBasexDefault, `file:///node_modules/@types/@ethersproject_basex/index.d.ts`) + + // @ts-ignore + const ethersBignumber = await import('raw-loader!@ethersproject/bignumber/lib/index.d.ts') + const ethersBignumberDefault = ethersBignumber.default.replace(/@ethersproject\//g, '@ethersproject_') + monaco.languages.typescript.typescriptDefaults.addExtraLib(ethersBignumberDefault, `file:///node_modules/@types/@ethersproject_bignumber/index.d.ts`) + + // @ts-ignore + const ethersBytes = await import('raw-loader!@ethersproject/bytes/lib/index.d.ts') + const ethersBytesDefault = ethersBytes.default.replace(/@ethersproject\//g, '@ethersproject_') + monaco.languages.typescript.typescriptDefaults.addExtraLib(ethersBytesDefault, `file:///node_modules/@types/@ethersproject_bytes/index.d.ts`) + + // @ts-ignore + const ethersConstants = await import('raw-loader!@ethersproject/constants/lib/index.d.ts') + const ethersConstantsDefault = ethersConstants.default.replace(/@ethersproject\//g, '@ethersproject_') + monaco.languages.typescript.typescriptDefaults.addExtraLib(ethersConstantsDefault, `file:///node_modules/@types/@ethersproject_constants/index.d.ts`) + + // @ts-ignore + const ethersContracts = await import('raw-loader!@ethersproject/contracts/lib/index.d.ts') + const ethersContractsDefault = ethersContracts.default.replace(/@ethersproject\//g, '@ethersproject_') + monaco.languages.typescript.typescriptDefaults.addExtraLib(ethersContractsDefault, `file:///node_modules/@types/@ethersproject_contracts/index.d.ts`) + + // @ts-ignore + const ethersHash = await import('raw-loader!@ethersproject/hash/lib/index.d.ts') + const ethersHashDefault = ethersHash.default.replace(/@ethersproject\//g, '@ethersproject_') + monaco.languages.typescript.typescriptDefaults.addExtraLib(ethersHashDefault, `file:///node_modules/@types/@ethersproject_hash/index.d.ts`) + + // @ts-ignore + const ethersHdnode = await import('raw-loader!@ethersproject/hdnode/lib/index.d.ts') + const ethersHdnodeDefault = ethersHdnode.default.replace(/@ethersproject\//g, '@ethersproject_') + monaco.languages.typescript.typescriptDefaults.addExtraLib(ethersHdnodeDefault, `file:///node_modules/@types/@ethersproject_hdnode/index.d.ts`) + + // @ts-ignore + const ethersJsonWallets = await import('raw-loader!@ethersproject/json-wallets/lib/index.d.ts') + const ethersJsonWalletsDefault = ethersJsonWallets.default.replace(/@ethersproject\//g, '@ethersproject_') + monaco.languages.typescript.typescriptDefaults.addExtraLib(ethersJsonWalletsDefault, `file:///node_modules/@types/@ethersproject_json-wallets/index.d.ts`) + + // @ts-ignore + const ethersKeccak256 = await import('raw-loader!@ethersproject/keccak256/lib/index.d.ts') + const ethersKeccak256Default = ethersKeccak256.default.replace(/@ethersproject\//g, '@ethersproject_') + monaco.languages.typescript.typescriptDefaults.addExtraLib(ethersKeccak256Default, `file:///node_modules/@types/@ethersproject_keccak256/index.d.ts`) + + // @ts-ignore + const ethersLogger = await import('raw-loader!@ethersproject/logger/lib/index.d.ts') + const ethersLoggerDefault = ethersLogger.default.replace(/@ethersproject\//g, '@ethersproject_') + monaco.languages.typescript.typescriptDefaults.addExtraLib(ethersLoggerDefault, `file:///node_modules/@types/@ethersproject_logger/index.d.ts`) + + // @ts-ignore + const ethersNetworks = await import('raw-loader!@ethersproject/networks/lib/index.d.ts') + const ethersNetworksDefault = ethersNetworks.default.replace(/@ethersproject\//g, '@ethersproject_') + monaco.languages.typescript.typescriptDefaults.addExtraLib(ethersNetworksDefault, `file:///node_modules/@types/@ethersproject_networks/index.d.ts`) + + // @ts-ignore + const ethersPbkdf2 = await import('raw-loader!@ethersproject/pbkdf2/lib/index.d.ts') + const ethersPbkdf2Default = ethersPbkdf2.default.replace(/@ethersproject\//g, '@ethersproject_') + monaco.languages.typescript.typescriptDefaults.addExtraLib(ethersPbkdf2Default, `file:///node_modules/@types/@ethersproject_pbkdf2/index.d.ts`) + + // @ts-ignore + const ethersProperties = await import('raw-loader!@ethersproject/properties/lib/index.d.ts') + const ethersPropertiesDefault = ethersProperties.default.replace(/@ethersproject\//g, '@ethersproject_') + monaco.languages.typescript.typescriptDefaults.addExtraLib(ethersPropertiesDefault, `file:///node_modules/@types/@ethersproject_properties/index.d.ts`) + + // @ts-ignore + const ethersProviders = await import('raw-loader!@ethersproject/providers/lib/index.d.ts') + const ethersProvidersDefault = ethersProviders.default.replace(/@ethersproject\//g, '@ethersproject_') + monaco.languages.typescript.typescriptDefaults.addExtraLib(ethersProvidersDefault, `file:///node_modules/@types/@ethersproject_providers/index.d.ts`) + + // @ts-ignore + const ethersRandom = await import('raw-loader!@ethersproject/random/lib/index.d.ts') + const ethersRandomDefault = ethersRandom.default.replace(/@ethersproject\//g, '@ethersproject_') + monaco.languages.typescript.typescriptDefaults.addExtraLib(ethersRandomDefault, `file:///node_modules/@types/@ethersproject_random/index.d.ts`) + + // @ts-ignore + const ethersRlp = await import('raw-loader!@ethersproject/rlp/lib/index.d.ts') + const ethersRlpDefault = ethersRlp.default.replace(/@ethersproject\//g, '@ethersproject_') + monaco.languages.typescript.typescriptDefaults.addExtraLib(ethersRlpDefault, `file:///node_modules/@types/@ethersproject_rlp/index.d.ts`) + + // @ts-ignore + const ethersSha2 = await import('raw-loader!@ethersproject/sha2/lib/index.d.ts') + const ethersSha2Default = ethersSha2.default.replace(/@ethersproject\//g, '@ethersproject_') + monaco.languages.typescript.typescriptDefaults.addExtraLib(ethersSha2Default, `file:///node_modules/@types/@ethersproject_sha2/index.d.ts`) + + // @ts-ignore + const ethersSingningkey = await import('raw-loader!@ethersproject/signing-key/lib/index.d.ts') + const ethersSingningkeyDefault = ethersSingningkey.default.replace(/@ethersproject\//g, '@ethersproject_') + monaco.languages.typescript.typescriptDefaults.addExtraLib(ethersSingningkeyDefault, `file:///node_modules/@types/@ethersproject_signing-key/index.d.ts`) + + // @ts-ignore + const ethersSolidity = await import('raw-loader!@ethersproject/solidity/lib/index.d.ts') + const ethersSolidityDefault = ethersSolidity.default.replace(/@ethersproject\//g, '@ethersproject_') + monaco.languages.typescript.typescriptDefaults.addExtraLib(ethersSolidityDefault, `file:///node_modules/@types/@ethersproject_solidity/index.d.ts`) + + // @ts-ignore + const ethersStrings = await import('raw-loader!@ethersproject/strings/lib/index.d.ts') + const ethersStringsDefault = ethersStrings.default.replace(/@ethersproject\//g, '@ethersproject_') + monaco.languages.typescript.typescriptDefaults.addExtraLib(ethersStringsDefault, `file:///node_modules/@types/@ethersproject_strings/index.d.ts`) + + // @ts-ignore + const ethersTransactions = await import('raw-loader!@ethersproject/transactions/lib/index.d.ts') + const ethersTransactionsDefault = ethersTransactions.default.replace(/@ethersproject\//g, '@ethersproject_') + monaco.languages.typescript.typescriptDefaults.addExtraLib(ethersTransactionsDefault, `file:///node_modules/@types/@ethersproject_transactions/index.d.ts`) + + // @ts-ignore + const ethersUnits = await import('raw-loader!@ethersproject/units/lib/index.d.ts') + const ethersUnitsDefault = ethersUnits.default.replace(/@ethersproject\//g, '@ethersproject_') + monaco.languages.typescript.typescriptDefaults.addExtraLib(ethersUnitsDefault, `file:///node_modules/@types/@ethersproject_units/index.d.ts`) + + // @ts-ignore + const ethersWallet = await import('raw-loader!@ethersproject/wallet/lib/index.d.ts') + const ethersWalletDefault = ethersWallet.default.replace(/@ethersproject\//g, '@ethersproject_') + monaco.languages.typescript.typescriptDefaults.addExtraLib(ethersWalletDefault, `file:///node_modules/@types/@ethersproject_wallet/index.d.ts`) + + // @ts-ignore + const ethersWeb = await import('raw-loader!@ethersproject/web/lib/index.d.ts') + const ethersWebDefault = ethersWeb.default.replace(/@ethersproject\//g, '@ethersproject_') + monaco.languages.typescript.typescriptDefaults.addExtraLib(ethersWebDefault, `file:///node_modules/@types/@ethersproject_web/index.d.ts`) + + // @ts-ignore + const ethersWordlists = await import('raw-loader!@ethersproject/wordlists/lib/index.d.ts') + const ethersWordlistsDefault = ethersWordlists.default.replace(/@ethersproject\//g, '@ethersproject_') + monaco.languages.typescript.typescriptDefaults.addExtraLib(ethersWordlistsDefault, `file:///node_modules/@types/@ethersproject_wordlists/index.d.ts`) + + // @ts-ignore + const versionEthers = await import('raw-loader!ethers/lib/_version.d.ts') + const versionEthersDefault = versionEthers.default.replace(/@ethersproject\//g, '@ethersproject_') + monaco.languages.typescript.typescriptDefaults.addExtraLib(versionEthersDefault, `file:///node_modules/@types/_version-ethers-lib/index.d.ts`) + + // @ts-ignore + const utilEthers = await import('raw-loader!ethers/lib/utils.d.ts') + const utilEthersDefault = utilEthers.default.replace(/@ethersproject\//g, '@ethersproject_') + monaco.languages.typescript.typescriptDefaults.addExtraLib(utilEthersDefault, `file:///node_modules/@types/utils-ethers-lib/index.d.ts`) + + // @ts-ignore + const ethers = await import('raw-loader!ethers/lib/ethers.d.ts') + let ethersDefault = ethers.default + ethersDefault = ethersDefault.replace(/.\/utils/g, 'utils-ethers-lib') + ethersDefault = ethersDefault.replace(/.\/_version/g, '_version-ethers-lib') + ethersDefault = ethersDefault.replace(/.\/ethers/g, 'ethers-lib') + ethersDefault = ethersDefault.replace(/@ethersproject\//g, '@ethersproject_') + ethersDefault = ethersDefault + '\n' + hardhatEthersExtension + monaco.languages.typescript.typescriptDefaults.addExtraLib(ethersDefault, `file:///node_modules/@types/ethers-lib/index.d.ts`) + + // @ts-ignore + const indexEthers = await import('raw-loader!ethers/lib/index.d.ts') + let indexEthersDefault = indexEthers.default + indexEthersDefault = indexEthersDefault.replace(/.\/ethers/g, 'ethers-lib') + indexEthersDefault = indexEthersDefault.replace(/@ethersproject\//g, '@ethersproject_') + monaco.languages.typescript.typescriptDefaults.addExtraLib(indexEthersDefault, `file:///node_modules/@types/ethers/index.d.ts`) + + // Web3 + + // @ts-ignore + const indexWeb3 = await import('raw-loader!web3/types/index.d.ts') + monaco.languages.typescript.typescriptDefaults.addExtraLib(indexWeb3.default, `file:///node_modules/@types/web3/index.d.ts`) + + // @ts-ignore + const indexWeb3Bzz = await import('raw-loader!web3-bzz/types/index.d.ts') + monaco.languages.typescript.typescriptDefaults.addExtraLib(indexWeb3Bzz.default, `file:///node_modules/@types/web3-bzz/index.d.ts`) + + // @ts-ignore + const indexWeb3Core = await import('raw-loader!web3-core/types/index.d.ts') + monaco.languages.typescript.typescriptDefaults.addExtraLib(indexWeb3Core.default, `file:///node_modules/@types/web3-core/index.d.ts`) + + // @ts-ignore + const indexWeb3Eth = await import('raw-loader!web3-eth/types/index.d.ts') + monaco.languages.typescript.typescriptDefaults.addExtraLib(indexWeb3Eth.default, `file:///node_modules/@types/web3-eth/index.d.ts`) + + // @ts-ignore + const indexWeb3Personal = await import('raw-loader!web3-eth-personal/types/index.d.ts') + monaco.languages.typescript.typescriptDefaults.addExtraLib(indexWeb3Personal.default, `file:///node_modules/@types/web3-eth-personal/index.d.ts`) + + // @ts-ignore + const indexWeb3Contract = await import('raw-loader!web3-eth-contract/types/index.d.ts') + monaco.languages.typescript.typescriptDefaults.addExtraLib(indexWeb3Contract.default, `file:///node_modules/@types/web3-eth-contract/index.d.ts`) + + // @ts-ignore + const indexWeb3Net = await import('raw-loader!web3-net/types/index.d.ts') + monaco.languages.typescript.typescriptDefaults.addExtraLib(indexWeb3Net.default, `file:///node_modules/@types/web3-net/index.d.ts`) + + // @ts-ignore + const indexWeb3Shh = await import('raw-loader!web3-shh/types/index.d.ts') + monaco.languages.typescript.typescriptDefaults.addExtraLib(indexWeb3Shh.default, `file:///node_modules/@types/web3-shh/index.d.ts`) + + // @ts-ignore + const indexWeb3Util = await import('raw-loader!web3-utils/types/index.d.ts') + monaco.languages.typescript.typescriptDefaults.addExtraLib(indexWeb3Util.default, `file:///node_modules/@types/web3-utils/index.d.ts`) + // remix + const indexRemixApi = remixTypes + `\n declare global { const remix: PluginClient; const web3Provider; } ` - monaco.languages.typescript.typescriptDefaults.addExtraLib(indexRemixApi) + monaco.languages.typescript.typescriptDefaults.addExtraLib(indexRemixApi) - // @ts-ignore - const chaiType = await import('raw-loader!@types/chai/index.d.ts') - monaco.languages.typescript.typescriptDefaults.addExtraLib(chaiType.default, `file:///node_modules/@types/chai/index.d.ts`) + // @ts-ignore + const chaiType = await import('raw-loader!@types/chai/index.d.ts') + monaco.languages.typescript.typescriptDefaults.addExtraLib(chaiType.default, `file:///node_modules/@types/chai/index.d.ts`) - // @ts-ignore - const mochaType = await import('raw-loader!@types/mocha/index.d.ts') - monaco.languages.typescript.typescriptDefaults.addExtraLib(mochaType.default, `file:///node_modules/@types/mocha/index.d.ts`) + // @ts-ignore + const mochaType = await import('raw-loader!@types/mocha/index.d.ts') + monaco.languages.typescript.typescriptDefaults.addExtraLib(mochaType.default, `file:///node_modules/@types/mocha/index.d.ts`) - const loadedElement = document.createElement('span') - loadedElement.setAttribute('data-id', 'typesloaded') - document.body.appendChild(loadedElement) + const loadedElement = document.createElement('span') + loadedElement.setAttribute('data-id', 'typesloaded') + document.body.appendChild(loadedElement) } \ No newline at end of file diff --git a/libs/remix-ui/file-decorators/src/lib/components/file-decoration-icon.tsx b/libs/remix-ui/file-decorators/src/lib/components/file-decoration-icon.tsx index ba2e25fde9..5a81f09f88 100644 --- a/libs/remix-ui/file-decorators/src/lib/components/file-decoration-icon.tsx +++ b/libs/remix-ui/file-decorators/src/lib/components/file-decoration-icon.tsx @@ -13,35 +13,35 @@ export type fileDecorationProps = { } export const FileDecorationIcons = (props: fileDecorationProps) => { - const [states, setStates] = useState([]) - useEffect(() => { - setStates(props.fileDecorations.filter((fileDecoration) => fileDecoration.path === props.file.path || `${fileDecoration.workspace.name}/${fileDecoration.path}` === props.file.path)) - }, [props.fileDecorations]) + const [states, setStates] = useState([]) + useEffect(() => { + setStates(props.fileDecorations.filter((fileDecoration) => fileDecoration.path === props.file.path || `${fileDecoration.workspace.name}/${fileDecoration.path}` === props.file.path)) + }, [props.fileDecorations]) - const getTags = function () { - if (states && states.length) { - const elements: JSX.Element[] = [] + const getTags = function () { + if (states && states.length) { + const elements: JSX.Element[] = [] - for (const [index, state] of states.entries()) { - switch (state.fileStateType) { - case fileDecorationType.Error: - elements.push(}/>) - break - case fileDecorationType.Warning: - elements.push(}/>) - break - case fileDecorationType.Custom: - elements.push(}/>) - break - } - } - return elements + for (const [index, state] of states.entries()) { + switch (state.fileStateType) { + case fileDecorationType.Error: + elements.push(}/>) + break + case fileDecorationType.Warning: + elements.push(}/>) + break + case fileDecorationType.Custom: + elements.push(}/>) + break } + } + return elements } + } - return <> - {getTags()} - + return <> + {getTags()} + } export default FileDecorationIcons \ No newline at end of file diff --git a/libs/remix-ui/file-decorators/src/lib/components/filedecorationicons/file-decoration-custom-icon.tsx b/libs/remix-ui/file-decorators/src/lib/components/filedecorationicons/file-decoration-custom-icon.tsx index 80822b9237..6f55bda303 100644 --- a/libs/remix-ui/file-decorators/src/lib/components/filedecorationicons/file-decoration-custom-icon.tsx +++ b/libs/remix-ui/file-decorators/src/lib/components/filedecorationicons/file-decoration-custom-icon.tsx @@ -5,9 +5,9 @@ import { fileDecoration } from '../../types' const FileDecorationCustomIcon = (props: { fileDecoration: fileDecoration }) => { - return <> - <>{props.fileDecoration.fileStateIcon} - + return <> + <>{props.fileDecoration.fileStateIcon} + } export default FileDecorationCustomIcon \ No newline at end of file diff --git a/libs/remix-ui/file-decorators/src/lib/components/filedecorationicons/file-decoration-error-icon.tsx b/libs/remix-ui/file-decorators/src/lib/components/filedecorationicons/file-decoration-error-icon.tsx index 675db8f354..5c97055b70 100644 --- a/libs/remix-ui/file-decorators/src/lib/components/filedecorationicons/file-decoration-error-icon.tsx +++ b/libs/remix-ui/file-decorators/src/lib/components/filedecorationicons/file-decoration-error-icon.tsx @@ -6,9 +6,9 @@ import { fileDecoration } from '../../types' const FileDecorationErrorIcon = (props: { fileDecoration: fileDecoration }) => { - return <> - {props.fileDecoration.text} - + return <> + {props.fileDecoration.text} + } export default FileDecorationErrorIcon \ No newline at end of file diff --git a/libs/remix-ui/file-decorators/src/lib/components/filedecorationicons/file-decoration-tooltip.tsx b/libs/remix-ui/file-decorators/src/lib/components/filedecorationicons/file-decoration-tooltip.tsx index 31eda11fb8..f5289bf3b4 100644 --- a/libs/remix-ui/file-decorators/src/lib/components/filedecorationicons/file-decoration-tooltip.tsx +++ b/libs/remix-ui/file-decorators/src/lib/components/filedecorationicons/file-decoration-tooltip.tsx @@ -8,28 +8,28 @@ const FileDecorationTooltip = (props: { index: number }, ) => { - const getComments = function (fileDecoration: fileDecoration) { - if (fileDecoration.comment) { - const comments = Array.isArray(fileDecoration.comment) ? fileDecoration.comment : [fileDecoration.comment] - return comments.map((comment, index) => { - return
{comment}
- }) - } + const getComments = function (fileDecoration: fileDecoration) { + if (fileDecoration.comment) { + const comments = Array.isArray(fileDecoration.comment) ? fileDecoration.comment : [fileDecoration.comment] + return comments.map((comment, index) => { + return
{comment}
+ }) } + } - return - -
{getComments(props.fileDecoration)}
-
- - } - > -
{props.icon}
-
+ return + +
{getComments(props.fileDecoration)}
+
+ + } + > +
{props.icon}
+
} export default FileDecorationTooltip; \ No newline at end of file diff --git a/libs/remix-ui/file-decorators/src/lib/components/filedecorationicons/file-decoration-warning-icon.tsx b/libs/remix-ui/file-decorators/src/lib/components/filedecorationicons/file-decoration-warning-icon.tsx index 7be59ea224..4c71940e7f 100644 --- a/libs/remix-ui/file-decorators/src/lib/components/filedecorationicons/file-decoration-warning-icon.tsx +++ b/libs/remix-ui/file-decorators/src/lib/components/filedecorationicons/file-decoration-warning-icon.tsx @@ -5,7 +5,7 @@ import { fileDecoration } from '../../types' const FileDecorationWarningIcon = (props: { fileDecoration: fileDecoration }) => { - return <>{props.fileDecoration.text} + return <>{props.fileDecoration.text} } export default FileDecorationWarningIcon \ No newline at end of file diff --git a/libs/remix-ui/file-decorators/src/lib/helper/index.tsx b/libs/remix-ui/file-decorators/src/lib/helper/index.tsx index 215d926e89..5930299d02 100644 --- a/libs/remix-ui/file-decorators/src/lib/helper/index.tsx +++ b/libs/remix-ui/file-decorators/src/lib/helper/index.tsx @@ -2,10 +2,10 @@ import React from "react" import { fileDecoration } from "../types" export const getComments = function (fileDecoration: fileDecoration) { - if(fileDecoration.comment){ - const comments = Array.isArray(fileDecoration.comment) ? fileDecoration.comment : [fileDecoration.comment] - return comments.map((comment, index) => { - return
{comment}

- }) - } + if(fileDecoration.comment){ + const comments = Array.isArray(fileDecoration.comment) ? fileDecoration.comment : [fileDecoration.comment] + return comments.map((comment, index) => { + return
{comment}

+ }) + } } \ No newline at end of file diff --git a/libs/remix-ui/helper/src/lib/bleach.ts b/libs/remix-ui/helper/src/lib/bleach.ts index 44aa129042..5803cc0d7c 100644 --- a/libs/remix-ui/helper/src/lib/bleach.ts +++ b/libs/remix-ui/helper/src/lib/bleach.ts @@ -7,90 +7,90 @@ import * as he from 'he' export const bleach = { - matcher: /<\/?([a-zA-Z0-9]+)*(.*?)\/?>/igm, - - whitelist: [ - 'a', - 'b', - 'p', - 'em', - 'strong' - ], - - analyze: function (html) { - html = String(html) || '' - - const matches = [] - let match - - // extract all tags - while ((match = bleach.matcher.exec(html)) != null) { - const attrr = match[2].split(' ') - const attrs = [] - - // extract attributes from the tag - attrr.shift() - attrr.forEach((attr) => { - attr = attr.split('=') - const attrName = attr[0] - let attrValue = attr.length > 1 ? attr.slice(1).join('=') : null - // remove quotes from attributes - if (attrValue && attrValue.charAt(0).match(/'|"/)) attrValue = attrValue.slice(1) - if (attrValue && attrValue.charAt(attrValue.length - 1).match(/'|"/)) attrValue = attrValue.slice(0, -1) - attr = { - name: attrName, - value: attrValue - } - if (!attr.value) delete attr.value - if (attr.name) attrs.push(attr) - }) - - const tag = { - full: match[0], - name: match[1], - attr: attrs - } - - matches.push(tag) + matcher: /<\/?([a-zA-Z0-9]+)*(.*?)\/?>/igm, + + whitelist: [ + 'a', + 'b', + 'p', + 'em', + 'strong' + ], + + analyze: function (html) { + html = String(html) || '' + + const matches = [] + let match + + // extract all tags + while ((match = bleach.matcher.exec(html)) != null) { + const attrr = match[2].split(' ') + const attrs = [] + + // extract attributes from the tag + attrr.shift() + attrr.forEach((attr) => { + attr = attr.split('=') + const attrName = attr[0] + let attrValue = attr.length > 1 ? attr.slice(1).join('=') : null + // remove quotes from attributes + if (attrValue && attrValue.charAt(0).match(/'|"/)) attrValue = attrValue.slice(1) + if (attrValue && attrValue.charAt(attrValue.length - 1).match(/'|"/)) attrValue = attrValue.slice(0, -1) + attr = { + name: attrName, + value: attrValue } + if (!attr.value) delete attr.value + if (attr.name) attrs.push(attr) + }) - return matches - }, + const tag = { + full: match[0], + name: match[1], + attr: attrs + } - sanitize: function (html, options = { mode: 'white', list: bleach.whitelist, encode_entities: false}) { - html = String(html) || '' + matches.push(tag) + } + + return matches + }, + + sanitize: function (html, options = { mode: 'white', list: bleach.whitelist, encode_entities: false}) { + html = String(html) || '' - const mode = options.mode || 'white' - const list = options.list || bleach.whitelist + const mode = options.mode || 'white' + const list = options.list || bleach.whitelist - const matches = bleach.analyze(html) + const matches = bleach.analyze(html) - if ((mode === 'white' && list.indexOf('script') === -1) || + if ((mode === 'white' && list.indexOf('script') === -1) || (mode === 'black' && list.indexOf('script') !== -1)) { - html = html.replace(/(.*?[\r\n])*?(.*?)(.*?[\r\n])*?<\/script>/gim, '') - } + html = html.replace(/(.*?[\r\n])*?(.*?)(.*?[\r\n])*?<\/script>/gim, '') + } - if ((mode === 'white' && list.indexOf('style') === -1) || + if ((mode === 'white' && list.indexOf('style') === -1) || (mode === 'black' && list.indexOf('style') !== -1)) { - html = html.replace(/(.*?[\r\n])*?(.*?)(.*?[\r\n])*?<\/style>/gim, '') + html = html.replace(/(.*?[\r\n])*?(.*?)(.*?[\r\n])*?<\/style>/gim, '') + } + + matches.forEach(function (tag) { + if (mode === 'white') { + if (list.indexOf(tag.name) === -1) { + html = html.replace(tag.full, '') + } + } else if (mode === 'black') { + if (list.indexOf(tag.name) !== -1) { + html = html.replace(tag.full, '') } + } else { + throw new Error('Unknown sanitization mode "' + mode + '"') + } + }) - matches.forEach(function (tag) { - if (mode === 'white') { - if (list.indexOf(tag.name) === -1) { - html = html.replace(tag.full, '') - } - } else if (mode === 'black') { - if (list.indexOf(tag.name) !== -1) { - html = html.replace(tag.full, '') - } - } else { - throw new Error('Unknown sanitization mode "' + mode + '"') - } - }) - - if (options.encode_entities) html = he.encode(html) - - return html - } + if (options.encode_entities) html = he.encode(html) + + return html + } } diff --git a/libs/remix-ui/helper/src/lib/components/PluginViewWrapper.tsx b/libs/remix-ui/helper/src/lib/components/PluginViewWrapper.tsx index 6cdb45e232..d77ba6cb6d 100644 --- a/libs/remix-ui/helper/src/lib/components/PluginViewWrapper.tsx +++ b/libs/remix-ui/helper/src/lib/components/PluginViewWrapper.tsx @@ -7,18 +7,18 @@ interface IPluginViewWrapperProps { export const PluginViewWrapper = (props: IPluginViewWrapperProps) => { - const [state, setState] = useState(null) + const [state, setState] = useState(null) - useEffect(() => { - if(props.plugin.setDispatch){ - props.plugin.setDispatch(setState) - } - }, []) + useEffect(() => { + if(props.plugin.setDispatch){ + props.plugin.setDispatch(setState) + } + }, []) - return ( - <>{state? - <>{props.plugin.updateComponent(state)} - :<> - } - ) + return ( + <>{state? + <>{props.plugin.updateComponent(state)} + :<> + } + ) } \ No newline at end of file diff --git a/libs/remix-ui/helper/src/lib/components/custom-dropdown.tsx b/libs/remix-ui/helper/src/lib/components/custom-dropdown.tsx index abc1e923df..0db262012d 100644 --- a/libs/remix-ui/helper/src/lib/components/custom-dropdown.tsx +++ b/libs/remix-ui/helper/src/lib/components/custom-dropdown.tsx @@ -6,106 +6,106 @@ import { CustomTooltip } from '@remix-ui/helper' // Dropdown needs access to the DOM node in order to position the Menu export const CustomToggle = React.forwardRef(({ children, onClick, icon, className = '' }: { children: React.ReactNode, onClick: (e) => void, icon: string, className: string }, ref: Ref) => ( - + )) export const CustomIconsToggle = React.forwardRef(({ onClick, icon, className = '' }: { children?: React.ReactNode, onClick: () => void, icon: string, className: string }, ref: Ref) => ( - { - e.preventDefault() - onClick() - }} - className={`${className.replace('dropdown-toggle', '')} mr-1 mb-0 pb-0 d-flex justify-content-end align-items-end remixuimenuicon_shadow remixuimenuicon_hamburger_menu fs-3`} - data-id="workspaceMenuDropdown" + { + e.preventDefault() + onClick() + }} + className={`${className.replace('dropdown-toggle', '')} mr-1 mb-0 pb-0 d-flex justify-content-end align-items-end remixuimenuicon_shadow remixuimenuicon_hamburger_menu fs-3`} + data-id="workspaceMenuDropdown" + > + { icon && - { icon && - - - } - + + + } + )) // forwardRef again here! // Dropdown needs access to the DOM of the Menu to measure it export const CustomMenu = React.forwardRef( - ({ children, style, 'data-id': dataId, className, 'aria-labelledby': labeledBy }: { children: React.ReactNode, style?: React.CSSProperties, 'data-id'?: string, className: string, 'aria-labelledby'?: string }, ref: Ref) => { - const height = window.innerHeight * 0.6 - return ( -
-
    - { - children - } -
-
- ) - }, + ({ children, style, 'data-id': dataId, className, 'aria-labelledby': labeledBy }: { children: React.ReactNode, style?: React.CSSProperties, 'data-id'?: string, className: string, 'aria-labelledby'?: string }, ref: Ref) => { + const height = window.innerHeight * 0.6 + return ( +
+
    + { + children + } +
+
+ ) + }, ) export const ProxyAddressToggle = React.forwardRef(({ address, onClick, className = '', onChange }: { address: string, onClick: (e) => void, className: string, onChange: (e: React.ChangeEvent) => void }, ref: Ref) => ( -
{ - e.preventDefault() - onClick(e) - }} - className={'d-flex '+ className.replace('dropdown-toggle', '')} - data-id="toggleProxyAddressDropdown" - > - { - e.preventDefault() - onChange(e) - }} - className="udapp_input form-control" - value={address} - placeholder="Enter Proxy Address" - style={{ width: '100%' }} - data-id="ERC1967AddressInput" - /> -
+
{ + e.preventDefault() + onClick(e) + }} + className={'d-flex '+ className.replace('dropdown-toggle', '')} + data-id="toggleProxyAddressDropdown" + > + { + e.preventDefault() + onChange(e) + }} + className="udapp_input form-control" + value={address} + placeholder="Enter Proxy Address" + style={{ width: '100%' }} + data-id="ERC1967AddressInput" + /> +
)) export const ProxyDropdownMenu = React.forwardRef( - ({ children, style, className, 'aria-labelledby': labeledBy }: { children: React.ReactNode, style?: React.CSSProperties, className: string, 'aria-labelledby'?: string }, ref: Ref) => { - return ( -
-
    - { - children - } -
-
- ) - }, + ({ children, style, className, 'aria-labelledby': labeledBy }: { children: React.ReactNode, style?: React.CSSProperties, className: string, 'aria-labelledby'?: string }, ref: Ref) => { + return ( +
+
    + { + children + } +
+
+ ) + }, ) diff --git a/libs/remix-ui/helper/src/lib/components/custom-tooltip.tsx b/libs/remix-ui/helper/src/lib/components/custom-tooltip.tsx index 560d06ea78..619362c584 100644 --- a/libs/remix-ui/helper/src/lib/components/custom-tooltip.tsx +++ b/libs/remix-ui/helper/src/lib/components/custom-tooltip.tsx @@ -5,28 +5,28 @@ import { CustomTooltipType } from '../../types/customtooltip' export function CustomTooltip ({ children, placement, tooltipId, tooltipClasses, tooltipText, tooltipTextClasses, delay }: CustomTooltipType) { - if (typeof tooltipText !== 'string') { - const newTooltipText = React.cloneElement(tooltipText, { - className: " bg-secondary text-wrap p-1 px-2 " - }) - tooltipText = newTooltipText - } + if (typeof tooltipText !== 'string') { + const newTooltipText = React.cloneElement(tooltipText, { + className: " bg-secondary text-wrap p-1 px-2 " + }) + tooltipText = newTooltipText + } - return ( - - - - {typeof tooltipText === 'string' ? ({tooltipText}) : (tooltipText)} - - - } - delay={delay} - > - {children} - - - ) + return ( + + + + {typeof tooltipText === 'string' ? ({tooltipText}) : (tooltipText)} + + + } + delay={delay} + > + {children} + + + ) } diff --git a/libs/remix-ui/helper/src/lib/components/web3Dialog.tsx b/libs/remix-ui/helper/src/lib/components/web3Dialog.tsx index 3412148086..bf3f5a6f1e 100644 --- a/libs/remix-ui/helper/src/lib/components/web3Dialog.tsx +++ b/libs/remix-ui/helper/src/lib/components/web3Dialog.tsx @@ -8,37 +8,37 @@ interface web3ProviderDialogProps { const thePath = '' export function Web3ProviderDialog (props: web3ProviderDialogProps) { - const handleInputEndpoint = (e) => { - props.setWeb3Endpoint(e.target.value) - } + const handleInputEndpoint = (e) => { + props.setWeb3Endpoint(e.target.value) + } - return ( - <> -
+ return ( + <> +
Note: To use Geth & https://remix.ethereum.org, configure it to allow requests from Remix:(see Geth Docs on rpc server) -
geth --http --http.corsdomain https://remix.ethereum.org
-
+
geth --http --http.corsdomain https://remix.ethereum.org
+
To run Remix & a local Geth test node, use this command: (see Geth Docs on Dev mode) -
geth --http --http.corsdomain="{window.origin}" --http.api web3,eth,debug,personal,net --vmdebug --datadir {thePath} --dev console
-
-
- WARNING: It is not safe to use the --http.corsdomain flag with a wildcard: --http.corsdomain * -
-
For more info: Remix Docs on Remix Provider -
-
+
geth --http --http.corsdomain="{window.origin}" --http.api web3,eth,debug,personal,net --vmdebug --datadir {thePath} --dev console
+
+
+ WARNING: It is not safe to use the --http.corsdomain flag with a wildcard: --http.corsdomain * +
+
For more info: Remix Docs on Remix Provider +
+
External HTTP Provider Endpoint -
- - - ) +
+ + + ) } diff --git a/libs/remix-ui/helper/src/lib/helper-components.tsx b/libs/remix-ui/helper/src/lib/helper-components.tsx index 4792b22308..7581a34ac9 100644 --- a/libs/remix-ui/helper/src/lib/helper-components.tsx +++ b/libs/remix-ui/helper/src/lib/helper-components.tsx @@ -2,141 +2,141 @@ import { LayoutCompatibilityReport } from '@openzeppelin/upgrades-core/dist/stor import React from 'react' export const fileChangedToastMsg = (from: string, path: string) => ( -
- - {from} +
+ + {from} is modifying - {path} - -
+
{path} +
+
) export const compilerConfigChangedToastMsg = (from: string, value: string) => ( -
- { from } is updating the Solidity compiler configuration. -
{value}
-
+
+ { from } is updating the Solidity compiler configuration. +
{value}
+
) export const compileToastMsg = (from: string, fileName: string) => ( -
- {from} is requiring to compile {fileName} -
+
+ {from} is requiring to compile {fileName} +
) export const compilingToastMsg = (settings: string) => ( -
- Recompiling and debugging with params -
{settings}
+
+ Recompiling and debugging with params +
{settings}
) export const compilationFinishedToastMsg = () => ( -
- Compilation failed... continuing without source code debugging. -
+
+ Compilation failed... continuing without source code debugging. +
) export const notFoundToastMsg = (address: string) => ( -
- Contract {address} not found in source code repository continuing without source code debugging. -
+
+ Contract {address} not found in source code repository continuing without source code debugging. +
) export const localCompilationToastMsg = () => ( -
- Using compilation result from Solidity module -
+
+ Using compilation result from Solidity module +
) export const sourceVerificationNotAvailableToastMsg = () => ( -
- Source verification plugin not activated or not available. continuing without source code debugging. -
+
+ Source verification plugin not activated or not available. continuing without source code debugging. +
) export const envChangeNotification = (env: { context: string, fork: string }, from: string) => ( -
- - - { from + ' '} - +
+ + + { from + ' '} + set your environment to - {env && env.context} - -
+
{env && env.context} +
+
) export const storageFullMessage = () => ( -
- - - Cannot save this file due to full LocalStorage. Backup existing files and free up some space. - -
+
+ + + Cannot save this file due to full LocalStorage. Backup existing files and free up some space. + +
) export const recursivePasteToastMsg = () => ( -
+
File(s) to paste is an ancestor of the destination folder -
+
) export const logBuilder = (msg: string) => { - return
{msg}
+ return
{msg}
} export const cancelProxyMsg = () => ( -
- Proxy deployment cancelled. -
+
+ Proxy deployment cancelled. +
) export const cancelUpgradeMsg = () => ( -
- Upgrade with proxy cancelled. -
+
+ Upgrade with proxy cancelled. +
) export const deployWithProxyMsg = () => ( -
- Deploy with Proxy will initiate two (2) transactions: -
    -
  1. Deploying the implementation contract
  2. -
  3. Deploying an ERC1967 proxy contract
  4. -
-
+
+ Deploy with Proxy will initiate two (2) transactions: +
    +
  1. Deploying the implementation contract
  2. +
  3. Deploying an ERC1967 proxy contract
  4. +
+
) export const upgradeWithProxyMsg = () => ( -
- Upgrade with Proxy will initiate two (2) transactions: -
    -
  1. Deploying the new implementation contract
  2. -
  3. Updating the proxy contract with the address of the new implementation contract
  4. -
-
+
+ Upgrade with Proxy will initiate two (2) transactions: +
    +
  1. Deploying the new implementation contract
  2. +
  3. Updating the proxy contract with the address of the new implementation contract
  4. +
+
) export const unavailableProxyLayoutMsg = () => ( -
-

The previous contract implementation is NOT available for an upgrade comparison
A new storage layout will be saved for future upgrades.

-
+
+

The previous contract implementation is NOT available for an upgrade comparison
A new storage layout will be saved for future upgrades.

+
) export const upgradeReportMsg = (report: LayoutCompatibilityReport) => ( -
-
- - - -
- The storage layout of new implementation is NOT - compatible with the previous implementation. - Your contract's storage may be partially or fully erased! -
-
-
- { report.explain() } -
-
+
+
+ + + +
+ The storage layout of new implementation is NOT + compatible with the previous implementation. + Your contract's storage may be partially or fully erased! +
+
+
+ { report.explain() } +
+
) diff --git a/libs/remix-ui/helper/src/lib/remix-ui-helper.ts b/libs/remix-ui/helper/src/lib/remix-ui-helper.ts index e06253c325..a868d15a0f 100644 --- a/libs/remix-ui/helper/src/lib/remix-ui-helper.ts +++ b/libs/remix-ui/helper/src/lib/remix-ui-helper.ts @@ -1,142 +1,142 @@ import * as ethJSUtil from '@ethereumjs/util' export const extractNameFromKey = (key: string): string => { - if (!key) return - const keyPath = key.split('/') + if (!key) return + const keyPath = key.split('/') - return keyPath[keyPath.length - 1] + return keyPath[keyPath.length - 1] } export const extractParentFromKey = (key: string):string => { - if (!key) return - const keyPath = key.split('/') + if (!key) return + const keyPath = key.split('/') - keyPath.pop() + keyPath.pop() - return keyPath.join('/') + return keyPath.join('/') } export const checkSpecialChars = (name: string) => { - return name.match(/[:*?"<>\\'|]/) != null + return name.match(/[:*?"<>\\'|]/) != null } export const checkSlash = (name: string) => { - return name.match(/\//) != null + return name.match(/\//) != null } export const createNonClashingNameAsync = async (name: string, fileManager, prefix = '') => { - if (!name) name = 'Undefined' - let _counter - let ext = 'sol' - const reg = /(.*)\.([^.]+)/g - const split = reg.exec(name) - if (split) { - name = split[1] - ext = split[2] - } - let exist = true + if (!name) name = 'Undefined' + let _counter + let ext = 'sol' + const reg = /(.*)\.([^.]+)/g + const split = reg.exec(name) + if (split) { + name = split[1] + ext = split[2] + } + let exist = true - do { - const isDuplicate = await fileManager.exists(name + (_counter || '') + prefix + '.' + ext) + do { + const isDuplicate = await fileManager.exists(name + (_counter || '') + prefix + '.' + ext) - if (isDuplicate) _counter = (_counter || 0) + 1 - else exist = false - } while (exist) - const counter = _counter || '' + if (isDuplicate) _counter = (_counter || 0) + 1 + else exist = false + } while (exist) + const counter = _counter || '' - return name + counter + prefix + '.' + ext + return name + counter + prefix + '.' + ext } export const createNonClashingTitle = async (name: string, fileManager) => { - if (!name) name = 'Undefined' - let _counter - let exist = true + if (!name) name = 'Undefined' + let _counter + let exist = true - do { - const isDuplicate = await fileManager.exists(name + (_counter || '')) + do { + const isDuplicate = await fileManager.exists(name + (_counter || '')) - if (isDuplicate) _counter = (_counter || 0) + 1 - else exist = false - } while (exist) - const counter = _counter || '' + if (isDuplicate) _counter = (_counter || 0) + 1 + else exist = false + } while (exist) + const counter = _counter || '' - return name + counter + return name + counter } export const joinPath = (...paths) => { - paths = paths.filter((value) => value !== '').map((path) => path.replace(/^\/|\/$/g, '')) // remove first and last slash) - if (paths.length === 1) return paths[0] - return paths.join('/') + paths = paths.filter((value) => value !== '').map((path) => path.replace(/^\/|\/$/g, '')) // remove first and last slash) + if (paths.length === 1) return paths[0] + return paths.join('/') } export const getPathIcon = (path: string) => { - return path.endsWith('.txt') - ? 'far fa-file-alt' : path.endsWith('.md') - ? 'fab fa-markdown' : path.endsWith('.sol') - ? 'fak fa-solidity-mono' : path.endsWith('.js') - ? 'fab fa-js' : path.endsWith('.json') - ? 'small fas fa-brackets-curly' : path.endsWith('.vy') - ? 'small fak fa-vyper2' : path.endsWith('.lex') - ? 'fak fa-lexon' : path.endsWith('ts') - ? 'small fak fa-ts-logo' : path.endsWith('.tsc') - ? 'fad fa-brackets-curly' : path.endsWith('.cairo') - ? 'small fak fa-cairo' : 'far fa-file' + return path.endsWith('.txt') + ? 'far fa-file-alt' : path.endsWith('.md') + ? 'fab fa-markdown' : path.endsWith('.sol') + ? 'fak fa-solidity-mono' : path.endsWith('.js') + ? 'fab fa-js' : path.endsWith('.json') + ? 'small fas fa-brackets-curly' : path.endsWith('.vy') + ? 'small fak fa-vyper2' : path.endsWith('.lex') + ? 'fak fa-lexon' : path.endsWith('ts') + ? 'small fak fa-ts-logo' : path.endsWith('.tsc') + ? 'fad fa-brackets-curly' : path.endsWith('.cairo') + ? 'small fak fa-cairo' : 'far fa-file' } export const isNumeric = (value) => { - return /^\+?(0|[1-9]\d*)$/.test(value) + return /^\+?(0|[1-9]\d*)$/.test(value) } export const shortenAddress = (address, etherBalance?) => { - const len = address.length + const len = address.length - return address.slice(0, 5) + '...' + address.slice(len - 5, len) + (etherBalance ? ' (' + etherBalance.toString() + ' ether)' : '') + return address.slice(0, 5) + '...' + address.slice(len - 5, len) + (etherBalance ? ' (' + etherBalance.toString() + ' ether)' : '') } export const addressToString = (address) => { - if (!address) return null - if (typeof address !== 'string') { - address = address.toString('hex') - } - if (address.indexOf('0x') === -1) { - address = '0x' + address - } - return ethJSUtil.toChecksumAddress(address) + if (!address) return null + if (typeof address !== 'string') { + address = address.toString('hex') + } + if (address.indexOf('0x') === -1) { + address = '0x' + address + } + return ethJSUtil.toChecksumAddress(address) } export const is0XPrefixed = (value) => { - return value.substr(0, 2) === '0x' + return value.substr(0, 2) === '0x' } export const isHexadecimal = (value) => { - return /^[0-9a-fA-F]+$/.test(value) && (value.length % 2 === 0) + return /^[0-9a-fA-F]+$/.test(value) && (value.length % 2 === 0) } export const isValidHash = (hash) => { // 0x prefixed, hexadecimal, 64digit - const hexValue = hash.slice(2, hash.length) - return is0XPrefixed(hash) && /^[0-9a-fA-F]{64}$/.test(hexValue) + const hexValue = hash.slice(2, hash.length) + return is0XPrefixed(hash) && /^[0-9a-fA-F]{64}$/.test(hexValue) } export const shortenHexData = (data) => { - if (!data) return '' - if (data.length < 5) return data - const len = data.length - return data.slice(0, 5) + '...' + data.slice(len - 5, len) + if (!data) return '' + if (data.length < 5) return data + const len = data.length + return data.slice(0, 5) + '...' + data.slice(len - 5, len) } export const addSlash = (file: string) => { - if (!file.startsWith('/'))file = '/' + file - return file + if (!file.startsWith('/'))file = '/' + file + return file } export const shortenProxyAddress = (address: string) => { - const len = address.length + const len = address.length - return address.slice(0, 5) + '...' + address.slice(len - 5, len) + return address.slice(0, 5) + '...' + address.slice(len - 5, len) } export const shortenDate = (dateString: string) => { - const date = new Date(dateString) + const date = new Date(dateString) - return date.toLocaleDateString(undefined, { month: "short", day: "numeric" }) + ', ' + date.toLocaleTimeString(undefined, { hour: '2-digit', minute: '2-digit' }) + return date.toLocaleDateString(undefined, { month: "short", day: "numeric" }) + ', ' + date.toLocaleTimeString(undefined, { hour: '2-digit', minute: '2-digit' }) } diff --git a/libs/remix-ui/home-tab/src/lib/components/customNavButtons.tsx b/libs/remix-ui/home-tab/src/lib/components/customNavButtons.tsx index c161a82591..a6df299b7c 100644 --- a/libs/remix-ui/home-tab/src/lib/components/customNavButtons.tsx +++ b/libs/remix-ui/home-tab/src/lib/components/customNavButtons.tsx @@ -2,28 +2,28 @@ import React from 'react' const CustomNavButtons = ({ parent, next, previous, goToSlide, ...rest }) => { - const { carouselState: { currentSlide, totalItems, containerWidth, transform } } = rest - return ( -
- - -
- ) + const { carouselState: { currentSlide, totalItems, containerWidth, transform } } = rest + return ( +
+ + +
+ ) } export default CustomNavButtons diff --git a/libs/remix-ui/home-tab/src/lib/components/homeTabFeatured.tsx b/libs/remix-ui/home-tab/src/lib/components/homeTabFeatured.tsx index c8de93004e..3aac753e24 100644 --- a/libs/remix-ui/home-tab/src/lib/components/homeTabFeatured.tsx +++ b/libs/remix-ui/home-tab/src/lib/components/homeTabFeatured.tsx @@ -8,93 +8,93 @@ import 'react-multi-carousel/lib/styles.css' const _paq = window._paq = window._paq || [] // eslint-disable-line function HomeTabFeatured() { - const themeFilter = useContext(ThemeContext) + const themeFilter = useContext(ThemeContext) - return ( -
- -
-
- - - - - - - + return ( + +
+ ) } export default HomeTabFeatured diff --git a/libs/remix-ui/home-tab/src/lib/components/homeTabFeaturedPlugins.tsx b/libs/remix-ui/home-tab/src/lib/components/homeTabFeaturedPlugins.tsx index 395b3c1fb3..85d9e86728 100644 --- a/libs/remix-ui/home-tab/src/lib/components/homeTabFeaturedPlugins.tsx +++ b/libs/remix-ui/home-tab/src/lib/components/homeTabFeaturedPlugins.tsx @@ -19,149 +19,149 @@ interface HomeTabFeaturedPluginsProps { function HomeTabFeaturedPlugins ({plugin}: HomeTabFeaturedPluginsProps) { - const themeFilter = useContext(ThemeContext) - const carouselRef = useRef({}) - const carouselRefDiv = useRef(null) - const intl = useIntl() + const themeFilter = useContext(ThemeContext) + const carouselRef = useRef({}) + const carouselRefDiv = useRef(null) + const intl = useIntl() - useEffect(() => { - document.addEventListener("wheel", handleScroll) - return () => { - document.removeEventListener("wheel", handleScroll) - } - }, []) - - function isDescendant(parent, child) { - let node = child.parentNode; - while (node != null) { - if (node === parent) { - return true; - } - node = node.parentNode; - } - return false; + useEffect(() => { + document.addEventListener("wheel", handleScroll) + return () => { + document.removeEventListener("wheel", handleScroll) } + }, []) - const handleScroll = (e) => { - if (isDescendant(carouselRefDiv.current, e.target)) { - e.stopPropagation() - let nextSlide = 0 - if (e.wheelDelta < 0) { - nextSlide = carouselRef.current.state.currentSlide + 1; - if (Math.abs(carouselRef.current.state.transform) >= carouselRef.current.containerRef.current.scrollWidth - carouselRef.current.state.containerWidth) return - carouselRef.current.goToSlide(nextSlide) - } else { - nextSlide = carouselRef.current.state.currentSlide - 1; - if (nextSlide < 0) nextSlide = 0 - carouselRef.current.goToSlide(nextSlide) - } - } + function isDescendant(parent, child) { + let node = child.parentNode; + while (node != null) { + if (node === parent) { + return true; + } + node = node.parentNode; } + return false; + } - const startSolidity = async () => { - await plugin.appManager.activatePlugin(['solidity', 'udapp', 'solidityStaticAnalysis', 'solidityUnitTesting']) - plugin.verticalIcons.select('solidity') - _paq.push(['trackEvent', 'hometabActivate', 'userActivate', 'solidity']) - } - const startCodeAnalyzer = async () => { - await plugin.appManager.activatePlugin(['solidity', 'solidityStaticAnalysis']) - plugin.verticalIcons.select('solidityStaticAnalysis') - _paq.push(['trackEvent', 'hometabActivate', 'userActivate', 'solidityStaticAnalysis']) - } - const startSourceVerify = async () => { - await plugin.appManager.activatePlugin(['solidity', 'sourcify']) - plugin.verticalIcons.select('sourcify') - _paq.push(['trackEvent', 'hometabActivate', 'userActivate', 'sourcify']) - } - const startCookbook = async () => { - await plugin.appManager.activatePlugin(['cookbook.dev']) - plugin.verticalIcons.select('cookbook.dev') - _paq.push(['trackEvent', 'hometabActivate', 'userActivate', 'cookbook.dev']) - } - const startSolidityUnitTesting = async () => { - await plugin.appManager.activatePlugin(['solidity', 'solidityUnitTesting']) - plugin.verticalIcons.select('solidityUnitTesting') - _paq.push(['trackEvent', 'hometabActivate', 'userActivate', 'solidityUnitTesting']) + const handleScroll = (e) => { + if (isDescendant(carouselRefDiv.current, e.target)) { + e.stopPropagation() + let nextSlide = 0 + if (e.wheelDelta < 0) { + nextSlide = carouselRef.current.state.currentSlide + 1; + if (Math.abs(carouselRef.current.state.transform) >= carouselRef.current.containerRef.current.scrollWidth - carouselRef.current.state.containerWidth) return + carouselRef.current.goToSlide(nextSlide) + } else { + nextSlide = carouselRef.current.state.currentSlide - 1; + if (nextSlide < 0) nextSlide = 0 + carouselRef.current.goToSlide(nextSlide) + } } + } + + const startSolidity = async () => { + await plugin.appManager.activatePlugin(['solidity', 'udapp', 'solidityStaticAnalysis', 'solidityUnitTesting']) + plugin.verticalIcons.select('solidity') + _paq.push(['trackEvent', 'hometabActivate', 'userActivate', 'solidity']) + } + const startCodeAnalyzer = async () => { + await plugin.appManager.activatePlugin(['solidity', 'solidityStaticAnalysis']) + plugin.verticalIcons.select('solidityStaticAnalysis') + _paq.push(['trackEvent', 'hometabActivate', 'userActivate', 'solidityStaticAnalysis']) + } + const startSourceVerify = async () => { + await plugin.appManager.activatePlugin(['solidity', 'sourcify']) + plugin.verticalIcons.select('sourcify') + _paq.push(['trackEvent', 'hometabActivate', 'userActivate', 'sourcify']) + } + const startCookbook = async () => { + await plugin.appManager.activatePlugin(['cookbook.dev']) + plugin.verticalIcons.select('cookbook.dev') + _paq.push(['trackEvent', 'hometabActivate', 'userActivate', 'cookbook.dev']) + } + const startSolidityUnitTesting = async () => { + await plugin.appManager.activatePlugin(['solidity', 'solidityUnitTesting']) + plugin.verticalIcons.select('solidityUnitTesting') + _paq.push(['trackEvent', 'hometabActivate', 'userActivate', 'solidityUnitTesting']) + } - return ( -
- -
- - - } - arrows={false} - swipeable={false} - draggable={true} - showDots={false} - responsive={ - { - superLargeDesktop: { - breakpoint: { max: 4000, min: 3000 }, - items: itemsToShow - }, - desktop: { - breakpoint: { max: 3000, min: 1024 }, - items: itemsToShow - } - } - } - renderButtonGroupOutside={true} - ssr={true} // means to render carousel on server-side. - keyBoardControl={true} - containerClass="carousel-container" - deviceType={"desktop"} - itemClass="w-100" - > - startCodeAnalyzer() } - /> - startCookbook()} - /> - startSolidity()} - /> - startSourceVerify()} - /> - startSolidityUnitTesting()} - /> - - -
-
- ) + return ( +
+ +
+ + + } + arrows={false} + swipeable={false} + draggable={true} + showDots={false} + responsive={ + { + superLargeDesktop: { + breakpoint: { max: 4000, min: 3000 }, + items: itemsToShow + }, + desktop: { + breakpoint: { max: 3000, min: 1024 }, + items: itemsToShow + } + } + } + renderButtonGroupOutside={true} + ssr={true} // means to render carousel on server-side. + keyBoardControl={true} + containerClass="carousel-container" + deviceType={"desktop"} + itemClass="w-100" + > + startCodeAnalyzer() } + /> + startCookbook()} + /> + startSolidity()} + /> + startSourceVerify()} + /> + startSolidityUnitTesting()} + /> + + +
+
+ ) } export default HomeTabFeaturedPlugins diff --git a/libs/remix-ui/home-tab/src/lib/components/homeTabFile.tsx b/libs/remix-ui/home-tab/src/lib/components/homeTabFile.tsx index 3a9acbf525..8b95dd3a62 100644 --- a/libs/remix-ui/home-tab/src/lib/components/homeTabFile.tsx +++ b/libs/remix-ui/home-tab/src/lib/components/homeTabFile.tsx @@ -11,185 +11,185 @@ interface HomeTabFileProps { } const loadingInitialState = { - tooltip: '', - showModalDialog: false, - importSource: '' + tooltip: '', + showModalDialog: false, + importSource: '' } const loadingReducer = (state = loadingInitialState, action) => { - return { ...state, tooltip: action.tooltip, showModalDialog: false, importSource: '' } + return { ...state, tooltip: action.tooltip, showModalDialog: false, importSource: '' } } function HomeTabFile ({plugin}: HomeTabFileProps) { - const [state, setState] = useState<{ + const [state, setState] = useState<{ searchInput: string, showModalDialog: boolean, modalInfo: { title: string, loadItem: string, examples: Array, prefix?: string }, importSource: string, toasterMsg: string }>({ - searchInput: '', - showModalDialog: false, - modalInfo: { title: '', loadItem: '', examples: [], prefix: '' }, - importSource: '', - toasterMsg: '' + searchInput: '', + showModalDialog: false, + modalInfo: { title: '', loadItem: '', examples: [], prefix: '' }, + importSource: '', + toasterMsg: '' }) - const [, dispatch] = useReducer(loadingReducer, loadingInitialState) + const [, dispatch] = useReducer(loadingReducer, loadingInitialState) - const inputValue = useRef(null) + const inputValue = useRef(null) - const processLoading = (type: string) => { - _paq.push(['trackEvent', 'hometab', 'filesSection', 'importFrom' + type]) - const contentImport = plugin.contentImport - const workspace = plugin.fileManager.getProvider('workspace') - const startsWith = state.importSource.substring(0, 4) + const processLoading = (type: string) => { + _paq.push(['trackEvent', 'hometab', 'filesSection', 'importFrom' + type]) + const contentImport = plugin.contentImport + const workspace = plugin.fileManager.getProvider('workspace') + const startsWith = state.importSource.substring(0, 4) - if ((type === 'ipfs' || type === 'IPFS') && (startsWith !== 'ipfs' && startsWith !== "IPFS")) { - setState(prevState => { - return { ...prevState, importSource: startsWith + state.importSource} - }) - } - contentImport.import( - state.modalInfo.prefix + state.importSource, - (loadingMsg) => dispatch({ tooltip: loadingMsg }), - async (error, content, cleanUrl, type, url) => { - if (error) { - toast(error.message || error) - } else { - try { - if (await workspace.exists(type + '/' + cleanUrl)) toast('File already exists in workspace') - else { - workspace.addExternal(type + '/' + cleanUrl, content, url) - plugin.call('menuicons', 'select', 'filePanel') - } - } catch (e) { - toast(e.message) - } - } - } - ) - setState(prevState => { - return { ...prevState, showModalDialog: false, importSource: '' } - }) - } - - const toast = (message: string) => { - setState(prevState => { - return { ...prevState, toasterMsg: message } - }) - } - - const createNewFile = async () => { - _paq.push(['trackEvent', 'hometab', 'filesSection', 'createNewFile']) - plugin.verticalIcons.select('filePanel') - await plugin.call('filePanel', 'createNewFile') - } - - const uploadFile = async (target) => { - _paq.push(['trackEvent', 'hometab', 'filesSection', 'uploadFile']) - await plugin.call('filePanel', 'uploadFile', target) - } - - const connectToLocalhost = () => { - _paq.push(['trackEvent', 'hometab', 'filesSection', 'connectToLocalhost']) - plugin.appManager.activatePlugin('remixd') - } - const importFromGist = () => { - _paq.push(['trackEvent', 'hometab', 'filesSection', 'importFromGist']) - plugin.call('gistHandler', 'load', '') - plugin.verticalIcons.select('filePanel') + if ((type === 'ipfs' || type === 'IPFS') && (startsWith !== 'ipfs' && startsWith !== "IPFS")) { + setState(prevState => { + return { ...prevState, importSource: startsWith + state.importSource} + }) } + contentImport.import( + state.modalInfo.prefix + state.importSource, + (loadingMsg) => dispatch({ tooltip: loadingMsg }), + async (error, content, cleanUrl, type, url) => { + if (error) { + toast(error.message || error) + } else { + try { + if (await workspace.exists(type + '/' + cleanUrl)) toast('File already exists in workspace') + else { + workspace.addExternal(type + '/' + cleanUrl, content, url) + plugin.call('menuicons', 'select', 'filePanel') + } + } catch (e) { + toast(e.message) + } + } + } + ) + setState(prevState => { + return { ...prevState, showModalDialog: false, importSource: '' } + }) + } - const showFullMessage = (title: string, loadItem: string, examples: Array, prefix = '') => { - setState(prevState => { - return { ...prevState, showModalDialog: true, modalInfo: { title: title, loadItem: loadItem, examples: examples, prefix } } - }) - } + const toast = (message: string) => { + setState(prevState => { + return { ...prevState, toasterMsg: message } + }) + } + + const createNewFile = async () => { + _paq.push(['trackEvent', 'hometab', 'filesSection', 'createNewFile']) + plugin.verticalIcons.select('filePanel') + await plugin.call('filePanel', 'createNewFile') + } + + const uploadFile = async (target) => { + _paq.push(['trackEvent', 'hometab', 'filesSection', 'uploadFile']) + await plugin.call('filePanel', 'uploadFile', target) + } + + const connectToLocalhost = () => { + _paq.push(['trackEvent', 'hometab', 'filesSection', 'connectToLocalhost']) + plugin.appManager.activatePlugin('remixd') + } + const importFromGist = () => { + _paq.push(['trackEvent', 'hometab', 'filesSection', 'importFromGist']) + plugin.call('gistHandler', 'load', '') + plugin.verticalIcons.select('filePanel') + } + + const showFullMessage = (title: string, loadItem: string, examples: Array, prefix = '') => { + setState(prevState => { + return { ...prevState, showModalDialog: true, modalInfo: { title: title, loadItem: loadItem, examples: examples, prefix } } + }) + } const hideFullMessage = () => { //eslint-disable-line - setState(prevState => { - return { ...prevState, showModalDialog: false, importSource: '' } - }) - } - - const examples = state.modalInfo.examples.map((urlEl, key) => ()) - - return ( - <> - hideFullMessage() } - okFn={ () => processLoading(state.modalInfo.title) } - > -
- { state.modalInfo.loadItem !== '' && Enter the { state.modalInfo.loadItem } you would like to load. } - { state.modalInfo.examples.length !== 0 && + setState(prevState => { + return { ...prevState, showModalDialog: false, importSource: '' } + }) + } + + const examples = state.modalInfo.examples.map((urlEl, key) => ()) + + return ( + <> + hideFullMessage() } + okFn={ () => processLoading(state.modalInfo.title) } + > +
+ { state.modalInfo.loadItem !== '' && Enter the { state.modalInfo.loadItem } you would like to load. } + { state.modalInfo.examples.length !== 0 && <> -
e.g
-
- { examples } -
+
e.g
+
+ { examples } +
} -
- { state.modalInfo.prefix && ipfs:// } - { - setState(prevState => { - return { ...prevState, importSource: inputValue.current.value } - }) - }} - /> -
-
-
- -
- -
- - - { - event.stopPropagation() - plugin.verticalIcons.select('filePanel') - uploadFile(event.target) - }} multiple /> - - - -
- -
-
+ + +
+ +
+ + + { + event.stopPropagation() + plugin.verticalIcons.select('filePanel') + uploadFile(event.target) + }} multiple /> + + + +
+ +
+ - - - -
-
- - ) + + + + +
+
+ + ) } export default HomeTabFile diff --git a/libs/remix-ui/home-tab/src/lib/components/homeTabGetStarted.tsx b/libs/remix-ui/home-tab/src/lib/components/homeTabGetStarted.tsx index 50973b4cf4..8a8596d6ad 100644 --- a/libs/remix-ui/home-tab/src/lib/components/homeTabGetStarted.tsx +++ b/libs/remix-ui/home-tab/src/lib/components/homeTabGetStarted.tsx @@ -18,131 +18,131 @@ interface HomeTabGetStartedProps { } function HomeTabGetStarted ({plugin}: HomeTabGetStartedProps) { - const themeFilter = useContext(ThemeContext) - const carouselRef = useRef({}) - const carouselRefDiv = useRef(null) - const intl = useIntl() + const themeFilter = useContext(ThemeContext) + const carouselRef = useRef({}) + const carouselRefDiv = useRef(null) + const intl = useIntl() - useEffect(() => { - document.addEventListener("wheel", handleScroll) - return () => { - document.removeEventListener("wheel", handleScroll) - } - }, []) - - function isDescendant(parent, child) { - let node = child.parentNode; - while (node != null) { - if (node === parent) { - return true; - } - node = node.parentNode; - } - return false; + useEffect(() => { + document.addEventListener("wheel", handleScroll) + return () => { + document.removeEventListener("wheel", handleScroll) } + }, []) - const handleScroll = (e) => { - if (isDescendant(carouselRefDiv.current, e.target)) { - e.stopPropagation() - let nextSlide = 0 - if (e.wheelDelta < 0) { - nextSlide = carouselRef.current.state.currentSlide + 1; - if (Math.abs(carouselRef.current.state.transform) >= carouselRef.current.containerRef.current.scrollWidth - carouselRef.current.state.containerWidth) return - carouselRef.current.goToSlide(nextSlide) - } else { - nextSlide = carouselRef.current.state.currentSlide - 1; - if (nextSlide < 0) nextSlide = 0 - carouselRef.current.goToSlide(nextSlide) - } - } + function isDescendant(parent, child) { + let node = child.parentNode; + while (node != null) { + if (node === parent) { + return true; + } + node = node.parentNode; } + return false; + } - const createWorkspace = async (templateName) => { - await plugin.appManager.activatePlugin('filePanel') - const timeStamp = Date.now() - let templateDisplayName = TEMPLATE_NAMES[templateName] - templateDisplayName = await plugin.call('filePanel', 'getAvailableWorkspaceName', templateDisplayName) - await plugin.call('filePanel', 'createWorkspace', templateDisplayName, templateName) - await plugin.call('filePanel', 'setWorkspace', templateDisplayName) - plugin.verticalIcons.select('filePanel') - _paq.push(['trackEvent', 'hometab', 'homeGetStarted', templateName]) + const handleScroll = (e) => { + if (isDescendant(carouselRefDiv.current, e.target)) { + e.stopPropagation() + let nextSlide = 0 + if (e.wheelDelta < 0) { + nextSlide = carouselRef.current.state.currentSlide + 1; + if (Math.abs(carouselRef.current.state.transform) >= carouselRef.current.containerRef.current.scrollWidth - carouselRef.current.state.containerWidth) return + carouselRef.current.goToSlide(nextSlide) + } else { + nextSlide = carouselRef.current.state.currentSlide - 1; + if (nextSlide < 0) nextSlide = 0 + carouselRef.current.goToSlide(nextSlide) + } } + } - return ( -
- +
+ + + } + arrows={false} + swipeable={false} + draggable={true} + showDots={false} + responsive={ + { + superLargeDesktop: { + breakpoint: { max: 4000, min: 3000 }, + items: 5 + }, + desktop: { + breakpoint: { max: 3000, min: 1024 }, + items: 5, + partialVisibilityGutter: 0 + } + } + } + renderButtonGroupOutside={true} + ssr={true} // means to render carousel on server-side. + keyBoardControl={true} + containerClass="carousel-container" + deviceType={"desktop"} + itemClass="w-100" + > + createWorkspace("gnosisSafeMultisig")} /> + createWorkspace("zeroxErc20")} /> + createWorkspace("ozerc20")} /> + createWorkspace("ozerc721")} /> + createWorkspace("ozerc1155")} /> + createWorkspace("remixDefault")} /> + + +
+
+ ) } export default HomeTabGetStarted diff --git a/libs/remix-ui/home-tab/src/lib/components/homeTabLearn.tsx b/libs/remix-ui/home-tab/src/lib/components/homeTabLearn.tsx index 50bf1aad33..252a569380 100644 --- a/libs/remix-ui/home-tab/src/lib/components/homeTabLearn.tsx +++ b/libs/remix-ui/home-tab/src/lib/components/homeTabLearn.tsx @@ -20,98 +20,98 @@ interface HomeTabLearnProps { } function HomeTabLearn ({plugin}: HomeTabLearnProps) { - const [state, setState] = useState<{ + const [state, setState] = useState<{ visibleTutorial: VisibleTutorial }>({ - visibleTutorial: VisibleTutorial.Basics + visibleTutorial: VisibleTutorial.Basics }) - const themeFilter = useContext(ThemeContext) + const themeFilter = useContext(ThemeContext) - const startLearnEthTutorial = async (tutorial: 'basics' | 'soliditybeginner' | 'deploylibraries') => { - await plugin.appManager.activatePlugin(['solidity', 'LearnEth', 'solidityUnitTesting']) - plugin.verticalIcons.select('LearnEth') - plugin.call('LearnEth', 'startTutorial', 'ethereum/remix-workshops', 'master', tutorial) - _paq.push(['trackEvent', 'hometab', 'startLearnEthTutorial', tutorial]) - } + const startLearnEthTutorial = async (tutorial: 'basics' | 'soliditybeginner' | 'deploylibraries') => { + await plugin.appManager.activatePlugin(['solidity', 'LearnEth', 'solidityUnitTesting']) + plugin.verticalIcons.select('LearnEth') + plugin.call('LearnEth', 'startTutorial', 'ethereum/remix-workshops', 'master', tutorial) + _paq.push(['trackEvent', 'hometab', 'startLearnEthTutorial', tutorial]) + } - const goToLearnEthHome = async () => { - if(await plugin.appManager.isActive('LearnEth')) { - plugin.verticalIcons.select('LearnEth') - await plugin.call('LearnEth', 'home') - } else { - await plugin.appManager.activatePlugin(['LearnEth', 'solidity', 'solidityUnitTesting']) - plugin.verticalIcons.select('LearnEth') - await plugin.call('LearnEth', 'home') - } + const goToLearnEthHome = async () => { + if(await plugin.appManager.isActive('LearnEth')) { + plugin.verticalIcons.select('LearnEth') + await plugin.call('LearnEth', 'home') + } else { + await plugin.appManager.activatePlugin(['LearnEth', 'solidity', 'solidityUnitTesting']) + plugin.verticalIcons.select('LearnEth') + await plugin.call('LearnEth', 'home') } + } - return ( -
-
- - - - -
-
- - - -
-
- ) + return ( +
+
+ + + + +
+
+ + + +
+
+ ) } export default HomeTabLearn diff --git a/libs/remix-ui/home-tab/src/lib/components/homeTabScamAlert.tsx b/libs/remix-ui/home-tab/src/lib/components/homeTabScamAlert.tsx index 82e16305bd..f8e757b157 100644 --- a/libs/remix-ui/home-tab/src/lib/components/homeTabScamAlert.tsx +++ b/libs/remix-ui/home-tab/src/lib/components/homeTabScamAlert.tsx @@ -5,34 +5,34 @@ import { FormattedMessage } from 'react-intl' const _paq = window._paq = window._paq || [] // eslint-disable-line function HomeTabScamAlert () { - return ( -
- - + ) } export default HomeTabScamAlert diff --git a/libs/remix-ui/home-tab/src/lib/components/homeTabTitle.tsx b/libs/remix-ui/home-tab/src/lib/components/homeTabTitle.tsx index d73d1c979a..9e3e0f607c 100644 --- a/libs/remix-ui/home-tab/src/lib/components/homeTabTitle.tsx +++ b/libs/remix-ui/home-tab/src/lib/components/homeTabTitle.tsx @@ -7,181 +7,181 @@ const _paq = window._paq = window._paq || [] // eslint-disable-line function HomeTabTitle () { - useEffect(() => { - document.addEventListener("keyup", (e) => handleSearchKeyDown(e)) - return () => { - document.removeEventListener("keyup", handleSearchKeyDown) - } - }, []) - const [state, setState] = useState<{ + useEffect(() => { + document.addEventListener("keyup", (e) => handleSearchKeyDown(e)) + return () => { + document.removeEventListener("keyup", handleSearchKeyDown) + } + }, []) + const [state, setState] = useState<{ searchDisable: boolean }>({ - searchDisable: true + searchDisable: true }) - const searchInputRef = useRef(null) - const remiAudioEl = useRef(null) - const intl = useIntl() + const searchInputRef = useRef(null) + const remiAudioEl = useRef(null) + const intl = useIntl() - const playRemi = async () => { - remiAudioEl.current.play() - } - const handleSearchKeyDown = (e: KeyboardEvent) => { - if (e.target !== searchInputRef.current) return - if (e.key === "Enter") { - _paq.push(['trackEvent', 'hometab', 'header', 'searchDocumentation']) - openLink() - searchInputRef.current.value = "" - } else { - setState(prevState => { - return { ...prevState, searchDisable: searchInputRef.current.value === "" } - }) - } + const playRemi = async () => { + remiAudioEl.current.play() + } + const handleSearchKeyDown = (e: KeyboardEvent) => { + if (e.target !== searchInputRef.current) return + if (e.key === "Enter") { + _paq.push(['trackEvent', 'hometab', 'header', 'searchDocumentation']) + openLink() + searchInputRef.current.value = "" + } else { + setState(prevState => { + return { ...prevState, searchDisable: searchInputRef.current.value === "" } + }) } + } - const openLink = (url = "") => { - if (url === "") { - window.open("https://remix-ide.readthedocs.io/en/latest/search.html?q=" + searchInputRef.current.value + "&check_keywords=yes&area=default", '_blank') - } else { - window.open(url, '_blank') - } + const openLink = (url = "") => { + if (url === "") { + window.open("https://remix-ide.readthedocs.io/en/latest/search.html?q=" + searchInputRef.current.value + "&check_keywords=yes&area=default", '_blank') + } else { + window.open(url, '_blank') } + } - return ( -
-
-
- Remix -
-
playRemi()} > - -
- -
-
- - } - tooltipTextClasses="border bg-light text-dark p-1 pr-3" - > - - - } - tooltipTextClasses="border bg-light text-dark p-1 pr-3" - > - - - } - tooltipTextClasses="border bg-light text-dark p-1 pr-3" - > - - - } - tooltipTextClasses="border bg-light text-dark p-1 pr-3" - > - - - - } - tooltipTextClasses="border bg-light text-dark p-1 pr-3" - > - - - -
- - - - -
- - + return ( +
+
+
+ Remix +
+
playRemi()} > +
+ +
- ) + + } + tooltipTextClasses="border bg-light text-dark p-1 pr-3" + > + + + } + tooltipTextClasses="border bg-light text-dark p-1 pr-3" + > + + + } + tooltipTextClasses="border bg-light text-dark p-1 pr-3" + > + + + } + tooltipTextClasses="border bg-light text-dark p-1 pr-3" + > + + + + } + tooltipTextClasses="border bg-light text-dark p-1 pr-3" + > + + + +
+ + + + +
+ + +
+
+ ) } export default HomeTabTitle \ No newline at end of file diff --git a/libs/remix-ui/home-tab/src/lib/components/pluginButton.tsx b/libs/remix-ui/home-tab/src/lib/components/pluginButton.tsx index 426c84bf2f..d847dcdf74 100644 --- a/libs/remix-ui/home-tab/src/lib/components/pluginButton.tsx +++ b/libs/remix-ui/home-tab/src/lib/components/pluginButton.tsx @@ -13,34 +13,34 @@ interface PluginButtonProps { } function PluginButton ({ imgPath, envID, envText, callback, l2, description, remixMaintained }: PluginButtonProps) { - const themeFilter = useContext(ThemeContext) + const themeFilter = useContext(ThemeContext) - return ( -
- - { l2 && } - { remixMaintained && + return ( +
+ + { l2 && } + { remixMaintained && - + - } + } -
- ) +
+ ) } export default PluginButton diff --git a/libs/remix-ui/home-tab/src/lib/components/types/carouselTypes.ts b/libs/remix-ui/home-tab/src/lib/components/types/carouselTypes.ts index 128815d979..a2250622d2 100644 --- a/libs/remix-ui/home-tab/src/lib/components/types/carouselTypes.ts +++ b/libs/remix-ui/home-tab/src/lib/components/types/carouselTypes.ts @@ -9,9 +9,9 @@ export interface ResponsiveType { }; } export function isMouseMoveEvent( - e: React.MouseEvent | React.TouchEvent + e: React.MouseEvent | React.TouchEvent ): e is React.MouseEvent { - return "clientX" && "clientY" in e; + return "clientX" && "clientY" in e; } export interface CarouselProps { responsive: ResponsiveType; @@ -99,18 +99,18 @@ export interface CarouselInternalState { } export default class Carousel extends React.Component { - previous: (slidesHavePassed: number) => void; - next: (slidesHavePassed: number) => void; - goToSlide: (slide: number, skipCallbacks?: SkipCallbackOptions) => void; - state: CarouselInternalState; - setClones: ( + previous: (slidesHavePassed: number) => void; + next: (slidesHavePassed: number) => void; + goToSlide: (slide: number, skipCallbacks?: SkipCallbackOptions) => void; + state: CarouselInternalState; + setClones: ( slidesToShow: number, itemWidth?: number, forResizing?: boolean ) => void; // reset carousel in infinite mode. - setItemsToShow: (shouldCorrectItemPosition?: boolean) => void; // reset carousel in non-infinite mode. - correctClonesPosition: ({ domLoaded }: { domLoaded: boolean }) => void; - onMove: boolean; - direction: Direction; - containerRef: React.RefObject; + setItemsToShow: (shouldCorrectItemPosition?: boolean) => void; // reset carousel in non-infinite mode. + correctClonesPosition: ({ domLoaded }: { domLoaded: boolean }) => void; + onMove: boolean; + direction: Direction; + containerRef: React.RefObject; } diff --git a/libs/remix-ui/home-tab/src/lib/components/workspaceTemplate.tsx b/libs/remix-ui/home-tab/src/lib/components/workspaceTemplate.tsx index 4610236f79..ce749ad66f 100644 --- a/libs/remix-ui/home-tab/src/lib/components/workspaceTemplate.tsx +++ b/libs/remix-ui/home-tab/src/lib/components/workspaceTemplate.tsx @@ -9,20 +9,20 @@ interface WorkspaceTemplateProps { function WorkspaceTemplate ({ gsID, workspaceTitle, description, callback }: WorkspaceTemplateProps) { - return ( -
- + return ( +
+ +
+ ) } export default WorkspaceTemplate diff --git a/libs/remix-ui/home-tab/src/lib/remix-ui-home-tab.tsx b/libs/remix-ui/home-tab/src/lib/remix-ui-home-tab.tsx index 851d6fe5b1..ab2c7b5dc5 100644 --- a/libs/remix-ui/home-tab/src/lib/remix-ui-home-tab.tsx +++ b/libs/remix-ui/home-tab/src/lib/remix-ui-home-tab.tsx @@ -21,48 +21,48 @@ export interface RemixUiHomeTabProps { } export const RemixUiHomeTab = (props: RemixUiHomeTabProps) => { - const { plugin } = props + const { plugin } = props - const [state, setState] = useState<{ + const [state, setState] = useState<{ themeQuality: { filter: string, name: string }, }>({ - themeQuality: themes.light, + themeQuality: themes.light, }) - useEffect(() => { - plugin.call('theme', 'currentTheme').then((theme) => { - // update theme quality. To be used for for images - setState(prevState => { - return { ...prevState, themeQuality: theme.quality === 'dark' ? themes.dark : themes.light } - }) - }) - plugin.on('theme', 'themeChanged', (theme) => { - // update theme quality. To be used for for images - setState(prevState => { - return { ...prevState, themeQuality: theme.quality === 'dark' ? themes.dark : themes.light } - }) - }) - }, []) + useEffect(() => { + plugin.call('theme', 'currentTheme').then((theme) => { + // update theme quality. To be used for for images + setState(prevState => { + return { ...prevState, themeQuality: theme.quality === 'dark' ? themes.dark : themes.light } + }) + }) + plugin.on('theme', 'themeChanged', (theme) => { + // update theme quality. To be used for for images + setState(prevState => { + return { ...prevState, themeQuality: theme.quality === 'dark' ? themes.dark : themes.light } + }) + }) + }, []) - return ( -
- -
-
- - - -
-
- - - - -
-
-
+ return ( +
+ +
+
+ + + +
+
+ + + + +
- ) +
+
+ ) } export default RemixUiHomeTab diff --git a/libs/remix-ui/home-tab/src/lib/themeContext.tsx b/libs/remix-ui/home-tab/src/lib/themeContext.tsx index fc67007dac..6b43c29ddc 100644 --- a/libs/remix-ui/home-tab/src/lib/themeContext.tsx +++ b/libs/remix-ui/home-tab/src/lib/themeContext.tsx @@ -1,16 +1,16 @@ import React from 'react' // eslint-disable-line export const themes = { - light: { - filter: 'invert(0)', - name: 'light' - }, - dark: { - filter: 'invert(1)', - name: 'dark' - } + light: { + filter: 'invert(0)', + name: 'light' + }, + dark: { + filter: 'invert(1)', + name: 'dark' + } } export const ThemeContext = React.createContext( - themes.dark // default value + themes.dark // default value ) diff --git a/libs/remix-ui/locale-module/src/lib/remix-ui-locale-module.tsx b/libs/remix-ui/locale-module/src/lib/remix-ui-locale-module.tsx index 905a63f253..f5fe12fd7d 100644 --- a/libs/remix-ui/locale-module/src/lib/remix-ui-locale-module.tsx +++ b/libs/remix-ui/locale-module/src/lib/remix-ui-locale-module.tsx @@ -8,51 +8,51 @@ export interface RemixUiLocaleModuleProps { } export function RemixUiLocaleModule({ localeModule }: RemixUiLocaleModuleProps) { - const [localeCode, setLocaleCode] = useState('') + const [localeCode, setLocaleCode] = useState('') - useEffect(() => { - localeModule.switchLocale() - }, [localeCode, localeModule]) + useEffect(() => { + localeModule.switchLocale() + }, [localeCode, localeModule]) - return ( -
-
-
- -
-
- {localeModule.getLocales() - ? localeModule.getLocales().map((locale, idx) => ( -
- { - localeModule.switchLocale(locale.code); - setLocaleCode(locale.code); - }} - className="align-middle custom-control-input" - name="locale" - id={locale.code} - data-id={`settingsTabLocale${locale.code}`} - checked={localeModule.active === locale.code.toLocaleLowerCase()} - /> - -
- )) - : null} -
-
+ return ( +
+
+
+ +
+
+ {localeModule.getLocales() + ? localeModule.getLocales().map((locale, idx) => ( +
+ { + localeModule.switchLocale(locale.code); + setLocaleCode(locale.code); + }} + className="align-middle custom-control-input" + name="locale" + id={locale.code} + data-id={`settingsTabLocale${locale.code}`} + checked={localeModule.active === locale.code.toLocaleLowerCase()} + /> + +
+ )) + : null}
- ) +
+
+ ) } export default RemixUiLocaleModule; diff --git a/libs/remix-ui/modal-dialog/src/lib/modal-dialog-custom.tsx b/libs/remix-ui/modal-dialog/src/lib/modal-dialog-custom.tsx index 060b4503ab..040107e273 100644 --- a/libs/remix-ui/modal-dialog/src/lib/modal-dialog-custom.tsx +++ b/libs/remix-ui/modal-dialog/src/lib/modal-dialog-custom.tsx @@ -6,11 +6,11 @@ import './modal-dialog-custom.css' export interface ModalDialogCustomProps {} export const ModalDialogCustom = (props: ModalDialogCustomProps) => { - return ( -
-

Welcome to modal-dialog-custom!

-
- ) + return ( +
+

Welcome to modal-dialog-custom!

+
+ ) } export default ModalDialogCustom diff --git a/libs/remix-ui/modal-dialog/src/lib/remix-ui-modal-dialog.tsx b/libs/remix-ui/modal-dialog/src/lib/remix-ui-modal-dialog.tsx index bb02249b82..892803cece 100644 --- a/libs/remix-ui/modal-dialog/src/lib/remix-ui-modal-dialog.tsx +++ b/libs/remix-ui/modal-dialog/src/lib/remix-ui-modal-dialog.tsx @@ -9,124 +9,124 @@ declare global { } export const ModalDialog = (props: ModalDialogProps) => { - const [state, setState] = useState({ - toggleBtn: true - }) - const calledHideFunctionOnce = useRef() - const modal = useRef(null) - const handleHide = () => { - if (!calledHideFunctionOnce.current) { props.handleHide() } - calledHideFunctionOnce.current = true - } + const [state, setState] = useState({ + toggleBtn: true + }) + const calledHideFunctionOnce = useRef() + const modal = useRef(null) + const handleHide = () => { + if (!calledHideFunctionOnce.current) { props.handleHide() } + calledHideFunctionOnce.current = true + } - useEffect(() => { - calledHideFunctionOnce.current = props.hide - modal.current.focus() - }, [props.hide]) + useEffect(() => { + calledHideFunctionOnce.current = props.hide + modal.current.focus() + }, [props.hide]) - useEffect(() => { - function handleBlur (e) { - if (!e.currentTarget.contains(e.relatedTarget)) { - e.stopPropagation() - if (document.activeElement !== this) { - !window.testmode && handleHide() - } - } - } - if (modal.current) { - modal.current.addEventListener('blur', handleBlur) + useEffect(() => { + function handleBlur (e) { + if (!e.currentTarget.contains(e.relatedTarget)) { + e.stopPropagation() + if (document.activeElement !== this) { + !window.testmode && handleHide() } - return () => { - modal.current && modal.current.removeEventListener('blur', handleBlur) - } - }, [modal.current]) + } + } + if (modal.current) { + modal.current.addEventListener('blur', handleBlur) + } + return () => { + modal.current && modal.current.removeEventListener('blur', handleBlur) + } + }, [modal.current]) - const modalKeyEvent = (keyCode) => { - if (keyCode === 27) { // Esc - if (props.cancelFn) props.cancelFn() - handleHide() - } else if (keyCode === 13) { // Enter - enterHandler() - } else if (keyCode === 37) { - // todo && footerIsActive) { // Arrow Left - setState((prevState) => { return { ...prevState, toggleBtn: true } }) - } else if (keyCode === 39) { - // todo && footerIsActive) { // Arrow Right - setState((prevState) => { return { ...prevState, toggleBtn: false } }) - } + const modalKeyEvent = (keyCode) => { + if (keyCode === 27) { // Esc + if (props.cancelFn) props.cancelFn() + handleHide() + } else if (keyCode === 13) { // Enter + enterHandler() + } else if (keyCode === 37) { + // todo && footerIsActive) { // Arrow Left + setState((prevState) => { return { ...prevState, toggleBtn: true } }) + } else if (keyCode === 39) { + // todo && footerIsActive) { // Arrow Right + setState((prevState) => { return { ...prevState, toggleBtn: false } }) } + } - const enterHandler = () => { - if (state.toggleBtn) { - if (props.okFn) props.okFn() - } else { - if (props.cancelFn) props.cancelFn() - } - handleHide() + const enterHandler = () => { + if (state.toggleBtn) { + if (props.okFn) props.okFn() + } else { + if (props.cancelFn) props.cancelFn() } + handleHide() + } - return ( + return ( +
+
{ modalKeyEvent(keyCode) }} > -
-
{ modalKeyEvent(keyCode) }} - > -
-
- {props.title && props.title} -
- {!props.showCancelIcon && +
+
+ {props.title && props.title} +
+ {!props.showCancelIcon && handleHide()}> - + - } -
-
- {props.children ? props.children : props.message} -
-
- {/* todo add autofocus ^^ */} - { props.okLabel && - } - { props.cancelLabel && - } -
-
-
+ } +
+
+ {props.children ? props.children : props.message} +
+
+ {/* todo add autofocus ^^ */} + { props.okLabel && + } + { props.cancelLabel && + } +
- ) +
+
+ ) } export default ModalDialog diff --git a/libs/remix-ui/panel/src/lib/dragbar/dragbar.tsx b/libs/remix-ui/panel/src/lib/dragbar/dragbar.tsx index 642786e866..936737813e 100644 --- a/libs/remix-ui/panel/src/lib/dragbar/dragbar.tsx +++ b/libs/remix-ui/panel/src/lib/dragbar/dragbar.tsx @@ -11,43 +11,43 @@ interface IRemixDragBarUi { } const DragBar = (props: IRemixDragBarUi) => { - const [dragState, setDragState] = useState(false) - const [dragBarPosY, setDragBarPosY] = useState(0) - const nodeRef = React.useRef(null) // fix for strictmode + const [dragState, setDragState] = useState(false) + const [dragBarPosY, setDragBarPosY] = useState(0) + const nodeRef = React.useRef(null) // fix for strictmode - function stopDrag (e: MouseEvent, data: any) { - const h = window.innerHeight - data.y - props.refObject.current.setAttribute('style', `height: ${h}px;`) - setDragBarPosY(window.innerHeight - props.refObject.current.offsetHeight) - setDragState(false) - props.setHideStatus(false) - } - const handleResize = () => { - if (!props.refObject.current) return - setDragBarPosY(window.innerHeight - props.refObject.current.offsetHeight) - } + function stopDrag (e: MouseEvent, data: any) { + const h = window.innerHeight - data.y + props.refObject.current.setAttribute('style', `height: ${h}px;`) + setDragBarPosY(window.innerHeight - props.refObject.current.offsetHeight) + setDragState(false) + props.setHideStatus(false) + } + const handleResize = () => { + if (!props.refObject.current) return + setDragBarPosY(window.innerHeight - props.refObject.current.offsetHeight) + } - useEffect(() => { - handleResize() - }, [props.hidden]) + useEffect(() => { + handleResize() + }, [props.hidden]) - useEffect(() => { - window.addEventListener('resize', handleResize) - // TODO: not a good way to wait on the ref doms element to be rendered of course - setTimeout(() => - handleResize(), 2000) - return () => window.removeEventListener('resize', handleResize) - }, []) + useEffect(() => { + window.addEventListener('resize', handleResize) + // TODO: not a good way to wait on the ref doms element to be rendered of course + setTimeout(() => + handleResize(), 2000) + return () => window.removeEventListener('resize', handleResize) + }, []) - function startDrag () { - setDragState(true) - } - return <> -
- -
-
- + function startDrag () { + setDragState(true) + } + return <> +
+ +
+
+ } export default DragBar diff --git a/libs/remix-ui/panel/src/lib/main/main-panel.tsx b/libs/remix-ui/panel/src/lib/main/main-panel.tsx index 0b59d665e3..65d19bcc5f 100644 --- a/libs/remix-ui/panel/src/lib/main/main-panel.tsx +++ b/libs/remix-ui/panel/src/lib/main/main-panel.tsx @@ -10,64 +10,64 @@ export type RemixUIMainPanelProps = { } const RemixUIMainPanel = (props: RemixUIMainPanelProps) => { - const appContext = useContext(props.Context) - const [plugins, setPlugins] = useState([]) - const editorRef = useRef(null) - const mainPanelRef = useRef(null) - const tabsRef = useRef(null) - const terminalRef = useRef(null) + const appContext = useContext(props.Context) + const [plugins, setPlugins] = useState([]) + const editorRef = useRef(null) + const mainPanelRef = useRef(null) + const tabsRef = useRef(null) + const terminalRef = useRef(null) - const refs = [tabsRef, editorRef, mainPanelRef, terminalRef] + const refs = [tabsRef, editorRef, mainPanelRef, terminalRef] - const renderPanels = () => { - if (appContext) { - const pluginPanels: PluginRecord[] = [] - Object.values(appContext.layout.panels).map((panel: any) => { - pluginPanels.push({ - profile: panel.plugin.profile, - active: panel.active, - view: panel.plugin.profile.name === 'tabs' ? panel.plugin.renderTabsbar() : panel.plugin.render(), - class: panel.plugin.profile.name + '-wrap ' + (panel.minimized ? 'minimized' : ''), - minimized: panel.minimized - }) - }) - setPlugins(pluginPanels) - } - } - - useEffect(() => { - renderPanels() - appContext.layout.event.on('change', () => { - renderPanels() + const renderPanels = () => { + if (appContext) { + const pluginPanels: PluginRecord[] = [] + Object.values(appContext.layout.panels).map((panel: any) => { + pluginPanels.push({ + profile: panel.plugin.profile, + active: panel.active, + view: panel.plugin.profile.name === 'tabs' ? panel.plugin.renderTabsbar() : panel.plugin.render(), + class: panel.plugin.profile.name + '-wrap ' + (panel.minimized ? 'minimized' : ''), + minimized: panel.minimized }) + }) + setPlugins(pluginPanels) + } + } - return () => { - appContext.layout.event.off('change') - } - }, []) + useEffect(() => { + renderPanels() + appContext.layout.event.on('change', () => { + renderPanels() + }) - const showTerminal = (hide: boolean) => { - appContext.layout.panels.terminal.minimized = hide - appContext.layout.event.emit('change', appContext.layout.panels) - appContext.layout.emit('change', appContext.layout.panels) + return () => { + appContext.layout.event.off('change') } + }, []) + + const showTerminal = (hide: boolean) => { + appContext.layout.panels.terminal.minimized = hide + appContext.layout.event.emit('change', appContext.layout.panels) + appContext.layout.emit('change', appContext.layout.panels) + } - return ( -
- {Object.values(plugins).map((pluginRecord, i) => { - return ( - - {(pluginRecord.profile.name === 'terminal') ? : null} - - - ) - })} -
- ) + return ( +
+ {Object.values(plugins).map((pluginRecord, i) => { + return ( + + {(pluginRecord.profile.name === 'terminal') ? : null} + + + ) + })} +
+ ) } export default RemixUIMainPanel diff --git a/libs/remix-ui/panel/src/lib/plugins/panel-header.tsx b/libs/remix-ui/panel/src/lib/plugins/panel-header.tsx index c73f6c9fa0..aca580ed5a 100644 --- a/libs/remix-ui/panel/src/lib/plugins/panel-header.tsx +++ b/libs/remix-ui/panel/src/lib/plugins/panel-header.tsx @@ -8,90 +8,90 @@ export interface RemixPanelProps { plugins: Record; } const RemixUIPanelHeader = (props: RemixPanelProps) => { - const [plugin, setPlugin] = useState() - const [toggleExpander, setToggleExpander] = useState(false) + const [plugin, setPlugin] = useState() + const [toggleExpander, setToggleExpander] = useState(false) - useEffect(() => { - setToggleExpander(false) - if (props.plugins) { - const p = Object.values(props.plugins).find((pluginRecord) => { - return pluginRecord.active === true - }) - setPlugin(p) - } - }, [props]) - - const toggleClass = () => { - setToggleExpander(!toggleExpander) + useEffect(() => { + setToggleExpander(false) + if (props.plugins) { + const p = Object.values(props.plugins).find((pluginRecord) => { + return pluginRecord.active === true + }) + setPlugin(p) } + }, [props]) + + const toggleClass = () => { + setToggleExpander(!toggleExpander) + } - const tooltipChild = ( - - ) + const tooltipChild = ( + + ) - return ( -
-
-
- { plugin?.profile?.name && } -
-
-
- {plugin?.profile?.maintainedBy?.toLowerCase() === "remix" && ( - - - - )} -
-
- - {tooltipChild} - -
-
-
-
- {plugin?.profile?.author && - - { plugin?.profile.author } - } - {plugin?.profile?.maintainedBy && - - { plugin?.profile.maintainedBy } - } - {plugin?.profile?.documentation && - - - - - - - } - {plugin?.profile?.description && - - { plugin?.profile.description } - } - {plugin?.profile?.repo && - + return ( +
+
+
+ { plugin?.profile?.name && } +
+
+
+ {plugin?.profile?.maintainedBy?.toLowerCase() === "remix" && ( + + + + )} +
+
+ + {tooltipChild} + +
+
+
+
-
) +
} +
+
) } export default RemixUIPanelHeader diff --git a/libs/remix-ui/panel/src/lib/plugins/panel-plugin.tsx b/libs/remix-ui/panel/src/lib/plugins/panel-plugin.tsx index 9f35cb73e3..3af8609f7a 100644 --- a/libs/remix-ui/panel/src/lib/plugins/panel-plugin.tsx +++ b/libs/remix-ui/panel/src/lib/plugins/panel-plugin.tsx @@ -7,32 +7,32 @@ interface panelPLuginProps { } const RemixUIPanelPlugin = (props: panelPLuginProps, panelRef: any) => { - const localRef = useRef(null) - const [view, setView] = useState() - useEffect(() => { + const localRef = useRef(null) + const [view, setView] = useState() + useEffect(() => { - const ref:any = panelRef || localRef - if (ref.current) { - if (props.pluginRecord.view) { - if (React.isValidElement(props.pluginRecord.view)) { - setView(props.pluginRecord.view) - } else { - ref.current.appendChild(props.pluginRecord.view) - } - } + const ref:any = panelRef || localRef + if (ref.current) { + if (props.pluginRecord.view) { + if (React.isValidElement(props.pluginRecord.view)) { + setView(props.pluginRecord.view) + } else { + ref.current.appendChild(props.pluginRecord.view) } - }, []) + } + } + }, []) - return ( -
- <>{view} -
- ) + return ( +
+ <>{view} +
+ ) } export default forwardRef(RemixUIPanelPlugin) diff --git a/libs/remix-ui/panel/src/lib/plugins/remix-ui-panel.tsx b/libs/remix-ui/panel/src/lib/plugins/remix-ui-panel.tsx index 37fa018c92..bd77f402d9 100644 --- a/libs/remix-ui/panel/src/lib/plugins/remix-ui-panel.tsx +++ b/libs/remix-ui/panel/src/lib/plugins/remix-ui-panel.tsx @@ -11,19 +11,19 @@ export interface RemixPanelProps { } export function RemixPluginPanel (props: RemixPanelProps) { - return ( - <> - {props.header} -
-
- {Object.values(props.plugins).map((pluginRecord) => { - return - })} -
-
- + return ( + <> + {props.header} +
+
+ {Object.values(props.plugins).map((pluginRecord) => { + return + })} +
+
+ - ) + ) } export default RemixPluginPanel diff --git a/libs/remix-ui/permission-handler/src/lib/permission-dialog.tsx b/libs/remix-ui/permission-handler/src/lib/permission-dialog.tsx index ddff788cbb..a113f45dd0 100644 --- a/libs/remix-ui/permission-handler/src/lib/permission-dialog.tsx +++ b/libs/remix-ui/permission-handler/src/lib/permission-dialog.tsx @@ -4,65 +4,65 @@ import { PermissionHandlerProps } from '../interface' import './permission-dialog.css' const PermissionHandlerDialog = (props: PermissionHandlerProps) => { - const { from, to, remember, method, message, sensitiveCall } = props.value - const [feedback, setFeedback] = useState('') - const theme = props.theme - const intl = useIntl() + const { from, to, remember, method, message, sensitiveCall } = props.value + const [feedback, setFeedback] = useState('') + const theme = props.theme + const intl = useIntl() - const switchMode = (e: any) => { - props.plugin.switchMode(from, to, method, e.target.checked, sensitiveCall) - } + const switchMode = (e: any) => { + props.plugin.switchMode(from, to, method, e.target.checked, sensitiveCall) + } - const reset = () => { - props.plugin.clear() - setFeedback(intl.formatMessage({ id: 'permissionHandler.allPermissionsReset' })) - } + const reset = () => { + props.plugin.clear() + setFeedback(intl.formatMessage({ id: 'permissionHandler.allPermissionsReset' })) + } - const imgFrom = () => { return } - const imgTo = () => { return } - const pluginsImages = () => { - return ( -
- {imgFrom()} - - {imgTo()} -
- ) - } + const imgFrom = () => { return } + const imgTo = () => { return } + const pluginsImages = () => { + return ( +
+ {imgFrom()} + + {imgTo()} +
+ ) + } - const text = () => { - return - } + const text = () => { + return + } - const pluginMessage = () => { - return message - ?
-
-

{message}

-
: null - } + const pluginMessage = () => { + return message + ?
+
+

{message}

+
: null + } - return (
- {pluginsImages()} -
-

{text()} :

-
{from.displayName}
-

{from.description || }

-
{to.displayName} :
-

{to.description || }

- {pluginMessage()} - { sensitiveCall ?

: '' } -
-
- {
- - -
- } - -
-
{feedback}
-
) + return (
+ {pluginsImages()} +
+

{text()} :

+
{from.displayName}
+

{from.description || }

+
{to.displayName} :
+

{to.description || }

+ {pluginMessage()} + { sensitiveCall ?

: '' } +
+
+ {
+ + +
+ } + +
+
{feedback}
+
) } export default PermissionHandlerDialog diff --git a/libs/remix-ui/plugin-manager/src/lib/components/ActivePluginCard.tsx b/libs/remix-ui/plugin-manager/src/lib/components/ActivePluginCard.tsx index 8290f1c8c3..771892e283 100644 --- a/libs/remix-ui/plugin-manager/src/lib/components/ActivePluginCard.tsx +++ b/libs/remix-ui/plugin-manager/src/lib/components/ActivePluginCard.tsx @@ -9,87 +9,87 @@ interface PluginCardProps { } function ActivePluginCard ({ - profile, - buttonText, - deactivatePlugin + profile, + buttonText, + deactivatePlugin }: PluginCardProps) { - return ( -
-
-
-
-
- { profile.displayName || profile.name } - { profile?.maintainedBy?.toLowerCase() == "remix" && + return ( +
+
+
+
+
+ { profile.displayName || profile.name } + { profile?.maintainedBy?.toLowerCase() == "remix" && - + - } - { profile.documentation && + } + { profile.documentation && - -