diff --git a/apps/remix-ide-e2e/src/tests/debugger.test.ts b/apps/remix-ide-e2e/src/tests/debugger.test.ts index 19918de0c4..e55ae8d6fd 100644 --- a/apps/remix-ide-e2e/src/tests/debugger.test.ts +++ b/apps/remix-ide-e2e/src/tests/debugger.test.ts @@ -205,6 +205,7 @@ module.exports = { .clickLaunchIcon('debugger') .waitForElementVisible('*[data-id="slider"]') .goToVMTraceStep(154) + .scrollInto('*[data-id="stepdetail"]') .waitForElementContainsText('*[data-id="stepdetail"]', 'vm trace step:\n154', 60000) }, diff --git a/apps/remix-ide-e2e/src/tests/editor_error_marker.test.ts b/apps/remix-ide-e2e/src/tests/editor_error_marker.test.ts new file mode 100644 index 0000000000..8d8cba54e0 --- /dev/null +++ b/apps/remix-ide-e2e/src/tests/editor_error_marker.test.ts @@ -0,0 +1,84 @@ +'use strict' + +import { NightwatchBrowser } from 'nightwatch' +import init from '../helpers/init' + +module.exports = { + + before: function (browser: NightwatchBrowser, done: VoidFunction) { + init(browser, done, 'http://127.0.0.1:8080', true) + }, + 'Should add error marker': function (browser: NightwatchBrowser) { + browser + .openFile('contracts') + .openFile('contracts/1_Storage.sol') + .addFile('scripts/adderror.ts', {content: addErrorMarker}) + .pause(4000) + .executeScriptInTerminal('remix.exeCurrent()') + .pause(4000) + .openFile('contracts/1_Storage.sol') + .useXpath() + .waitForElementVisible("//*[@class='cdr squiggly-error']") + .waitForElementVisible("//*[@class='cdr squiggly-warning']") + }, + 'Should clear error marker': function (browser: NightwatchBrowser) { + browser + .useCss() + .addFile('scripts/clear.ts', {content: clearMarkers}) + .pause(4000) + .executeScriptInTerminal('remix.exeCurrent()') + .pause(4000) + .openFile('contracts/1_Storage.sol') + .useXpath() + .waitForElementNotPresent("//*[@class='cdr squiggly-error']") + .waitForElementNotPresent("//*[@class='cdr squiggly-warning']") + } +} + +const clearMarkers =` +(async () => { + await remix.call('editor', 'clearErrorMarkers' as any, ['contracts/1_Storage.sol']) +})()` + +const addErrorMarker = ` +(async () => { + + + let errors = [ + { + position: { + start: { + line: 10, + column: 1, + }, + end: { + line: 10, + column: 10 + } + }, + message: 'testing', + severity: 'error', + file: 'contracts/1_Storage.sol' + }, + { + position: { + start: { + line: 18, + column: 1, + }, + end: { + line: 18, + column: 10 + } + }, + message: 'testing2', + severity: 'warning', + file: 'contracts/1_Storage.sol' + }, + ] + + + await remix.call('editor', 'addErrorMarker' as any, errors) + + +})()` \ No newline at end of file diff --git a/apps/remix-ide-e2e/src/tests/gist.test.ts b/apps/remix-ide-e2e/src/tests/gist.test.ts index c1ba2cfcaa..0c27e28cdf 100644 --- a/apps/remix-ide-e2e/src/tests/gist.test.ts +++ b/apps/remix-ide-e2e/src/tests/gist.test.ts @@ -9,10 +9,11 @@ const testData = { // 99266d6da54cc12f37f11586e8171546c7700d67 module.exports = { + '@disabled': true, before: function (browser: NightwatchBrowser, done: VoidFunction) { init(browser, done) }, - UploadToGists: function (browser: NightwatchBrowser) { + 'UploadToGists #group1': function (browser: NightwatchBrowser) { /* - set the access token - publish to gist @@ -68,7 +69,7 @@ module.exports = { */ }, - 'Load Gist Modal': function (browser: NightwatchBrowser) { + 'Load Gist Modal #group1': function (browser: NightwatchBrowser) { browser.clickLaunchIcon('home') .waitForElementVisible('*[data-id="remixIdeIconPanel"]', 10000) .clickLaunchIcon('filePanel') @@ -84,7 +85,7 @@ module.exports = { .modalFooterCancelClick('gisthandler') }, - 'Display Error Message For Invalid Gist ID': function (browser: NightwatchBrowser) { + 'Display Error Message For Invalid Gist ID #group1': function (browser: NightwatchBrowser) { browser .pause(1000) .waitForElementVisible('*[data-id="remixIdeIconPanel"]', 10000) @@ -101,7 +102,7 @@ module.exports = { .modalFooterOKClick('gisthandler') }, - 'Display Error Message For Missing Gist Token When Publishing': function (browser: NightwatchBrowser) { + 'Display Error Message For Missing Gist Token When Publishing #group1': function (browser: NightwatchBrowser) { browser .pause(1000) .waitForElementVisible('*[data-id="remixIdeIconPanel"]', 10000) @@ -125,7 +126,7 @@ module.exports = { .click('[data-id="fileSystemModalDialogModalFooter-react"] .modal-ok') }, - 'Import From Gist For Valid Gist ID': function (browser: NightwatchBrowser) { + 'Import From Gist For Valid Gist ID #group2': function (browser: NightwatchBrowser) { browser .waitForElementVisible('*[data-id="remixIdeIconPanel"]', 15000) .clickLaunchIcon('settings') @@ -140,6 +141,7 @@ module.exports = { }) .setValue('*[data-id="gisthandlerModalDialogModalBody-react"] input[data-id="modalDialogCustomPromp"]', testData.validGistId) .modalFooterOKClick('gisthandler') + .pause(10000) .openFile(`gist-${testData.validGistId}/README.txt`) .waitForElementVisible(`div[title='default_workspace/gist-${testData.validGistId}/README.txt']`) .assert.containsText(`div[title='default_workspace/gist-${testData.validGistId}/README.txt'] > span`, 'README.txt') diff --git a/apps/remix-ide-e2e/src/tests/terminal.test.ts b/apps/remix-ide-e2e/src/tests/terminal.test.ts index f944b453f9..baf0639cc5 100644 --- a/apps/remix-ide-e2e/src/tests/terminal.test.ts +++ b/apps/remix-ide-e2e/src/tests/terminal.test.ts @@ -217,13 +217,42 @@ module.exports = { .addFile('scripts/log_tx_block.js', { content: scriptBlockAndTransaction } ) .pause(1000) .executeScriptInTerminal('remix.execute(\'scripts/log_tx_block.js\')') - .pause(10000) // check if the input of the transaction is being logged (web3 call) - .journalChildIncludes('0x775526410000000000000000000000000000000000000000000000000000000000000060464c0335b2f1609abd9de25141c0a3b49db516fc7375970dc737c32b986e88e3000000000000000000000000000000000000000000000000000000000000039e000000000000000000000000000000000000000000000000000000000000000602926b30b10e7a514d92bc71e085f5bff2687fac2856ae43ef7621bf1756fa370516d310bec5727543089be9a4d5f68471174ee528e95a2520b0ca36c2b6c6eb0000000000000000000000000000000000000000000000000000000000046f49036f5e4ea4dd042801c8841e3db8e654124305da0f11824fc1db60c405dbb39f00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000') + .waitForElementContainsText('*[data-id="terminalJournal"]', '0x775526410000000000000000000000000000000000000000000000000000000000000060464c0335b2f1609abd9de25141c0a3b49db516fc7375970dc737c32b986e88e3000000000000000000000000000000000000000000000000000000000000039e000000000000000000000000000000000000000000000000000000000000000602926b30b10e7a514d92bc71e085f5bff2687fac2856ae43ef7621bf1756fa370516d310bec5727543089be9a4d5f68471174ee528e95a2520b0ca36c2b6c6eb0000000000000000000000000000000000000000000000000000000000046f49036f5e4ea4dd042801c8841e3db8e654124305da0f11824fc1db60c405dbb39f00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000', 120000) // check if the logsBloom is being logged (web3 call) - .journalChildIncludes('0x00000000000000000000000000100000000000000000020000000000002000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000000000000040000000060000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000100000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000001') + .waitForElementContainsText('*[data-id="terminalJournal"]', '0x00000000000000000000000000100000000000000000020000000000002000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000000000000040000000060000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000100000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000001', 120000) // check if the logsBloom is being logged (ethers.js call) - .journalChildIncludes('"hex":"0x025cd8"') + .waitForElementContainsText('*[data-id="terminalJournal"]', '"hex":"0x025cd8"', 120000) + }, + + 'Should listen on all transactions #group8': function (browser: NightwatchBrowser) { + browser + .clickLaunchIcon('udapp') // connect to mainnet + .switchEnvironment('External Http Provider') + .waitForElementPresent('[data-id="basic-http-provider-modal-footer-ok-react"]') + .execute(() => { + (document.querySelector('*[data-id="basic-http-providerModalDialogContainer-react"] input[data-id="modalDialogCustomPromp"]') as any).focus() + }, [], () => {}) + .setValue('[data-id="modalDialogCustomPromp"]', 'https://rpc.archivenode.io/e50zmkroshle2e2e50zm0044i7ao04ym') + .modalFooterOKClick('basic-http-provider') + .click('[data-id="terminalClearConsole"]') // clear the console + .click('[data-id="listenNetworkCheckInput"]') // start to listen + .waitForElementContainsText('*[data-id="terminalJournal"]', 'from:', 200000) + .waitForElementContainsText('*[data-id="terminalJournal"]', 'to:', 200000) + .click('[data-id="terminalClearConsole"]') // clear the console + .waitForElementContainsText('*[data-id="terminalJournal"]', 'from:', 200000) + .waitForElementContainsText('*[data-id="terminalJournal"]', 'to:', 200000) + .click('[data-id="listenNetworkCheckInput"]') // stop to listen + .pause(30000) + .click('[data-id="terminalClearConsole"]') // clear the console + .pause(5000) + .click('[data-id="terminalClearConsole"]') // clear the console + .pause(20000) + .execute(function () { + return (document.querySelector('[data-id="terminalJournal"]') as any).innerText + }, [], function (result) { + browser.assert.equal(result.value, '', 'terminal log should be empty') + }) } } diff --git a/apps/remix-ide-e2e/src/tests/vyper_api.ts b/apps/remix-ide-e2e/src/tests/vyper_api.ts index f168372963..bb0afc3859 100644 --- a/apps/remix-ide-e2e/src/tests/vyper_api.ts +++ b/apps/remix-ide-e2e/src/tests/vyper_api.ts @@ -21,25 +21,30 @@ module.exports = { .frame(0) }, - 'Should add the Ballot.vy #group1': function (browser: NightwatchBrowser) { - browser.click('button[data-id="add-ballot"]') + 'Should clone the Vyper repo #group1': function (browser: NightwatchBrowser) { + browser.click('button[data-id="add-repository"]') .frameParent() - .openFile('ballot.vy') + .waitForElementContainsText('*[data-shared="tooltipPopup"]', 'Vyper repository cloned', 30000) + .openFile('examples') + .openFile('examples/auctions') + .openFile('examples/auctions/blind_auction.vy') }, - 'Compile ballot.vy should error #group1': function (browser: NightwatchBrowser) { + 'Compile blind_auction should success #group1': function (browser: NightwatchBrowser) { browser.clickLaunchIcon('vyper') // @ts-ignore .frame(0) .click('[data-id="remote-compiler"]') .click('[data-id="compile"]') - .assert.containsText('[data-id="error-message"]', 'unexpected indent') + .waitForElementVisible('[data-id="copy-abi"]') }, - 'Compile test contract should success #group1': function (browser: NightwatchBrowser) { + 'Compile test contract and deploy to remix VM #group1': function (browser: NightwatchBrowser) { let contractAddress browser .frameParent() + .clickLaunchIcon('filePanel') + .switchWorkspace('default_workspace') .addFile('test.vy', { content: testContract }) .clickLaunchIcon('vyper') // @ts-ignore diff --git a/apps/remix-ide/src/app/editor/editor.js b/apps/remix-ide/src/app/editor/editor.js index 34b9c172e5..f46b1b2d21 100644 --- a/apps/remix-ide/src/app/editor/editor.js +++ b/apps/remix-ide/src/app/editor/editor.js @@ -13,7 +13,7 @@ const profile = { name: 'editor', description: 'service - editor', version: packageJson.version, - methods: ['highlight', 'discardHighlight', 'clearAnnotations', 'addLineText', 'discardLineTexts', 'addAnnotation', 'gotoLine', 'revealRange', 'getCursorPosition'] + methods: ['highlight', 'discardHighlight', 'clearAnnotations', 'addLineText', 'discardLineTexts', 'addAnnotation', 'gotoLine', 'revealRange', 'getCursorPosition', 'addErrorMarker', 'clearErrorMarkers'] } class Editor extends Plugin { @@ -504,6 +504,15 @@ class Editor extends Plugin { } } + // error markers + async addErrorMarker (error){ + this.api.addErrorMarker(error) + } + + async clearErrorMarkers(sources){ + this.api.clearErrorMarkers(sources) + } + /** * 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: diff --git a/apps/remix-ide/src/app/tabs/debugger-tab.js b/apps/remix-ide/src/app/tabs/debugger-tab.js index a06db0c508..5e62bd93d4 100644 --- a/apps/remix-ide/src/app/tabs/debugger-tab.js +++ b/apps/remix-ide/src/app/tabs/debugger-tab.js @@ -3,7 +3,7 @@ import { DebuggerApiMixin } from '@remixproject/debugger-plugin' // eslint-disab import { ViewPlugin } from '@remixproject/engine-web' import * as packageJson from '../../../../../package.json' import React from 'react' // eslint-disable-line -import * as remixBleach from '../../lib/remixBleach' +import { bleach } from '@remix-ui/helper' import { compilationFinishedToastMsg, compilingToastMsg, localCompilationToastMsg, notFoundToastMsg, sourceVerificationNotAvailableToastMsg } from '@remix-ui/helper' const css = require('./styles/debugger-tab-styles') @@ -51,7 +51,7 @@ export class DebuggerTab extends DebuggerApiMixin(ViewPlugin) { this.on('fetchAndCompile', 'sourceVerificationNotAvailable', () => { this.call('notification', 'toast', sourceVerificationNotAvailableToastMsg()) }) - return
+ return
} showMessage (title, message) { @@ -59,7 +59,7 @@ export class DebuggerTab extends DebuggerApiMixin(ViewPlugin) { this.call('notification', 'alert', { id: 'debuggerTabShowMessage', title, - message: remixBleach.sanitize(message) + message: bleach.sanitize(message) }) } catch (e) { console.log(e) diff --git a/apps/remix-ide/src/app/tabs/styles/debugger-tab-styles.js b/apps/remix-ide/src/app/tabs/styles/debugger-tab-styles.js index 6a15d2cf56..3fb7add424 100644 --- a/apps/remix-ide/src/app/tabs/styles/debugger-tab-styles.js +++ b/apps/remix-ide/src/app/tabs/styles/debugger-tab-styles.js @@ -1,9 +1,6 @@ var csjs = require('csjs-inject') const css = csjs` - .debuggerTabView { - padding: 2%; - } .debugger { margin-bottom: 1%; } diff --git a/apps/remix-ide/src/assets/css/themes/bootstrap-cerulean.min.css b/apps/remix-ide/src/assets/css/themes/bootstrap-cerulean.min.css index f95afd68ec..98d4b409c3 100644 --- a/apps/remix-ide/src/assets/css/themes/bootstrap-cerulean.min.css +++ b/apps/remix-ide/src/assets/css/themes/bootstrap-cerulean.min.css @@ -34,6 +34,7 @@ --dark:#343a40; --body-bg: #fff; --text-bg-mark: #fcf8e3; + --custom-select: #fff; --breakpoint-xs:0; --breakpoint-sm:576px; --breakpoint-md:768px; @@ -435,25 +436,25 @@ pre code { .container,.container-sm { max-width:540px } - + } @media (min-width:768px) { .container,.container-md,.container-sm { max-width:720px } - + } @media (min-width:992px) { .container,.container-lg,.container-md,.container-sm { max-width:960px } - + } @media (min-width:1200px) { .container,.container-lg,.container-md,.container-sm,.container-xl { max-width:1140px } - + } .row { display:-ms-flexbox; @@ -873,7 +874,7 @@ pre code { .offset-sm-11 { margin-left:91.666667% } - + } @media (min-width:768px) { .col-md { @@ -1075,7 +1076,7 @@ pre code { .offset-md-11 { margin-left:91.666667% } - + } @media (min-width:992px) { .col-lg { @@ -1277,7 +1278,7 @@ pre code { .offset-lg-11 { margin-left:91.666667% } - + } @media (min-width:1200px) { .col-xl { @@ -1479,7 +1480,7 @@ pre code { .offset-xl-11 { margin-left:91.666667% } - + } .table { width:100%; @@ -1662,7 +1663,7 @@ pre code { .table-responsive-sm>.table-bordered { border:0 } - + } @media (max-width:767.98px) { .table-responsive-md { @@ -1674,7 +1675,7 @@ pre code { .table-responsive-md>.table-bordered { border:0 } - + } @media (max-width:991.98px) { .table-responsive-lg { @@ -1686,7 +1687,7 @@ pre code { .table-responsive-lg>.table-bordered { border:0 } - + } @media (max-width:1199.98px) { .table-responsive-xl { @@ -1698,7 +1699,7 @@ pre code { .table-responsive-xl>.table-bordered { border:0 } - + } .table-responsive { display:block; @@ -1728,7 +1729,7 @@ pre code { .form-control { transition:none } - + } .form-control::-ms-expand { background-color:transparent; @@ -2114,7 +2115,7 @@ textarea.form-control { .form-inline .custom-control-label { margin-bottom:0 } - + } .btn { display:inline-block; @@ -2138,7 +2139,7 @@ textarea.form-control { .btn { transition:none } - + } .btn:hover { color:#495057; @@ -2626,7 +2627,7 @@ input[type=button].btn-block,input[type=reset].btn-block,input[type=submit].btn- .fade { transition:none } - + } .fade:not(.show) { opacity:0 @@ -2644,7 +2645,7 @@ input[type=button].btn-block,input[type=reset].btn-block,input[type=submit].btn- .collapsing { transition:none } - + } .dropdown,.dropleft,.dropright,.dropup { position:relative @@ -2701,7 +2702,7 @@ input[type=button].btn-block,input[type=reset].btn-block,input[type=submit].btn- right:0; left:auto } - + } @media (min-width:768px) { .dropdown-menu-md-left { @@ -2712,7 +2713,7 @@ input[type=button].btn-block,input[type=reset].btn-block,input[type=submit].btn- right:0; left:auto } - + } @media (min-width:992px) { .dropdown-menu-lg-left { @@ -2723,7 +2724,7 @@ input[type=button].btn-block,input[type=reset].btn-block,input[type=submit].btn- right:0; left:auto } - + } @media (min-width:1200px) { .dropdown-menu-xl-left { @@ -2734,7 +2735,7 @@ input[type=button].btn-block,input[type=reset].btn-block,input[type=submit].btn- right:0; left:auto } - + } .dropup .dropdown-menu { top:auto; @@ -3190,7 +3191,7 @@ input[type=button].btn-block,input[type=reset].btn-block,input[type=submit].btn- .custom-switch .custom-control-label::after { transition:none } - + } .custom-switch .custom-control-input:checked~.custom-control-label::after { background-color:#fff; @@ -3356,7 +3357,7 @@ input[type=button].btn-block,input[type=reset].btn-block,input[type=submit].btn- -webkit-transition:none; transition:none } - + } .custom-range::-webkit-slider-thumb:active { background-color:#cfeaf9 @@ -3386,7 +3387,7 @@ input[type=button].btn-block,input[type=reset].btn-block,input[type=submit].btn- -moz-transition:none; transition:none } - + } .custom-range::-moz-range-thumb:active { background-color:#cfeaf9 @@ -3418,7 +3419,7 @@ input[type=button].btn-block,input[type=reset].btn-block,input[type=submit].btn- -ms-transition:none; transition:none } - + } .custom-range::-ms-thumb:active { background-color:#cfeaf9 @@ -3463,7 +3464,7 @@ input[type=button].btn-block,input[type=reset].btn-block,input[type=submit].btn- .custom-control-label::before,.custom-file-label,.custom-select { transition:none } - + } .nav { display:-ms-flexbox; @@ -3629,7 +3630,7 @@ input[type=button].btn-block,input[type=reset].btn-block,input[type=submit].btn- padding-right:0; padding-left:0 } - + } @media (min-width:576px) { .navbar-expand-sm { @@ -3662,14 +3663,14 @@ input[type=button].btn-block,input[type=reset].btn-block,input[type=submit].btn- .navbar-expand-sm .navbar-toggler { display:none } - + } @media (max-width:767.98px) { .navbar-expand-md>.container,.navbar-expand-md>.container-fluid,.navbar-expand-md>.container-lg,.navbar-expand-md>.container-md,.navbar-expand-md>.container-sm,.navbar-expand-md>.container-xl { padding-right:0; padding-left:0 } - + } @media (min-width:768px) { .navbar-expand-md { @@ -3702,14 +3703,14 @@ input[type=button].btn-block,input[type=reset].btn-block,input[type=submit].btn- .navbar-expand-md .navbar-toggler { display:none } - + } @media (max-width:991.98px) { .navbar-expand-lg>.container,.navbar-expand-lg>.container-fluid,.navbar-expand-lg>.container-lg,.navbar-expand-lg>.container-md,.navbar-expand-lg>.container-sm,.navbar-expand-lg>.container-xl { padding-right:0; padding-left:0 } - + } @media (min-width:992px) { .navbar-expand-lg { @@ -3742,14 +3743,14 @@ input[type=button].btn-block,input[type=reset].btn-block,input[type=submit].btn- .navbar-expand-lg .navbar-toggler { display:none } - + } @media (max-width:1199.98px) { .navbar-expand-xl>.container,.navbar-expand-xl>.container-fluid,.navbar-expand-xl>.container-lg,.navbar-expand-xl>.container-md,.navbar-expand-xl>.container-sm,.navbar-expand-xl>.container-xl { padding-right:0; padding-left:0 } - + } @media (min-width:1200px) { .navbar-expand-xl { @@ -3782,7 +3783,7 @@ input[type=button].btn-block,input[type=reset].btn-block,input[type=submit].btn- .navbar-expand-xl .navbar-toggler { display:none } - + } .navbar-expand { -ms-flex-flow:row nowrap; @@ -4010,7 +4011,7 @@ input[type=button].btn-block,input[type=reset].btn-block,input[type=submit].btn- margin-bottom:0; margin-left:15px } - + } .card-group>.card { margin-bottom:15px @@ -4051,7 +4052,7 @@ input[type=button].btn-block,input[type=reset].btn-block,input[type=submit].btn- .card-group>.card:not(:first-child) .card-footer,.card-group>.card:not(:first-child) .card-img-bottom { border-bottom-left-radius:0 } - + } .card-columns .card { margin-bottom:.75rem @@ -4071,7 +4072,7 @@ input[type=button].btn-block,input[type=reset].btn-block,input[type=submit].btn- display:inline-block; width:100% } - + } .accordion { overflow-anchor:none @@ -4218,7 +4219,7 @@ input[type=button].btn-block,input[type=reset].btn-block,input[type=submit].btn- .badge { transition:none } - + } a.badge:focus,a.badge:hover { text-decoration:none @@ -4341,7 +4342,7 @@ a.badge-dark.focus,a.badge-dark:focus { .jumbotron { padding:4rem 2rem } - + } .jumbotron-fluid { padding-right:0; @@ -4467,7 +4468,7 @@ a.badge-dark.focus,a.badge-dark:focus { to { background-position:0 0 } - + } @keyframes progress-bar-stripes { from { @@ -4476,7 +4477,7 @@ a.badge-dark.focus,a.badge-dark:focus { to { background-position:0 0 } - + } .progress { display:-ms-flexbox; @@ -4506,7 +4507,7 @@ a.badge-dark.focus,a.badge-dark:focus { .progress-bar { transition:none } - + } .progress-bar-striped { background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent); @@ -4521,7 +4522,7 @@ a.badge-dark.focus,a.badge-dark:focus { -webkit-animation:none; animation:none } - + } .media { display:-ms-flexbox; @@ -4637,7 +4638,7 @@ a.badge-dark.focus,a.badge-dark:focus { margin-left:-1px; border-left-width:1px } - + } @media (min-width:768px) { .list-group-horizontal-md { @@ -4663,7 +4664,7 @@ a.badge-dark.focus,a.badge-dark:focus { margin-left:-1px; border-left-width:1px } - + } @media (min-width:992px) { .list-group-horizontal-lg { @@ -4689,7 +4690,7 @@ a.badge-dark.focus,a.badge-dark:focus { margin-left:-1px; border-left-width:1px } - + } @media (min-width:1200px) { .list-group-horizontal-xl { @@ -4715,7 +4716,7 @@ a.badge-dark.focus,a.badge-dark:focus { margin-left:-1px; border-left-width:1px } - + } .list-group-flush { border-radius:0 @@ -4930,7 +4931,7 @@ a.close.disabled { .modal.fade .modal-dialog { transition:none } - + } .modal.show .modal-dialog { -webkit-transform:none; @@ -5086,19 +5087,19 @@ a.close.disabled { .modal-sm { max-width:300px } - + } @media (min-width:992px) { .modal-lg,.modal-xl { max-width:800px } - + } @media (min-width:1200px) { .modal-xl { max-width:1140px } - + } .tooltip { position:absolute; @@ -5365,7 +5366,7 @@ a.close.disabled { .carousel-item { transition:none } - + } .carousel-item-next,.carousel-item-prev,.carousel-item.active { display:block @@ -5397,7 +5398,7 @@ a.close.disabled { .carousel-fade .active.carousel-item-left,.carousel-fade .active.carousel-item-right { transition:none } - + } .carousel-control-next,.carousel-control-prev { position:absolute; @@ -5420,7 +5421,7 @@ a.close.disabled { .carousel-control-next,.carousel-control-prev { transition:none } - + } .carousel-control-next:focus,.carousel-control-next:hover,.carousel-control-prev:focus,.carousel-control-prev:hover { color:#fff; @@ -5482,7 +5483,7 @@ a.close.disabled { .carousel-indicators li { transition:none } - + } .carousel-indicators .active { opacity:1 @@ -5503,14 +5504,14 @@ a.close.disabled { -webkit-transform:rotate(360deg); transform:rotate(360deg) } - + } @keyframes spinner-border { to { -webkit-transform:rotate(360deg); transform:rotate(360deg) } - + } .spinner-border { display:inline-block; @@ -5538,7 +5539,7 @@ a.close.disabled { -webkit-transform:none; transform:none } - + } @keyframes spinner-grow { 0% { @@ -5550,7 +5551,7 @@ a.close.disabled { -webkit-transform:none; transform:none } - + } .spinner-grow { display:inline-block; @@ -5794,7 +5795,7 @@ a.bg-dark:focus,a.bg-dark:hover,button.bg-dark:focus,button.bg-dark:hover { display:-ms-inline-flexbox!important; display:inline-flex!important } - + } @media (min-width:768px) { .d-md-none { @@ -5826,7 +5827,7 @@ a.bg-dark:focus,a.bg-dark:hover,button.bg-dark:focus,button.bg-dark:hover { display:-ms-inline-flexbox!important; display:inline-flex!important } - + } @media (min-width:992px) { .d-lg-none { @@ -5858,7 +5859,7 @@ a.bg-dark:focus,a.bg-dark:hover,button.bg-dark:focus,button.bg-dark:hover { display:-ms-inline-flexbox!important; display:inline-flex!important } - + } @media (min-width:1200px) { .d-xl-none { @@ -5890,7 +5891,7 @@ a.bg-dark:focus,a.bg-dark:hover,button.bg-dark:focus,button.bg-dark:hover { display:-ms-inline-flexbox!important; display:inline-flex!important } - + } @media print { .d-print-none { @@ -5922,7 +5923,7 @@ a.bg-dark:focus,a.bg-dark:hover,button.bg-dark:focus,button.bg-dark:hover { display:-ms-inline-flexbox!important; display:inline-flex!important } - + } .embed-responsive { position:relative; @@ -6229,7 +6230,7 @@ a.bg-dark:focus,a.bg-dark:hover,button.bg-dark:focus,button.bg-dark:hover { -ms-flex-item-align:stretch!important; align-self:stretch!important } - + } @media (min-width:768px) { .flex-md-row { @@ -6368,7 +6369,7 @@ a.bg-dark:focus,a.bg-dark:hover,button.bg-dark:focus,button.bg-dark:hover { -ms-flex-item-align:stretch!important; align-self:stretch!important } - + } @media (min-width:992px) { .flex-lg-row { @@ -6507,7 +6508,7 @@ a.bg-dark:focus,a.bg-dark:hover,button.bg-dark:focus,button.bg-dark:hover { -ms-flex-item-align:stretch!important; align-self:stretch!important } - + } @media (min-width:1200px) { .flex-xl-row { @@ -6646,7 +6647,7 @@ a.bg-dark:focus,a.bg-dark:hover,button.bg-dark:focus,button.bg-dark:hover { -ms-flex-item-align:stretch!important; align-self:stretch!important } - + } .float-left { float:left!important @@ -6667,7 +6668,7 @@ a.bg-dark:focus,a.bg-dark:hover,button.bg-dark:focus,button.bg-dark:hover { .float-sm-none { float:none!important } - + } @media (min-width:768px) { .float-md-left { @@ -6679,7 +6680,7 @@ a.bg-dark:focus,a.bg-dark:hover,button.bg-dark:focus,button.bg-dark:hover { .float-md-none { float:none!important } - + } @media (min-width:992px) { .float-lg-left { @@ -6691,7 +6692,7 @@ a.bg-dark:focus,a.bg-dark:hover,button.bg-dark:focus,button.bg-dark:hover { .float-lg-none { float:none!important } - + } @media (min-width:1200px) { .float-xl-left { @@ -6703,7 +6704,7 @@ a.bg-dark:focus,a.bg-dark:hover,button.bg-dark:focus,button.bg-dark:hover { .float-xl-none { float:none!important } - + } .user-select-all { -webkit-user-select:all!important; @@ -6766,7 +6767,7 @@ a.bg-dark:focus,a.bg-dark:hover,button.bg-dark:focus,button.bg-dark:hover { top:0; z-index:1020 } - + } .sr-only { position:absolute; @@ -7388,7 +7389,7 @@ a.bg-dark:focus,a.bg-dark:hover,button.bg-dark:focus,button.bg-dark:hover { .ml-sm-auto,.mx-sm-auto { margin-left:auto!important } - + } @media (min-width:768px) { .m-md-0 { @@ -7661,7 +7662,7 @@ a.bg-dark:focus,a.bg-dark:hover,button.bg-dark:focus,button.bg-dark:hover { .ml-md-auto,.mx-md-auto { margin-left:auto!important } - + } @media (min-width:992px) { .m-lg-0 { @@ -7934,7 +7935,7 @@ a.bg-dark:focus,a.bg-dark:hover,button.bg-dark:focus,button.bg-dark:hover { .ml-lg-auto,.mx-lg-auto { margin-left:auto!important } - + } @media (min-width:1200px) { .m-xl-0 { @@ -8207,7 +8208,7 @@ a.bg-dark:focus,a.bg-dark:hover,button.bg-dark:focus,button.bg-dark:hover { .ml-xl-auto,.mx-xl-auto { margin-left:auto!important } - + } .stretched-link::after { position:absolute; @@ -8256,7 +8257,7 @@ a.bg-dark:focus,a.bg-dark:hover,button.bg-dark:focus,button.bg-dark:hover { .text-sm-center { text-align:center!important } - + } @media (min-width:768px) { .text-md-left { @@ -8268,7 +8269,7 @@ a.bg-dark:focus,a.bg-dark:hover,button.bg-dark:focus,button.bg-dark:hover { .text-md-center { text-align:center!important } - + } @media (min-width:992px) { .text-lg-left { @@ -8280,7 +8281,7 @@ a.bg-dark:focus,a.bg-dark:hover,button.bg-dark:focus,button.bg-dark:hover { .text-lg-center { text-align:center!important } - + } @media (min-width:1200px) { .text-xl-left { @@ -8292,7 +8293,7 @@ a.bg-dark:focus,a.bg-dark:hover,button.bg-dark:focus,button.bg-dark:hover { .text-xl-center { text-align:center!important } - + } .text-lowercase { text-transform:lowercase!important @@ -8472,7 +8473,7 @@ a.text-dark:focus,a.text-dark:hover { color:inherit; border-color:#dee2e6 } - + } .bg-primary { background-image:linear-gradient(#54b4eb,#2fa4e7 60%,#1d9ce5); diff --git a/apps/remix-ide/src/assets/css/themes/bootstrap-cyborg.min.css b/apps/remix-ide/src/assets/css/themes/bootstrap-cyborg.min.css index 1ed6d7b4b6..30026bb48b 100644 --- a/apps/remix-ide/src/assets/css/themes/bootstrap-cyborg.min.css +++ b/apps/remix-ide/src/assets/css/themes/bootstrap-cyborg.min.css @@ -9,8 +9,7 @@ * Copyright 2011-2020 The Bootstrap Authors * Copyright 2011-2020 Twitter, Inc. * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) -*/@import url(https://fonts.googleapis.com/css2?family=Roboto:wght@400; -700&display=swap); +*/@import url(https://fonts.googleapis.com/css2?family=Roboto:wght@400;700&display=swap); :root { --blue:#2a9fd6; --indigo:#6610f2; @@ -36,6 +35,7 @@ --dark:#adafae; --body-bg: #060606; --text-bg-mark: #fcf8e3; + --custom-select: #fff; --breakpoint-xs:0; --breakpoint-sm:576px; --breakpoint-md:768px; diff --git a/apps/remix-ide/src/lib/offsetToLineColumnConverter.js b/apps/remix-ide/src/lib/offsetToLineColumnConverter.js deleted file mode 100644 index b2a2e37e32..0000000000 --- a/apps/remix-ide/src/lib/offsetToLineColumnConverter.js +++ /dev/null @@ -1,75 +0,0 @@ -'use strict' -import { Plugin } from '@remixproject/engine' -import * as packageJson from '../../../../package.json' -import { sourceMappingDecoder } from '@remix-project/remix-debug' - -const profile = { - name: 'offsetToLineColumnConverter', - methods: ['offsetToLineColumn'], - events: [], - version: packageJson.version -} - -export class OffsetToLineColumnConverter extends Plugin { - constructor () { - super(profile) - this.lineBreakPositionsByContent = {} - 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. - * So we use the asts (which references both index and filename) to look up the actual content targeted by the @arg file index. - * @param {{start, length}} rawLocation - offset location - * @param {number} file - The index where to find the source in the sources parameters - * @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 (var filename in asts) { - const source = asts[filename] - if (source.id === file) { - this.lineBreakPositionsByContent[file] = this.sourceMappingDecoder.getLinebreakPositions(sources[filename].content) - break - } - } - } - } - return this.sourceMappingDecoder.convertOffsetToLineColumn(rawLocation, this.lineBreakPositionsByContent[file]) - } - - /** - * 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]) - } - - /** - * Clear the cache - */ - clear () { - this.lineBreakPositionsByContent = {} - } - - /** - * called by plugin API - */ - activate () { - this.on('solidity', 'compilationFinished', () => { - this.clear() - }) - } -} diff --git a/apps/solidity-compiler/src/app/compiler-api.ts b/apps/solidity-compiler/src/app/compiler-api.ts index 74eec1d57f..f7eb97442c 100644 --- a/apps/solidity-compiler/src/app/compiler-api.ts +++ b/apps/solidity-compiler/src/app/compiler-api.ts @@ -222,10 +222,10 @@ export const CompilerApiMixin = (Base) => class extends Base { } this.compiler.event.register('loadingCompiler', this.data.eventHandlers.onLoadingCompiler) - this.data.eventHandlers.onCompilerLoaded = (version) => { + this.data.eventHandlers.onCompilerLoaded = (version, license) => { this.data.loading = false this.statusChanged({ key: 'none' }) - this.emit('compilerLoaded', version) + this.emit('compilerLoaded', version, license) } this.compiler.event.register('compilerLoaded', this.data.eventHandlers.onCompilerLoaded) diff --git a/apps/vyper/src/app/app.tsx b/apps/vyper/src/app/app.tsx index a6ce7c9db9..18b0fd0a80 100644 --- a/apps/vyper/src/app/app.tsx +++ b/apps/vyper/src/app/app.tsx @@ -38,12 +38,15 @@ const App: React.FC = () => { async function start() { try { await remixClient.loaded() - remixClient.onFileChange(name => setContract(name)) - const name = await remixClient.getContractName() - setContract(name) + remixClient.onFileChange(name => setContract(name)) + remixClient.onNoFileSelected(() => setContract('')) } catch (err) { console.log(err) } + try { + const name = await remixClient.getContractName() // throw if no file are selected + setContract(name) + } catch (e) {} } start() }, []) diff --git a/apps/vyper/src/app/components/CompilerButton.tsx b/apps/vyper/src/app/components/CompilerButton.tsx index fb0fb6ff05..c7fe318f0d 100644 --- a/apps/vyper/src/app/components/CompilerButton.tsx +++ b/apps/vyper/src/app/components/CompilerButton.tsx @@ -28,22 +28,60 @@ function CompilerButton({ contract, setOutput, compilerUrl }: Props) { /** Compile a Contract */ async function compileContract() { try { - const _contract = await remixClient.getContract() + await remixClient.discardHighlight() + let _contract: any + try { + _contract = await remixClient.getContract() + } catch (e: any) { + setOutput('', { status: 'failed', message: e.message}) + return + } remixClient.changeStatus({ key: 'loading', type: 'info', title: 'Compiling' }) - const output = await compile(compilerUrl, _contract) + let output + try { + output = await compile(compilerUrl, _contract) + } catch (e: any) { + setOutput(_contract.name, { status: 'failed', message: e.message}) + return + } setOutput(_contract.name, output) // ERROR if (isCompilationError(output)) { const line = output.line - const lineColumnPos = { - start: { line: line - 1 }, - end: { line: line - 1 } + if (line) { + const lineColumnPos = { + start: { line: line - 1, column: 10 }, + end: { line: line - 1, column: 10 } + } + remixClient.highlight(lineColumnPos as any, _contract.name, '#e0b4b4') + } else { + const regex = output.message.match(/line ((\d+):(\d+))+/g) + const errors = output.message.split(/line ((\d+):(\d+))+/g) // extract error message + if (regex) { + let errorIndex = 0 + regex.map((errorLocation) => { + const location = errorLocation.replace('line ', '').split(':') + let message = errors[errorIndex] + errorIndex = errorIndex + 4 + if (message && message.split('\n\n').length > 0) { + try { + message = message.split('\n\n')[1] + } catch (e) {} + } + if (location.length > 0) { + const lineColumnPos = { + start: { line: parseInt(location[0]) - 1, column: 10 }, + end: { line: parseInt(location[0]) - 1, column: 10 } + } + remixClient.highlight(lineColumnPos as any, _contract.name, message) + } + }) + } } - remixClient.highlight(lineColumnPos as any, _contract.name, '#e0b4b4') throw new Error(output.message) } // SUCCESS @@ -61,7 +99,6 @@ function CompilerButton({ contract, setOutput, compilerUrl }: Props) { type: 'error', title: err.message }) - console.error(err) } } diff --git a/apps/vyper/src/app/components/LocalUrl.tsx b/apps/vyper/src/app/components/LocalUrl.tsx index 3659b4094e..5e2dcbacf1 100644 --- a/apps/vyper/src/app/components/LocalUrl.tsx +++ b/apps/vyper/src/app/components/LocalUrl.tsx @@ -26,11 +26,10 @@ function LocalUrlInput({ url, setUrl, environment }: Props) { type="email" placeholder="eg http://localhost:8000/compile" /> - The url to your local compiler ) } -export default LocalUrlInput; \ No newline at end of file +export default LocalUrlInput; diff --git a/apps/vyper/src/app/components/VyperResult.tsx b/apps/vyper/src/app/components/VyperResult.tsx index f550d17370..e58f901fea 100644 --- a/apps/vyper/src/app/components/VyperResult.tsx +++ b/apps/vyper/src/app/components/VyperResult.tsx @@ -7,7 +7,6 @@ import { } from '../utils'; import Tabs from 'react-bootstrap/Tabs' import Tab from 'react-bootstrap/Tab' -import { Ballot } from '../examples/ballot'; import Button from 'react-bootstrap/Button'; import JSONTree from 'react-json-view' import { CopyToClipboard } from '@remix-ui/clipboard' @@ -17,14 +16,20 @@ interface VyperResultProps { output?: VyperCompilationOutput; } +export type ExampleContract = { + name: string, + address: string +} + function VyperResult({ output }: VyperResultProps) { - const [ active, setActive ] = useState('abi'); - + const [ active, setActive ] = useState('abi') + if (!output) return ( +

No contract compiled yet.

-
) @@ -33,7 +38,7 @@ function VyperResult({ output }: VyperResultProps) { return (
-

{output.message}

+
{output.message}
) } @@ -41,7 +46,7 @@ function VyperResult({ output }: VyperResultProps) { setActive(key)}> JSON.stringify(output.abi)}> - + diff --git a/apps/vyper/src/app/examples/ballot.tsx b/apps/vyper/src/app/examples/ballot.tsx deleted file mode 100644 index 4b368c6fda..0000000000 --- a/apps/vyper/src/app/examples/ballot.tsx +++ /dev/null @@ -1,161 +0,0 @@ -export const Ballot = { - name: 'browser/ballot.vy', - content: `# Voting with delegation. - - # Information about voters - struct Voter: - # weight is accumulated by delegation - weight: int128 - # if true, that person already voted (which includes voting by delegating) - voted: bool - # person delegated to - delegate: address - # index of the voted proposal, which is not meaningful unless 'voted' is True. - vote: int128 - - # Users can create proposals - struct Proposal: - # short name (up to 32 bytes) - name: bytes32 - # number of accumulated votes - voteCount: int128 - - voters: public(map(address, Voter)) - proposals: public(map(int128, Proposal)) - voterCount: public(int128) - chairperson: public(address) - int128Proposals: public(int128) - - - @public - @constant - def delegated(addr: address) -> bool: - return self.voters[addr].delegate != ZERO_ADDRESS - - - @public - @constant - def directlyVoted(addr: address) -> bool: - return self.voters[addr].voted and (self.voters[addr].delegate == ZERO_ADDRESS) - - - # Setup global variables - @public - def __init__(_proposalNames: bytes32[2]): - self.chairperson = msg.sender - self.voterCount = 0 - for i in range(2): - self.proposals[i] = Proposal({ - name: _proposalNames[i], - voteCount: 0 - }) - self.int128Proposals += 1 - - # Give a 'voter' the right to vote on this ballot. - # This may only be called by the 'chairperson'. - @public - def giveRightToVote(voter: address): - # Throws if the sender is not the chairperson. - assert msg.sender == self.chairperson - # Throws if the voter has already voted. - assert not self.voters[voter].voted - # Throws if the voter's voting weight isn't 0. - assert self.voters[voter].weight == 0 - self.voters[voter].weight = 1 - self.voterCount += 1 - - # Used by 'delegate' below, and can be called by anyone. - @public - def forwardWeight(delegate_with_weight_to_forward: address): - assert self.delegated(delegate_with_weight_to_forward) - # Throw if there is nothing to do: - assert self.voters[delegate_with_weight_to_forward].weight > 0 - - target: address = self.voters[delegate_with_weight_to_forward].delegate - for i in range(4): - if self.delegated(target): - target = self.voters[target].delegate - # The following effectively detects cycles of length <= 5, - # in which the delegation is given back to the delegator. - # This could be done for any int128ber of loops, - # or even infinitely with a while loop. - # However, cycles aren't actually problematic for correctness; - # they just result in spoiled votes. - # So, in the production version, this should instead be - # the responsibility of the contract's client, and this - # check should be removed. - assert target != delegate_with_weight_to_forward - else: - # Weight will be moved to someone who directly voted or - # hasn't voted. - break - - weight_to_forward: int128 = self.voters[delegate_with_weight_to_forward].weight - self.voters[delegate_with_weight_to_forward].weight = 0 - self.voters[target].weight += weight_to_forward - - if self.directlyVoted(target): - self.proposals[self.voters[target].vote].voteCount += weight_to_forward - self.voters[target].weight = 0 - - # To reiterate: if target is also a delegate, this function will need - # to be called again, similarly to as above. - - # Delegate your vote to the voter 'to'. - @public - def delegate(to: address): - # Throws if the sender has already voted - assert not self.voters[msg.sender].voted - # Throws if the sender tries to delegate their vote to themselves or to - # the default address value of 0x0000000000000000000000000000000000000000 - # (the latter might not be problematic, but I don't want to think about it). - assert to != msg.sender - assert to != ZERO_ADDRESS - - self.voters[msg.sender].voted = True - self.voters[msg.sender].delegate = to - - # This call will throw if and only if this delegation would cause a loop - # of length <= 5 that ends up delegating back to the delegator. - self.forwardWeight(msg.sender) - - # Give your vote (including votes delegated to you) - # to proposal 'proposals[proposal].name'. - @public - def vote(proposal: int128): - # can't vote twice - assert not self.voters[msg.sender].voted - # can only vote on legitimate proposals - assert proposal < self.int128Proposals - - self.voters[msg.sender].vote = proposal - self.voters[msg.sender].voted = True - - # transfer msg.sender's weight to proposal - self.proposals[proposal].voteCount += self.voters[msg.sender].weight - self.voters[msg.sender].weight = 0 - - # Computes the winning proposal taking all - # previous votes into account. - @public - @constant - def winningProposal() -> int128: - winning_vote_count: int128 = 0 - winning_proposal: int128 = 0 - for i in range(2): - if self.proposals[i].voteCount > winning_vote_count: - winning_vote_count = self.proposals[i].voteCount - winning_proposal = i - return winning_proposal - - # Calls winningProposal() function to get the index - # of the winner contained in the proposals array and then - # returns the name of the winner - @public - @constant - def winnerName() -> bytes32: - return self.proposals[self.winningProposal()].name - - ` - } - \ No newline at end of file diff --git a/apps/vyper/src/app/utils/compiler.tsx b/apps/vyper/src/app/utils/compiler.tsx index ebc117dba1..0b65c65a1a 100644 --- a/apps/vyper/src/app/utils/compiler.tsx +++ b/apps/vyper/src/app/utils/compiler.tsx @@ -18,8 +18,8 @@ export interface VyperCompilationResult { export interface VyperCompilationError { status: 'failed' - column: number - line: number + column?: number + line?: number message: string } diff --git a/apps/vyper/src/app/utils/remix-client.tsx b/apps/vyper/src/app/utils/remix-client.tsx index 080ba64a75..913131e594 100644 --- a/apps/vyper/src/app/utils/remix-client.tsx +++ b/apps/vyper/src/app/utils/remix-client.tsx @@ -3,6 +3,7 @@ import { Api, Status } from '@remixproject/plugin-utils'; import { createClient } from '@remixproject/plugin-webview' import { PluginClient } from '@remixproject/plugin'; import { Contract } from './compiler'; +import { ExampleContract } from '../components/VyperResult'; export class RemixClient extends PluginClient { private client = createClient>(this); @@ -14,34 +15,70 @@ export class RemixClient extends PluginClient { /** Emit an event when file changed */ async onFileChange(cb: (contract: string) => any) { this.client.on('fileManager', 'currentFileChanged', async (name: string) => { - if (!name) return cb(name) }) } + /** Emit an event when file changed */ + async onNoFileSelected(cb: () => any) { + this.client.on('fileManager', 'noFileSelected', async () => { + cb() + }) + } + /** Load Ballot contract example into the file manager */ - async loadContract({name, content}: Contract) { + async loadContract({name, address}: ExampleContract) { try { - await this.client.call('fileManager', 'setFile', name, content) - await this.client.call('fileManager', 'switchFile', name) + const content = await this.client.call('contentImport', 'resolve', address) + await this.client.call('fileManager', 'setFile', content.cleanUrl, content.content) + await this.client.call('fileManager', 'switchFile', content.cleanUrl) } catch (err) { console.log(err) } } + async cloneVyperRepo() { + try { + // @ts-ignore + this.call('notification', 'toast', 'cloning Vyper repository...') + await this.call('manager', 'activatePlugin', 'dGitProvider') + // @ts-ignore + await this.call('dGitProvider', 'clone', { url: 'https://github.com/vyperlang/vyper', token: null }, 'vyper-lang') + // @ts-ignore + this.call('notification', 'toast', 'Vyper repository cloned, the workspace Vyper has been created.') + } catch (e) { + // @ts-ignore + this.call('notification', 'toast', e.message) + } + } + /** Update the status of the plugin in remix */ changeStatus(status: Status) { this.client.emit('statusChanged', status); } /** Highlight a part of the editor */ - highlight(lineColumnPos: HighlightPosition, name: string, color: string) { - return this.client.call('editor', 'highlight', lineColumnPos, name, color) + async highlight(lineColumnPos: HighlightPosition, name: string, message: string) { + await this.client.call('editor', 'highlight', lineColumnPos, name) + /* + column: -1 + row: -1 + text: "browser/Untitled1.sol: Warning: SPDX license identifier not provided in source file. Before publishing, consider adding a comment containing "SPDX-License-Identifier: " to each source file. Use "SPDX-License-Identifier: UNLICENSED" for non-open-source code. Please see https://spdx.org for more information.↵" + type: "warning" + */ + const annotation = { + column: 0, + row: lineColumnPos.start.line, + type: 'error', + text: message + } + await this.client.call('editor', 'addAnnotation', annotation, name) } /** Remove current Hightlight */ - discardHighlight() { - return this.client.call('editor', 'discardHighlight') + async discardHighlight() { + await this.client.call('editor', 'discardHighlight') + await this.client.call('editor', 'clearAnnotations') } /** Get the name of the current contract */ diff --git a/libs/remix-lib/src/execution/txListener.ts b/libs/remix-lib/src/execution/txListener.ts index 0d5b26eb84..808d9fe4e0 100644 --- a/libs/remix-lib/src/execution/txListener.ts +++ b/libs/remix-lib/src/execution/txListener.ts @@ -1,5 +1,4 @@ 'use strict' -import { each } from 'async' import { ethers } from 'ethers' import { toBuffer, addHexPrefix } from 'ethereumjs-util' import { EventManager } from '../eventManager' @@ -34,8 +33,7 @@ export class TxListener { _listenOnNetwork:boolean _loopId blocks - lastBlock - + constructor (opt, executionContext) { this.event = new EventManager() // has a default for now for backwards compatability @@ -107,8 +105,7 @@ export class TxListener { addExecutionCosts(txResult, tx, execResult) tx.envMode = this.executionContext.getProvider() tx.status = txResult.receipt.status // 0x0 or 0x1 - this._resolve([tx], () => { - }) + this._resolve([tx]) }) }) } @@ -123,9 +120,7 @@ export class TxListener { if (this._loopId) { clearInterval(this._loopId) } - if (this._listenOnNetwork) { - this._startListenOnNetwork() - } + this._listenOnNetwork ? this.startListening() : this.stopListening() } /** @@ -133,7 +128,6 @@ export class TxListener { */ init () { this.blocks = [] - this.lastBlock = -1 } /** @@ -164,34 +158,54 @@ export class TxListener { this._isListening = false } - _startListenOnNetwork () { - this._loopId = setInterval(() => { + 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 - this.executionContext.web3().eth.getBlockNumber((error, blockNumber) => { - if (this._loopId === null) return - if (error) return console.log(error) - if (currentLoopId === this._loopId && blockNumber > this.lastBlock) { - let current = this.lastBlock + 1 - this.lastBlock = blockNumber - while (blockNumber >= current) { - try { - this._manageBlock(current) - } catch (e) { - console.log(e) - } - current++ + 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++ } - }) - }, 2000) + lastSeenBlock = current + } + processingBlock = false + } + this._loopId = setInterval(processBlocks, 20000) + processBlocks() } - _manageBlock (blockNumber) { - this.executionContext.web3().eth.getBlock(blockNumber, true, (error, result) => { - if (!error) { - this._newBlock(Object.assign({ type: 'web3' }, result)) - } - }) + 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) {} } /** @@ -215,31 +229,37 @@ export class TxListener { return this._resolvedTransactions[txHash] } - _newBlock (block) { + async _newBlock (block) { this.blocks.push(block) - this._resolve(block.transactions, () => { - this.event.trigger('newBlock', [block]) - }) + await this._resolve(block.transactions) + this.event.trigger('newBlock', [block]) } - _resolve (transactions, callback) { - each(transactions, (tx, cb) => { + _resolveAsync (tx) { + return new Promise((resolve, reject) => { this._api.resolveReceipt(tx, (error, receipt) => { - if (error) return cb(error) + if (error) return reject(error) this._resolveTx(tx, receipt, (error, resolvedData) => { - if (error) cb(error) + if (error) return reject(error) if (resolvedData) { this.event.trigger('txResolved', [tx, receipt, resolvedData]) } this.event.trigger('newTransaction', [tx, receipt]) - cb() + resolve({}) }) }) - }, () => { - callback() }) } + 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() diff --git a/libs/remix-lib/src/helpers/hhconsoleSigs.ts b/libs/remix-lib/src/helpers/hhconsoleSigs.ts index 1ae8955736..ee1c9dbcc0 100644 --- a/libs/remix-lib/src/helpers/hhconsoleSigs.ts +++ b/libs/remix-lib/src/helpers/hhconsoleSigs.ts @@ -375,5 +375,7 @@ export const ConsoleLogs = { 3982404743: '(address,address,address,uint)', 4161329696: '(address,address,address,string)', 238520724: '(address,address,address,bool)', - 1717301556: '(address,address,address,address)' + 1717301556: '(address,address,address,address)', + 4133908826: '(uint,uint)', + 3054400204: '(string,uint)' } diff --git a/libs/remix-solidity/src/compiler/compiler-worker.ts b/libs/remix-solidity/src/compiler/compiler-worker.ts index 662a03ec06..313d1b0119 100644 --- a/libs/remix-solidity/src/compiler/compiler-worker.ts +++ b/libs/remix-solidity/src/compiler/compiler-worker.ts @@ -33,7 +33,8 @@ export default function (self) { // eslint-disable-line @typescript-eslint/expli } self.postMessage({ cmd: 'versionLoaded', - data: compiler.version() + data: compiler.version(), + license: compiler.license() }) break } diff --git a/libs/remix-solidity/src/compiler/compiler.ts b/libs/remix-solidity/src/compiler/compiler.ts index f728748a9e..20aa8880a2 100644 --- a/libs/remix-solidity/src/compiler/compiler.ts +++ b/libs/remix-solidity/src/compiler/compiler.ts @@ -25,6 +25,7 @@ export class Compiler { compileJSON: null, worker: null, currentVersion: null, + compilerLicense: null, optimize: false, runs: 200, evmVersion: null, @@ -94,9 +95,10 @@ export class Compiler { * @param version compiler version */ - onCompilerLoaded (version: string): void { + onCompilerLoaded (version: string, license: string): void { this.state.currentVersion = version - this.event.trigger('compilerLoaded', [version]) + this.state.compilerLicense = license + this.event.trigger('compilerLoaded', [version, license]) } /** @@ -131,7 +133,7 @@ export class Compiler { } this.onCompilationFinished(result, missingInputs, source, input, this.state.currentVersion) } - this.onCompilerLoaded(compiler.version()) + this.onCompilerLoaded(compiler.version(), compiler.license()) } } @@ -184,6 +186,7 @@ export class Compiler { 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) => { @@ -203,13 +206,14 @@ export class Compiler { } 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) + this.onCompilerLoaded(version, license) } }) } @@ -273,7 +277,7 @@ export class Compiler { const data: MessageFromWorker = msg.data switch (data.cmd) { case 'versionLoaded': - if (data.data) this.onCompilerLoaded(data.data) + if (data.data && data.license) this.onCompilerLoaded(data.data, data.license) break case 'compiled': { diff --git a/libs/remix-solidity/src/compiler/types.ts b/libs/remix-solidity/src/compiler/types.ts index c4e48dd2ff..9f3cfb4b54 100644 --- a/libs/remix-solidity/src/compiler/types.ts +++ b/libs/remix-solidity/src/compiler/types.ts @@ -158,6 +158,7 @@ export interface CompilerState { compileJSON: ((input: SourceWithTarget) => void) | null, worker: any, currentVersion: string| null| undefined, + compilerLicense: string| null optimize: boolean, runs: number evmVersion: EVMVersion| null, @@ -186,6 +187,7 @@ export interface MessageToWorker { export interface MessageFromWorker { cmd: string, + license?: string, job?: number, missingInputs?: string[], input?: any, diff --git a/libs/remix-tests/src/compiler.ts b/libs/remix-tests/src/compiler.ts index b399f7376e..7fedf9c830 100644 --- a/libs/remix-tests/src/compiler.ts +++ b/libs/remix-tests/src/compiler.ts @@ -134,7 +134,7 @@ export function compileFileOrFiles (filename: string, isDirectory: boolean, opts if (runs) compiler.set('runs', runs) if (currentCompilerUrl) { compiler.loadRemoteVersion(currentCompilerUrl) - compiler.event.register('compilerLoaded', this, function (version) { + compiler.event.register('compilerLoaded', this, function (version, license) { next() }) } else { @@ -198,7 +198,7 @@ export function compileContractSources (sources: SrcIfc, newCompConfig: any, imp compiler.set('runs', runs) compiler.loadVersion(usingWorker, currentCompilerUrl) // @ts-ignore - compiler.event.register('compilerLoaded', this, (version) => { + compiler.event.register('compilerLoaded', this, (version, license) => { next() }) } else { diff --git a/libs/remix-ui/debugger-ui/src/lib/debugger-ui.css b/libs/remix-ui/debugger-ui/src/lib/debugger-ui.css index 7bb10333dc..636cfa4eea 100644 --- a/libs/remix-ui/debugger-ui/src/lib/debugger-ui.css +++ b/libs/remix-ui/debugger-ui/src/lib/debugger-ui.css @@ -16,4 +16,8 @@ } .validationError { overflow-wrap: break-word; +} +.debuggerPanels { + overflow-y: scroll; + height: fit-content; } \ No newline at end of file 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 bd8008d0e3..74660fdabe 100644 --- a/libs/remix-ui/debugger-ui/src/lib/debugger-ui.tsx +++ b/libs/remix-ui/debugger-ui/src/lib/debugger-ui.tsx @@ -1,4 +1,4 @@ -import React, { useState, useEffect } from 'react' // eslint-disable-line +import React, { useState, useEffect, useRef } from 'react' // eslint-disable-line import TxBrowser from './tx-browser/tx-browser' // eslint-disable-line import StepManager from './step-manager/step-manager' // eslint-disable-line import VmDebugger from './vm-debugger/vm-debugger' // eslint-disable-line @@ -36,6 +36,28 @@ export const DebuggerUI = (props: DebuggerUIProps) => { sourceLocationStatus: '' }) + 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() }, []) @@ -260,34 +282,35 @@ export const DebuggerUI = (props: DebuggerUIProps) => { }) setTimeout(async() => { - try { - await debuggerInstance.debug(blockNumber, txNumber, tx, () => { - listenToEvents(debuggerInstance, currentReceipt) + 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 => { return { ...prevState, - blockNumber, - txNumber, - debugging: true, - currentReceipt, - currentBlock, - currentTransaction, - debugger: debuggerInstance, - toastMessage: `debugging ${txNumber}`, - validationError: '' + validationError: error.message || error } }) - }) - } catch (error) { - unLoad() - setState(prevState => { - return { - ...prevState, - validationError: error.message || error - } - }) - } - }, 300) + } + }, 300) + handleResize() } const debug = (txHash, web3?) => { @@ -315,17 +338,18 @@ export const DebuggerUI = (props: DebuggerUIProps) => { 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 } + return (
-
+
-

Debugger Configuration

-
+
{ setState(prevState => { return { ...prevState, opt: { ...prevState.opt, debugWithGeneratedSources: checked } } @@ -333,7 +357,7 @@ export const DebuggerUI = (props: DebuggerUIProps) => { }} type="checkbox" title="Debug with generated sources" />
- { state.isLocalNodeUsed &&
+ { state.isLocalNodeUsed &&
{ setState(prevState => { return { ...prevState, opt: { ...prevState.opt, debugWithLocalNode: checked } } @@ -356,9 +380,11 @@ export const DebuggerUI = (props: DebuggerUIProps) => {
} { state.debugging && } +
+
{ state.debugging && } + { state.debugging && }
- { state.debugging && }
) } 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 3a58c1d2f4..612757fe36 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 @@ -98,7 +98,7 @@ export const VmDebuggerHead = ({ vmDebugger: { registerEvent, triggerEvent } }) }, [registerEvent]) return ( -
+
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 aad442eef4..2f7ba40384 100644 --- a/libs/remix-ui/editor/src/lib/remix-ui-editor.tsx +++ b/libs/remix-ui/editor/src/lib/remix-ui-editor.tsx @@ -9,6 +9,8 @@ import { IMarkdownString } from 'monaco-editor' import './remix-ui-editor.css' import { loadTypes } from './web-types' +import monaco from '../types/monaco' +import { MarkerSeverity } from 'monaco-editor' type cursorPosition = { startLineNumber: number, @@ -60,6 +62,22 @@ export type lineText = { hoverMessage: IMarkdownString | IMarkdownString[] } +type errorMarker = { + message: string + severity: MarkerSeverity + position: { + start: { + line: number + column: number + }, + end: { + line: number + column: number + } + }, + file: string +} + loader.config({ paths: { vs: 'assets/js/monaco-editor/dev/vs' } }) export type DecorationsReturn = { @@ -91,6 +109,8 @@ export interface EditorUIProps { addDecoration: (marker: sourceMarker, filePath: string, typeOfDecoration: string) => DecorationsReturn clearDecorationsByPlugin: (filePath: string, plugin: string, typeOfDecoration: string, registeredDecorations: any, currentDecorations: any) => DecorationsReturn keepDecorationsFor: (filePath: string, plugin: string, typeOfDecoration: string, registeredDecorations: any, currentDecorations: any) => DecorationsReturn + addErrorMarker: (errors: []) => void + clearErrorMarkers: (sources: string[] | {[fileName: string]: any}) => void } } @@ -120,7 +140,7 @@ export const EditorUI = (props: EditorUIProps) => { const currentFileRef = 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) => { @@ -278,7 +298,7 @@ export const EditorUI = (props: EditorUIProps) => { monacoRef.current.editor.setModelLanguage(file.model, 'remix-cairo') } else if (file.language === 'zokrates') { monacoRef.current.editor.setModelLanguage(file.model, 'remix-zokrates') - } + } }, [props.currentFile]) const convertToMonacoDecoration = (decoration: lineText | sourceAnnotation | sourceMarker, typeOfDecoration: string) => { @@ -347,7 +367,7 @@ export const EditorUI = (props: EditorUIProps) => { registeredDecorations: newRegisteredDecorations } } - + props.editorAPI.keepDecorationsFor = (filePath: string, plugin: string, typeOfDecoration: string, registeredDecorations: any, currentDecorations: any) => { const model = editorModelsState[filePath]?.model if (!model) return { @@ -376,11 +396,62 @@ export const EditorUI = (props: EditorUIProps) => { registeredDecorations: [{ value: decoration, type: typeOfDecoration }] } } - + props.editorAPI.addDecoration = (marker: sourceMarker, filePath: string, typeOfDecoration: string) => { return addDecoration(marker, filePath, typeOfDecoration) } + props.editorAPI.addErrorMarker = async (errors: errorMarker[]) => { + + 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: monaco.editor.IMarkerData = { + severity: errorServerityMap[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, 'remix-solidity', allMarkersPerfile[filePath]) + } + } + } + + props.editorAPI.clearErrorMarkers = async (sources: string[] | {[fileName: string]: any}) => { + 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, 'remix-solidity', []) + } + } + } + } + props.editorAPI.findMatches = (uri: string, value: string) => { if (!editorRef.current) return const model = editorModelsState[uri]?.model @@ -435,7 +506,7 @@ export const EditorUI = (props: EditorUIProps) => { } } - function handleEditorDidMount (editor) { + function handleEditorDidMount(editor) { editorRef.current = editor defineAndSetTheme(monacoRef.current) reducerListener(props.plugin, dispatch, monacoRef.current, editorRef.current, props.events) @@ -453,7 +524,7 @@ export const EditorUI = (props: EditorUIProps) => { editor.addCommand(monacoRef.current.KeyMod.CtrlCmd | monacoRef.current.KeyCode.US_MINUS, () => { editor.updateOptions({ fontSize: editor.getOption(43).fontSize - 1 }) }) - + // add context menu items const zoominAction = { id: "zoomIn", @@ -483,25 +554,25 @@ export const EditorUI = (props: EditorUIProps) => { 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) - } catch (e) { - console.log(e) - } + const result = await openEditorBase(input, source) + if (input && input.resource && input.resource.path) { + try { + await props.plugin.call('fileManager', 'open', input.resource.path) + } catch (e) { + console.log(e) } - return result + } + return result } } - function handleEditorWillMount (monaco) { + 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' }) - + // Register a tokens provider for the language monacoRef.current.languages.setMonarchTokensProvider('remix-solidity', solidityTokensProvider) monacoRef.current.languages.setLanguageConfiguration('remix-solidity', solidityLanguageConfig) @@ -523,7 +594,7 @@ export const EditorUI = (props: EditorUIProps) => { language={editorModelsState[props.currentFile] ? editorModelsState[props.currentFile].language : 'text'} onMount={handleEditorDidMount} beforeMount={handleEditorWillMount} - options={{ glyphMargin: true, readOnly: true}} + options={{ glyphMargin: true, readOnly: true }} defaultValue={defaultEditorValue} />
@@ -531,9 +602,9 @@ export const EditorUI = (props: EditorUIProps) => { hide={false} gotoLine={(line, column) => props.plugin.call('editor', 'gotoLine', line, column)} openFile={(file) => props.plugin.call('fileManager', 'switchFile', file)} - getLastCompilationResult={() => { return props.plugin.call('compilerArtefacts', 'getLastCompilationResult') } } - offsetToLineColumn={(position, file, sources, asts) => { return props.plugin.call('offsetToLineColumnConverter', 'offsetToLineColumn', position, file, sources, asts) } } - getCurrentFileName={() => { return props.plugin.call('fileManager', 'file') } } + getLastCompilationResult={() => { return props.plugin.call('compilerArtefacts', 'getLastCompilationResult') }} + offsetToLineColumn={(position, file, sources, asts) => { return props.plugin.call('offsetToLineColumnConverter', 'offsetToLineColumn', position, file, sources, asts) }} + getCurrentFileName={() => { return props.plugin.call('fileManager', 'file') }} onContextListenerChanged={(listener) => { props.plugin.on('contextualListener', 'contextChanged', listener) }} onCurrentFileChanged={(listener) => { props.plugin.on('fileManager', 'currentFileChanged', listener) }} referencesOf={(node: astNode) => { return props.plugin.call('contextualListener', 'referencesOf', node) }} diff --git a/libs/remix-ui/helper/src/index.ts b/libs/remix-ui/helper/src/index.ts index 9f050ea8b8..19e7721ac3 100644 --- a/libs/remix-ui/helper/src/index.ts +++ b/libs/remix-ui/helper/src/index.ts @@ -1,4 +1,5 @@ export * from './lib/remix-ui-helper' +export * from './lib/bleach' export * from './lib/helper-components' export * from './lib/components/PluginViewWrapper' export * from './lib/components/custom-dropdown' \ No newline at end of file diff --git a/apps/remix-ide/src/lib/remixBleach.js b/libs/remix-ui/helper/src/lib/bleach.ts similarity index 87% rename from apps/remix-ide/src/lib/remixBleach.js rename to libs/remix-ui/helper/src/lib/bleach.ts index e159c8a6df..44aa129042 100644 --- a/apps/remix-ide/src/lib/remixBleach.js +++ b/libs/remix-ui/helper/src/lib/bleach.ts @@ -5,7 +5,7 @@ */ import * as he from 'he' -const remixBleach = { +export const bleach = { matcher: /<\/?([a-zA-Z0-9]+)*(.*?)\/?>/igm, @@ -24,7 +24,7 @@ const remixBleach = { let match // extract all tags - while ((match = remixBleach.matcher.exec(html)) != null) { + while ((match = bleach.matcher.exec(html)) != null) { const attrr = match[2].split(' ') const attrs = [] @@ -45,7 +45,7 @@ const remixBleach = { if (attr.name) attrs.push(attr) }) - var tag = { + const tag = { full: match[0], name: match[1], attr: attrs @@ -57,14 +57,13 @@ const remixBleach = { return matches }, - sanitize: function (html, options) { + sanitize: function (html, options = { mode: 'white', list: bleach.whitelist, encode_entities: false}) { html = String(html) || '' - options = options || {} - + const mode = options.mode || 'white' - const list = options.list || remixBleach.whitelist + const list = options.list || bleach.whitelist - var matches = remixBleach.analyze(html) + const matches = bleach.analyze(html) if ((mode === 'white' && list.indexOf('script') === -1) || (mode === 'black' && list.indexOf('script') !== -1)) { @@ -95,5 +94,3 @@ const remixBleach = { return html } } - -module.exports = remixBleach diff --git a/libs/remix-ui/modal-dialog/src/lib/remix-ui-modal-dialog.css b/libs/remix-ui/modal-dialog/src/lib/remix-ui-modal-dialog.css index 6f6cd9b221..62a0dd5987 100644 --- a/libs/remix-ui/modal-dialog/src/lib/remix-ui-modal-dialog.css +++ b/libs/remix-ui/modal-dialog/src/lib/remix-ui-modal-dialog.css @@ -8,6 +8,7 @@ .remixModalBody { overflow-y: auto; max-height: 600px; + white-space: pre-line; } @-webkit-keyframes animatetop { from {top: -300px; opacity: 0} 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 4bffb2565f..b6e8c9948a 100644 --- a/libs/remix-ui/panel/src/lib/plugins/panel-header.tsx +++ b/libs/remix-ui/panel/src/lib/plugins/panel-header.tsx @@ -27,8 +27,8 @@ const RemixUIPanelHeader = (props: RemixPanelProps) => { return (
-
{plugin?.profile.displayName || plugin?.profile.name}
-
+
{plugin?.profile.displayName || plugin?.profile.name}
+
{plugin?.profile?.maintainedBy?.toLowerCase() === "remix" && ()}
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..557605a566 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 @@ -15,7 +15,7 @@ export function RemixPluginPanel (props: RemixPanelProps) { <> {props.header}
-
+
{Object.values(props.plugins).map((pluginRecord) => { return })} diff --git a/libs/remix-ui/run-tab/src/lib/actions/actions.ts b/libs/remix-ui/run-tab/src/lib/actions/actions.ts index a99a8b3800..669851a869 100644 --- a/libs/remix-ui/run-tab/src/lib/actions/actions.ts +++ b/libs/remix-ui/run-tab/src/lib/actions/actions.ts @@ -1,5 +1,5 @@ import { ContractData } from "@remix-project/core-plugin" -import { addNewInstance, addProvider, clearAllInstances, clearRecorderCount, hidePopUp, removeExistingInstance, removeProvider, setBaseFeePerGas, setConfirmSettings, setCurrentContract, setExecutionEnvironment, setExternalEndpoint, setGasLimit, setGasPrice, setGasPriceStatus, setMatchPassphrase, setMaxFee, setMaxPriorityFee, setNetworkName, setPassphrase, setPathToScenario, setSelectedAccount, setSendUnit, setSendValue, setTxFeeContent } from "./payload" +import { addNewInstance, addProvider, clearAllInstances, clearRecorderCount, hidePopUp, removeExistingInstance, removeProvider, setBaseFeePerGas, setConfirmSettings, setCurrentContract, setExecutionEnvironment, setExternalEndpoint, setGasLimit, setGasPrice, setGasPriceStatus, setMatchPassphrase, setMaxFee, setMaxPriorityFee, setNetworkName, setPassphrase, setPathToScenario, setSelectedAccount, setSendUnit, setSendValue } from "./payload" export const setAccount = (dispatch: React.Dispatch, account: string) => { dispatch(setSelectedAccount(account)) @@ -65,10 +65,6 @@ export const updateGasPrice = (dispatch: React.Dispatch, price: string) => dispatch(setGasPrice(price)) } -export const updateTxFeeContent = (dispatch: React.Dispatch, content: string) => { - dispatch(setTxFeeContent(content)) -} - export const addInstance = (dispatch: React.Dispatch, instance: { contractData?: ContractData, address: string, name: string, abi?: any, decodedResponse?: Record }) => { instance.decodedResponse = {} dispatch(addNewInstance(instance)) diff --git a/libs/remix-ui/run-tab/src/lib/actions/index.ts b/libs/remix-ui/run-tab/src/lib/actions/index.ts index 6a20e9470d..f61de6ebd8 100644 --- a/libs/remix-ui/run-tab/src/lib/actions/index.ts +++ b/libs/remix-ui/run-tab/src/lib/actions/index.ts @@ -5,7 +5,7 @@ import { resetAndInit, setupEvents } from './events' import { createNewBlockchainAccount, fillAccountsList, setExecutionContext, signMessageWithAddress } from './account' import { clearInstances, clearPopUp, removeInstance, setAccount, setGasFee, setMatchPassphrasePrompt, setNetworkNameFromProvider, setPassphrasePrompt, setSelectedContract, setSendTransactionValue, setUnit, - updateBaseFeePerGas, updateConfirmSettings, updateGasPrice, updateGasPriceStatus, updateMaxFee, updateMaxPriorityFee, updateScenarioPath, updateTxFeeContent } from './actions' + updateBaseFeePerGas, updateConfirmSettings, updateGasPrice, updateGasPriceStatus, updateMaxFee, updateMaxPriorityFee, updateScenarioPath } from './actions' import { createInstance, getContext, getFuncABIInputs, getSelectedContract, loadAddress, runTransactions, updateInstanceBalance } from './deploy' import { CompilerAbstract as CompilerAbstractType } from '@remix-project/remix-solidity-ts' import { ContractData, FuncABI } from "@remix-project/core-plugin" @@ -50,7 +50,6 @@ export const setGasPrice = (price: string) => updateGasPrice(dispatch, price) export const setGasPriceStatus = (status: boolean) => updateGasPriceStatus(dispatch, status) export const setMaxFee = (fee: string) => updateMaxFee(dispatch, fee) export const setMaxPriorityFee = (fee: string) => updateMaxPriorityFee(dispatch, fee) -export const setTxFeeContent = (content: string) => updateTxFeeContent(dispatch, content) export const removeInstances = () => clearInstances(dispatch) export const removeSingleInstance = (index: number) => removeInstance(dispatch, index) export const getExecutionContext = () => getContext(plugin) diff --git a/libs/remix-ui/run-tab/src/lib/actions/payload.ts b/libs/remix-ui/run-tab/src/lib/actions/payload.ts index bb0fc91a41..fac454ca0b 100644 --- a/libs/remix-ui/run-tab/src/lib/actions/payload.ts +++ b/libs/remix-ui/run-tab/src/lib/actions/payload.ts @@ -1,6 +1,6 @@ import { ContractList } from '../reducers/runTab' import { ContractData } from '@remix-project/core-plugin' -import { ADD_DEPLOY_OPTION, ADD_INSTANCE, ADD_PROVIDER, CLEAR_INSTANCES, CLEAR_RECORDER_COUNT, DISPLAY_NOTIFICATION, DISPLAY_POPUP_MESSAGE, FETCH_ACCOUNTS_LIST_FAILED, FETCH_ACCOUNTS_LIST_REQUEST, FETCH_ACCOUNTS_LIST_SUCCESS, FETCH_CONTRACT_LIST_FAILED, FETCH_CONTRACT_LIST_REQUEST, FETCH_CONTRACT_LIST_SUCCESS, HIDE_NOTIFICATION, HIDE_POPUP_MESSAGE, REMOVE_DEPLOY_OPTION, REMOVE_INSTANCE, REMOVE_PROVIDER, RESET_STATE, SET_BASE_FEE_PER_GAS, SET_CONFIRM_SETTINGS, SET_CURRENT_CONTRACT, SET_CURRENT_FILE, SET_DECODED_RESPONSE, SET_DEPLOY_OPTIONS, SET_EXECUTION_ENVIRONMENT, SET_EXTERNAL_WEB3_ENDPOINT, SET_GAS_LIMIT, SET_GAS_PRICE, SET_GAS_PRICE_STATUS, SET_IPFS_CHECKED_STATE, SET_LOAD_TYPE, SET_MATCH_PASSPHRASE, SET_MAX_FEE, SET_MAX_PRIORITY_FEE, SET_NETWORK_NAME, SET_PASSPHRASE, SET_PATH_TO_SCENARIO, SET_PERSONAL_MODE, SET_PROXY_ENV_ADDRESS, SET_RECORDER_COUNT, SET_SELECTED_ACCOUNT, SET_SEND_UNIT, SET_SEND_VALUE, SET_TX_FEE_CONTENT } from '../constants' +import { ADD_DEPLOY_OPTION, ADD_INSTANCE, ADD_PROVIDER, CLEAR_INSTANCES, CLEAR_RECORDER_COUNT, DISPLAY_NOTIFICATION, DISPLAY_POPUP_MESSAGE, FETCH_ACCOUNTS_LIST_FAILED, FETCH_ACCOUNTS_LIST_REQUEST, FETCH_ACCOUNTS_LIST_SUCCESS, FETCH_CONTRACT_LIST_FAILED, FETCH_CONTRACT_LIST_REQUEST, FETCH_CONTRACT_LIST_SUCCESS, HIDE_NOTIFICATION, HIDE_POPUP_MESSAGE, REMOVE_DEPLOY_OPTION, REMOVE_INSTANCE, REMOVE_PROVIDER, RESET_STATE, SET_BASE_FEE_PER_GAS, SET_CONFIRM_SETTINGS, SET_CURRENT_CONTRACT, SET_CURRENT_FILE, SET_DECODED_RESPONSE, SET_DEPLOY_OPTIONS, SET_EXECUTION_ENVIRONMENT, SET_EXTERNAL_WEB3_ENDPOINT, SET_GAS_LIMIT, SET_GAS_PRICE, SET_GAS_PRICE_STATUS, SET_IPFS_CHECKED_STATE, SET_LOAD_TYPE, SET_MATCH_PASSPHRASE, SET_MAX_FEE, SET_MAX_PRIORITY_FEE, SET_NETWORK_NAME, SET_PASSPHRASE, SET_PATH_TO_SCENARIO, SET_PERSONAL_MODE, SET_PROXY_ENV_ADDRESS, SET_RECORDER_COUNT, SET_SELECTED_ACCOUNT, SET_SEND_UNIT, SET_SEND_VALUE } from '../constants' import { DeployMode, DeployOptions } from '../types' export const fetchAccountsListRequest = () => { @@ -216,13 +216,6 @@ export const setGasPrice = (price: string) => { } } -export const setTxFeeContent = (content: string) => { - return { - type: SET_TX_FEE_CONTENT, - payload: content - } -} - export const addNewInstance = (instance: { contractData?: ContractData, address: string, name: string, abi?: any }) => { return { type: ADD_INSTANCE, diff --git a/libs/remix-ui/run-tab/src/lib/components/contractDropdownUI.tsx b/libs/remix-ui/run-tab/src/lib/components/contractDropdownUI.tsx index a224c94242..c3b08c0858 100644 --- a/libs/remix-ui/run-tab/src/lib/components/contractDropdownUI.tsx +++ b/libs/remix-ui/run-tab/src/lib/components/contractDropdownUI.tsx @@ -28,6 +28,7 @@ export function ContractDropdownUI (props: ContractDropdownProps) { const [constructorInterface, setConstructorInterface] = useState(null) const [constructorInputs, setConstructorInputs] = useState(null) const contractsRef = useRef(null) + const atAddressValue = useRef(null) const { contractList, loadType, currentFile, currentContract, compilationCount, deployOptions, proxyKey } = props.contracts useEffect(() => { @@ -52,7 +53,7 @@ export function ContractDropdownUI (props: ContractDropdownProps) { }, [loadedAddress]) useEffect(() => { - if (/.(.abi)$/.exec(currentFile)) { + if (/.(.abi)$/.exec(currentFile) && "" !== atAddressValue.current.value) { setAbiLabel({ display: 'block', content: currentFile @@ -175,7 +176,6 @@ export function ContractDropdownUI (props: ContractDropdownProps) { const atAddressChanged = (event) => { const value = event.target.value - if (!value) { enableAtAddress(false) } else { @@ -282,6 +282,7 @@ export function ContractDropdownUI (props: ContractDropdownProps) {
('') + const [transactionFee, setTransactionFee] = useState('') useEffect(() => { props.init((txFeeText, gasPriceValue, gasPriceStatus) => { - if (txFeeText) props.setTxFeeContent(txFeeText) + if (txFeeText) setTransactionFee(txFeeText) if (gasPriceValue) onGasPriceChange(gasPriceValue) if (props.network && props.network.lastBlock && props.network.lastBlock.baseFeePerGas) { - const baseFee = Web3.utils.fromWei(Web3.utils.toBN(parseInt(props.network.lastBlock.baseFeePerGas, 16)), 'Gwei') + const baseFee = Web3.utils.fromWei(Web3.utils.toBN(props.network.lastBlock.baseFeePerGas), 'Gwei') setBaseFee(baseFee) onMaxFeeChange(baseFee) @@ -24,8 +25,8 @@ export function MainnetPrompt (props: MainnetProps) { const onMaxFeeChange = (value: string) => { const maxFee = value // @ts-ignore - if (parseInt(props.network.lastBlock.baseFeePerGas, 16) > Web3.utils.toWei(maxFee, 'Gwei')) { - props.setTxFeeContent('Transaction is invalid. Max fee should not be less than Base fee') + if (Web3.utils.toBN(props.network.lastBlock.baseFeePerGas).gt(Web3.utils.toBN(Web3.utils.toWei(maxFee, 'Gwei')))) { + setTransactionFee('Transaction is invalid. Max fee should not be less than Base fee') props.updateGasPriceStatus(false) props.updateConfirmSettings(true) return @@ -35,7 +36,7 @@ export function MainnetPrompt (props: MainnetProps) { } props.setNewGasPrice(maxFee, (txFeeText, priceStatus) => { - props.setTxFeeContent(txFeeText) + setTransactionFee(txFeeText) if (priceStatus) { props.updateConfirmSettings(false) } else { @@ -51,7 +52,7 @@ export function MainnetPrompt (props: MainnetProps) { const gasPrice = value props.setNewGasPrice(gasPrice, (txFeeText, priceStatus) => { - props.setTxFeeContent(txFeeText) + setTransactionFee(txFeeText) props.updateGasPriceStatus(priceStatus) props.updateGasPrice(gasPrice) }) @@ -105,7 +106,7 @@ export function MainnetPrompt (props: MainnetProps) {
- Max fee (Not less than base fee {Web3.utils.fromWei(Web3.utils.toBN(parseInt(props.network.lastBlock.baseFeePerGas, 16)), 'Gwei')} Gwei): + Max fee (Not less than base fee {Web3.utils.fromWei(Web3.utils.toBN(props.network.lastBlock.baseFeePerGas), 'Gwei')} Gwei): onMaxFeeChange(e.target.value)} defaultValue={baseFee} /> Gwei @@ -120,7 +121,7 @@ export function MainnetPrompt (props: MainnetProps) { }
Max transaction fee: - { props.txFeeContent } + { transactionFee }
diff --git a/libs/remix-ui/run-tab/src/lib/constants/index.ts b/libs/remix-ui/run-tab/src/lib/constants/index.ts index 91dd777b3b..646cc4a14f 100644 --- a/libs/remix-ui/run-tab/src/lib/constants/index.ts +++ b/libs/remix-ui/run-tab/src/lib/constants/index.ts @@ -32,7 +32,6 @@ export const SET_MAX_FEE = 'SET_MAX_FEE' export const SET_MAX_PRIORITY_FEE = 'SET_MAX_PRIORITY_FEE' export const SET_BASE_FEE_PER_GAS = 'SET_BASE_FEE_PER_GAS' export const SET_GAS_PRICE = 'SET_GAS_PRICE' -export const SET_TX_FEE_CONTENT = 'SET_TX_FEE_CONTENT' export const ADD_INSTANCE = 'ADD_INSTANCE' export const REMOVE_INSTANCE = 'REMOVE_INSTANCE' export const CLEAR_INSTANCES = 'CLEAR_INSTANCES' diff --git a/libs/remix-ui/run-tab/src/lib/reducers/runTab.ts b/libs/remix-ui/run-tab/src/lib/reducers/runTab.ts index 5bae13a09e..361a4bf100 100644 --- a/libs/remix-ui/run-tab/src/lib/reducers/runTab.ts +++ b/libs/remix-ui/run-tab/src/lib/reducers/runTab.ts @@ -1,7 +1,7 @@ import { CompilerAbstract } from '@remix-project/remix-solidity-ts' import { ContractData } from '@remix-project/core-plugin' import { DeployOptions } from '../types' -import { ADD_INSTANCE, ADD_PROVIDER, CLEAR_INSTANCES, CLEAR_RECORDER_COUNT, DISPLAY_NOTIFICATION, DISPLAY_POPUP_MESSAGE, FETCH_ACCOUNTS_LIST_FAILED, FETCH_ACCOUNTS_LIST_REQUEST, FETCH_ACCOUNTS_LIST_SUCCESS, FETCH_CONTRACT_LIST_FAILED, FETCH_CONTRACT_LIST_REQUEST, FETCH_CONTRACT_LIST_SUCCESS, FETCH_PROVIDER_LIST_FAILED, FETCH_PROVIDER_LIST_REQUEST, FETCH_PROVIDER_LIST_SUCCESS, HIDE_NOTIFICATION, HIDE_POPUP_MESSAGE, REMOVE_INSTANCE, REMOVE_PROVIDER, RESET_STATE, SET_BASE_FEE_PER_GAS, SET_CONFIRM_SETTINGS, SET_CURRENT_CONTRACT, SET_CURRENT_FILE, SET_DECODED_RESPONSE, SET_DEPLOY_OPTIONS, SET_EXECUTION_ENVIRONMENT, SET_EXTERNAL_WEB3_ENDPOINT, SET_GAS_LIMIT, SET_GAS_PRICE, SET_GAS_PRICE_STATUS, SET_IPFS_CHECKED_STATE, SET_LOAD_TYPE, SET_MATCH_PASSPHRASE, SET_MAX_FEE, SET_MAX_PRIORITY_FEE, SET_NETWORK_NAME, SET_PASSPHRASE, SET_PATH_TO_SCENARIO, SET_PERSONAL_MODE, SET_RECORDER_COUNT, SET_SELECTED_ACCOUNT, SET_SEND_UNIT, SET_SEND_VALUE, SET_TX_FEE_CONTENT, SET_PROXY_ENV_ADDRESS, ADD_DEPLOY_OPTION, REMOVE_DEPLOY_OPTION } from '../constants' +import { ADD_INSTANCE, ADD_PROVIDER, CLEAR_INSTANCES, CLEAR_RECORDER_COUNT, DISPLAY_NOTIFICATION, DISPLAY_POPUP_MESSAGE, FETCH_ACCOUNTS_LIST_FAILED, FETCH_ACCOUNTS_LIST_REQUEST, FETCH_ACCOUNTS_LIST_SUCCESS, FETCH_CONTRACT_LIST_FAILED, FETCH_CONTRACT_LIST_REQUEST, FETCH_CONTRACT_LIST_SUCCESS, FETCH_PROVIDER_LIST_FAILED, FETCH_PROVIDER_LIST_REQUEST, FETCH_PROVIDER_LIST_SUCCESS, HIDE_NOTIFICATION, HIDE_POPUP_MESSAGE, REMOVE_INSTANCE, REMOVE_PROVIDER, RESET_STATE, SET_BASE_FEE_PER_GAS, SET_CONFIRM_SETTINGS, SET_CURRENT_CONTRACT, SET_CURRENT_FILE, SET_DECODED_RESPONSE, SET_DEPLOY_OPTIONS, SET_EXECUTION_ENVIRONMENT, SET_EXTERNAL_WEB3_ENDPOINT, SET_GAS_LIMIT, SET_GAS_PRICE, SET_GAS_PRICE_STATUS, SET_IPFS_CHECKED_STATE, SET_LOAD_TYPE, SET_MATCH_PASSPHRASE, SET_MAX_FEE, SET_MAX_PRIORITY_FEE, SET_NETWORK_NAME, SET_PASSPHRASE, SET_PATH_TO_SCENARIO, SET_PERSONAL_MODE, SET_RECORDER_COUNT, SET_SELECTED_ACCOUNT, SET_SEND_UNIT, SET_SEND_VALUE, SET_PROXY_ENV_ADDRESS, ADD_DEPLOY_OPTION, REMOVE_DEPLOY_OPTION } from '../constants' declare const window: any interface Action { @@ -82,7 +82,6 @@ export interface RunTabState { maxFee: string, maxPriorityFee: string, baseFeePerGas: string, - txFeeContent: string, gasPrice: string, instances: { instanceList: { @@ -171,7 +170,6 @@ export const runTabInitialState: RunTabState = { maxFee: '', maxPriorityFee: '1', baseFeePerGas: '', - txFeeContent: '', gasPrice: '', instances: { instanceList: [], @@ -582,15 +580,6 @@ export const runTabReducer = (state: RunTabState = runTabInitialState, action: A } } - case SET_TX_FEE_CONTENT: { - const payload: string = action.payload - - return { - ...state, - txFeeContent: payload - } - } - case ADD_INSTANCE: { const payload: { contractData: ContractData, address: string, name: string, abi?: any, decodedResponse?: Record } = action.payload diff --git a/libs/remix-ui/run-tab/src/lib/run-tab.tsx b/libs/remix-ui/run-tab/src/lib/run-tab.tsx index 128d315201..1ad855434c 100644 --- a/libs/remix-ui/run-tab/src/lib/run-tab.tsx +++ b/libs/remix-ui/run-tab/src/lib/run-tab.tsx @@ -21,7 +21,7 @@ import { setBaseFeePerGas, setConfirmSettings, setGasPrice, setGasPriceStatus, setMaxFee, setMaxPriorityFee, - setTxFeeContent, removeInstances, + removeInstances, removeSingleInstance, getExecutionContext, executeTransactions, loadFromAddress, storeNewScenario, runScenario, @@ -189,8 +189,6 @@ export function RunTabUI (props: RunTabProps) { updateGasPriceStatus={setGasPriceStatus} updateMaxFee={setMaxFee} updateMaxPriorityFee={setMaxPriorityFee} - setTxFeeContent={setTxFeeContent} - txFeeContent={runTab.txFeeContent} maxFee={runTab.maxFee} maxPriorityFee={runTab.maxPriorityFee} /> diff --git a/libs/remix-ui/run-tab/src/lib/types/index.ts b/libs/remix-ui/run-tab/src/lib/types/index.ts index 8ef73156df..4d1e37524a 100644 --- a/libs/remix-ui/run-tab/src/lib/types/index.ts +++ b/libs/remix-ui/run-tab/src/lib/types/index.ts @@ -270,10 +270,8 @@ export interface MainnetProps { updateMaxFee: (fee: string) => void, updateBaseFeePerGas: (fee: string) => void, init: (cb: (txFeeText: string, gasPriceValue: string, gasPriceStatus: boolean) => void) => void, - setTxFeeContent: (content: string) => void, updateGasPrice: (price: string) => void, updateMaxPriorityFee: (fee: string) => void - txFeeContent: string, maxFee: string, maxPriorityFee: string } diff --git a/libs/remix-ui/settings/src/lib/github-settings.tsx b/libs/remix-ui/settings/src/lib/github-settings.tsx index 243598d6ec..6136dd8f95 100644 --- a/libs/remix-ui/settings/src/lib/github-settings.tsx +++ b/libs/remix-ui/settings/src/lib/github-settings.tsx @@ -9,9 +9,9 @@ export function GithubSettings (props: GithubSettingsProps) { useEffect(() => { if (props.config) { - const githubToken = props.config.get('settings/gist-access-token') - const githubUserName = props.config.get('settings/github-user-name') - const githubEmail = props.config.get('settings/github-email') + const githubToken = props.config.get('settings/gist-access-token') || '' + const githubUserName = props.config.get('settings/github-user-name') || '' + const githubEmail = props.config.get('settings/github-email') || '' setGithubToken(githubToken) setGithubUsername(githubUserName) diff --git a/libs/remix-ui/settings/src/lib/remix-ui-settings.tsx b/libs/remix-ui/settings/src/lib/remix-ui-settings.tsx index 912da468f3..1d767c7643 100644 --- a/libs/remix-ui/settings/src/lib/remix-ui-settings.tsx +++ b/libs/remix-ui/settings/src/lib/remix-ui-settings.tsx @@ -214,7 +214,7 @@ export const RemixUiSettings = (props: RemixUiSettingsProps) => {

{ labels[type].link }

- handleSaveTokenState(e, type)} value={ tokenValue[type] } /> + handleSaveTokenState(e, type)} value={ tokenValue[type] || '' } />
saveToken(type)} value="Save" type="button" disabled={tokenValue === ''}> diff --git a/libs/remix-ui/solidity-compiler/src/lib/actions/compiler.ts b/libs/remix-ui/solidity-compiler/src/lib/actions/compiler.ts index c67aee6ddb..b1c0b686de 100644 --- a/libs/remix-ui/solidity-compiler/src/lib/actions/compiler.ts +++ b/libs/remix-ui/solidity-compiler/src/lib/actions/compiler.ts @@ -46,8 +46,8 @@ export const listenToEvents = (compileTabLogic: CompileTabLogic, api) => (dispat dispatch(setCompilerMode('loadingCompiler')) }) - compileTabLogic.compiler.event.register('compilerLoaded', () => { - dispatch(setCompilerMode('compilerLoaded')) + compileTabLogic.compiler.event.register('compilerLoaded', (version, license) => { + dispatch(setCompilerMode('compilerLoaded', version, license)) }) compileTabLogic.compiler.event.register('compilationFinished', (success, data, source, input, version) => { diff --git a/libs/remix-ui/solidity-compiler/src/lib/compiler-container.tsx b/libs/remix-ui/solidity-compiler/src/lib/compiler-container.tsx index 069377d045..857a86e73c 100644 --- a/libs/remix-ui/solidity-compiler/src/lib/compiler-container.tsx +++ b/libs/remix-ui/solidity-compiler/src/lib/compiler-container.tsx @@ -48,6 +48,7 @@ export const CompilerContainer = (props: CompilerContainerProps) => { timeout: 300, allversions: [], customVersions: [], + compilerLicense: null, selectedVersion: null, defaultVersion: 'soljson-v0.8.7+commit.e28d00a7.js', // this default version is defined: in makeMockCompiler (for browser test) runs: '', @@ -185,7 +186,7 @@ export const CompilerContainer = (props: CompilerContainerProps) => { loadingCompiler() break case 'compilerLoaded': - compilerLoaded() + compilerLoaded(compilerContainer.compiler.args[1]) break case 'compilationFinished': compilationFinished() @@ -432,14 +433,20 @@ export const CompilerContainer = (props: CompilerContainerProps) => { if (!compileIcon.current) return compileIcon.current.setAttribute('title', 'compiler is loading, please wait a few moments.') compileIcon.current.classList.add('remixui_spinningIcon') + setState(prevState => { + return { ...prevState, compilerLicense: 'Compiler is loading. License will be displayed once compiler is loaded'} + }) _updateLanguageSelector() setDisableCompileButton(true) } - const compilerLoaded = () => { + const compilerLoaded = (license) => { if (!compileIcon.current) return compileIcon.current.setAttribute('title', '') compileIcon.current.classList.remove('remixui_spinningIcon') + setState(prevState => { + return { ...prevState, compilerLicense: license ? license : 'Could not retreive license for selected compiler version' } + }) if (state.autoCompile) compile() const isDisabled = !compiledFileName || (compiledFileName && !isSolFileSelected(compiledFileName)) @@ -554,6 +561,10 @@ export const CompilerContainer = (props: CompilerContainerProps) => { modal('Add a custom compiler', promptMessage('URL'), 'OK', addCustomCompiler, 'Cancel', () => {}) } + const showCompilerLicense = () => { + modal('Compiler License', state.compilerLicense ? state.compilerLicense : 'License not available', 'OK', () => {}) + } + const promptMessage = (message) => { return ( <> @@ -701,10 +712,9 @@ export const CompilerContainer = (props: CompilerContainerProps) => {
- + + promptCompiler()} title="Add a custom compiler with URL"> + showCompilerLicense()} title="See compiler license">