diff --git a/.circleci/config.yml b/.circleci/config.yml index acc9a0ffb8..e7580743ae 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -72,6 +72,8 @@ jobs: parallelism: 12 steps: - checkout + - run: git submodule sync --recursive + - run: git submodule update --recursive --init - run: npm install - run: npx nx build remix-ide --with-deps - run: @@ -105,6 +107,8 @@ jobs: parallelism: 12 steps: - checkout + - run: git submodule sync --recursive + - run: git submodule update --recursive --init - run: npm install - run: npm run downloadsolc_assets - run: npx nx build remix-ide --with-deps @@ -139,6 +143,8 @@ jobs: parallelism: 12 steps: - checkout + - run: git submodule sync --recursive + - run: git submodule update --recursive --init - run: npm install - run: npx nx build remix-ide --with-deps - run: @@ -172,6 +178,8 @@ jobs: parallelism: 12 steps: - checkout + - run: git submodule sync --recursive + - run: git submodule update --recursive --init - run: npm install - run: npm run downloadsolc_assets - run: npx nx build remix-ide --with-deps @@ -206,6 +214,8 @@ jobs: steps: - checkout + - run: git submodule sync --recursive + - run: git submodule update --recursive --init - run: npm install - run: npx nx build remix-ide --with-deps - run: @@ -239,6 +249,8 @@ jobs: steps: - checkout + - run: git submodule sync --recursive + - run: git submodule update --recursive --init - run: npm install - run: npx nx build remix-ide --with-deps - run: npx nx build remix-ide-e2e-src-local-plugin @@ -254,6 +266,7 @@ jobs: path: ./reports/tests - store_artifacts: path: ./reports/screenshots + deploy-remix-live: docker: # specify the version you desire here @@ -272,6 +285,8 @@ jobs: steps: - checkout + - run: git submodule sync --recursive + - run: git submodule update --recursive --init - run: npm install - run: npm run downloadsolc_assets - run: npm run build:production @@ -300,6 +315,8 @@ jobs: steps: - checkout + - run: git submodule sync --recursive + - run: git submodule update --recursive --init - setup_remote_docker - run: npm install - run: npm run downloadsolc_assets @@ -326,6 +343,8 @@ jobs: steps: - checkout + - run: git submodule sync --recursive + - run: git submodule update --recursive --init - run: npm install - run: npm run downloadsolc_assets - run: npm run build:production @@ -354,6 +373,8 @@ jobs: steps: - checkout + - run: git submodule sync --recursive + - run: git submodule update --recursive --init - run: npm install - run: npm run build:libs - run: npm run downloadsolc_assets diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000000..f3b7f8e547 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "libs/remix-ui/tabs/react-tabs"] + path = libs/remix-ui/tabs/react-tabs + url = https://github.com/reactjs/react-tabs diff --git a/apps/remix-ide-e2e/src/commands/journalChildIncludes.ts b/apps/remix-ide-e2e/src/commands/journalChildIncludes.ts index af9a061136..689be3bf34 100644 --- a/apps/remix-ide-e2e/src/commands/journalChildIncludes.ts +++ b/apps/remix-ide-e2e/src/commands/journalChildIncludes.ts @@ -5,10 +5,10 @@ import EventEmitter from 'events' Checks if any child elements of journal (console) contains a matching value. */ class JournalChildIncludes extends EventEmitter { - command (this: NightwatchBrowser, val: string): NightwatchBrowser { + command (this: NightwatchBrowser, val: string, opts = { shouldHaveOnlyOneOccurence: false }): NightwatchBrowser { let isTextFound = false const browser = this.api - + let occurence = 0 this.api.elements('css selector', '*[data-id="terminalJournal"]', (res) => { Array.isArray(res.value) && res.value.forEach(function (jsonWebElement) { const jsonWebElementId = jsonWebElement.ELEMENT || jsonWebElement[Object.keys(jsonWebElement)[0]] @@ -16,12 +16,16 @@ class JournalChildIncludes extends EventEmitter { browser.elementIdText(jsonWebElementId, (jsonElement) => { const text = jsonElement.value - if (typeof text === 'string' && text.indexOf(val) !== -1) isTextFound = true + if (typeof text === 'string' && text.indexOf(val) !== -1) { + isTextFound = true + occurence++ + } }) }) }) browser.perform(() => { browser.assert.ok(isTextFound, isTextFound ? `<*[data-id="terminalJournal"]> contains ${val}.` : `${val} not found in <*[data-id="terminalJournal"]> div:last-child>`) + if (opts.shouldHaveOnlyOneOccurence) browser.assert.ok(occurence === 1, `${occurence} occurence found of "${val}"`) this.emit('complete') }) return this diff --git a/apps/remix-ide-e2e/src/tests/remixd.test.ts b/apps/remix-ide-e2e/src/tests/remixd.test.ts index 293bdb16c0..970a4f556a 100644 --- a/apps/remix-ide-e2e/src/tests/remixd.test.ts +++ b/apps/remix-ide-e2e/src/tests/remixd.test.ts @@ -152,7 +152,6 @@ function runTests (browser: NightwatchBrowser) { }) .clickLaunchIcon('filePanel') .waitForElementVisible('[data-path="folder1"]') - .click('[data-path="folder1"]') .waitForElementVisible('[data-path="folder1/contract1.sol"]') .waitForElementVisible('[data-path="folder1/renamed_contract_' + browserName + '.sol"]') // check if renamed file is preset .waitForElementNotPresent('[data-path="folder1/contract_' + browserName + '.sol"]') // check if renamed (old) file is not present diff --git a/apps/remix-ide-e2e/src/tests/solidityUnittests.spec.ts b/apps/remix-ide-e2e/src/tests/solidityUnittests.spec.ts index 36eb781ac7..e5a4136eb5 100644 --- a/apps/remix-ide-e2e/src/tests/solidityUnittests.spec.ts +++ b/apps/remix-ide-e2e/src/tests/solidityUnittests.spec.ts @@ -13,7 +13,7 @@ module.exports = { return sources }, - 'Should launch solidity unit test plugin': function (browser: NightwatchBrowser) { + 'Should launch solidity unit test plugin and create test files in FE': function (browser: NightwatchBrowser) { browser.waitForElementPresent('*[data-id="verticalIconsKindfilePanel"]') .clickLaunchIcon('filePanel') .addFile('simple_storage.sol', sources[0]['simple_storage.sol']) @@ -23,6 +23,15 @@ module.exports = { .click('*[data-id="verticalIconsKindsolidityUnitTesting"]') .waitForElementPresent('*[data-id="sidePanelSwapitTitle"]') .assert.containsText('*[data-id="sidePanelSwapitTitle"]', 'SOLIDITY UNIT TESTING') + .clickLaunchIcon('filePanel') + .waitForElementVisible('li[data-id="treeViewLitreeViewItem.deps/remix-tests/remix_tests.sol"]') + .waitForElementVisible('li[data-id="treeViewLitreeViewItem.deps/remix-tests/remix_accounts.sol"]') + .openFile('.deps/remix-tests/remix_tests.sol') + // remix_test.sol should be opened in editor + .getEditorValue((content) => browser.assert.ok(content.indexOf('library Assert {') !== -1)) + .openFile('.deps/remix-tests/remix_accounts.sol') + // remix_accounts.sol should be opened in editor + .getEditorValue((content) => browser.assert.ok(content.indexOf('library TestsAccounts {') !== -1)) }, 'Should generate test file': function (browser: NightwatchBrowser) { @@ -150,7 +159,7 @@ module.exports = { .click('*[data-id="testTabGenerateTestFolder"]') }, - 'Changing current path when workspace changed': function (browser: NightwatchBrowser) { + 'Changing current path when workspace changed and checking test files creation': function (browser: NightwatchBrowser) { browser .waitForElementPresent('*[data-id="verticalIconsKindfilePanel"]') .clickLaunchIcon('settings') @@ -168,6 +177,14 @@ module.exports = { .waitForElementVisible('*[data-id="fileSystem-modal-footer-ok-react"]') .execute(function () { (document.querySelector('[data-id="fileSystem-modal-footer-ok-react"]') as HTMLElement).click() }) .waitForElementPresent('*[data-id="workspacesSelect"] option[value="workspace_new"]') + .waitForElementVisible('li[data-id="treeViewLitreeViewItem.deps/remix-tests/remix_tests.sol"]') + .waitForElementVisible('li[data-id="treeViewLitreeViewItem.deps/remix-tests/remix_accounts.sol"]') + .openFile('.deps/remix-tests/remix_tests.sol') + // remix_test.sol should be opened in editor + .getEditorValue((content) => browser.assert.ok(content.indexOf('library Assert {') !== -1)) + .openFile('.deps/remix-tests/remix_accounts.sol') + // remix_accounts.sol should be opened in editor + .getEditorValue((content) => browser.assert.ok(content.indexOf('library TestsAccounts {') !== -1)) // end of creating .clickLaunchIcon('solidityUnitTesting') .pause(2000) @@ -276,6 +293,8 @@ module.exports = { .setValue('*[data-id="slider"]', new Array(1).fill(browser.Keys.RIGHT_ARROW)) .waitForElementContainsText('*[data-id="functionPanel"]', 'equal(a, b, message)', 60000) .waitForElementContainsText('*[data-id="functionPanel"]', 'checkWinningProposalPassed()', 60000) + // remix_test.sol should be opened in editor + .getEditorValue((content) => browser.assert.ok(content.indexOf('library Assert {') !== -1)) .pause(1000) .clickLaunchIcon('solidityUnitTesting') .scrollAndClick('#Check_winning_proposal_again') diff --git a/apps/remix-ide-e2e/src/tests/terminal.test.ts b/apps/remix-ide-e2e/src/tests/terminal.test.ts index 7e176b362a..54bfae0cf7 100644 --- a/apps/remix-ide-e2e/src/tests/terminal.test.ts +++ b/apps/remix-ide-e2e/src/tests/terminal.test.ts @@ -128,6 +128,31 @@ module.exports = { .waitForElementContainsText('*[data-id="terminalJournal"]', '0x5B38Da6a701c568545dCfcB03FcB875f56beddC4', 60000) // check that the script is logging the event .waitForElementContainsText('*[data-id="terminalJournal"]', 'newOwner', 60000) .waitForElementContainsText('*[data-id="terminalJournal"]', '0xd9145CCE52D386f254917e481eB44e9943F39138', 60000) + }, + + 'Should print hardhat logs': function (browser: NightwatchBrowser) { + browser + .click('*[data-id="terminalClearConsole"]') // clear the terminal + .addFile('printHardhatlog.sol', { content: hardhatLog }) + .clickLaunchIcon('solidity') + .waitForElementVisible('[for="autoCompile"]') + .click('[for="autoCompile"]') + .testContracts('printHardhatlog.sol', { content: hardhatLog }, ['OwnerTest']) + .clickLaunchIcon('udapp') + .click('*[data-id="deployAndRunClearInstances"]') + .selectContract('OwnerTest') + .createContract('') + .pause(1000) + .journalChildIncludes('constructor', { shouldHaveOnlyOneOccurence: true }) + .pause(5000) + .click('*[data-id="terminalClearConsole"]') // clear the terminal + .clickInstance(0) + .clickFunction('changeOwner - transact (not payable)', { types: 'address newOwner', values: '0xd9145CCE52D386f254917e481eB44e9943F39138' }) + .pause(1000) + .journalChildIncludes('inside changeOwner', { shouldHaveOnlyOneOccurence: true }) + .clickFunction('getOwner - call') + .pause(1000) + .journalChildIncludes('inside getOwner', { shouldHaveOnlyOneOccurence: true }) .end() } } @@ -238,3 +263,61 @@ const deployWithEthersJs = ` console.log(e.message) } })()` + +const hardhatLog = ` +// SPDX-License-Identifier: GPL-3.0 + +pragma solidity >=0.7.0 <0.9.0; + +import "hardhat/console.sol"; + +/** + * @title Owner + * @dev Set & change owner + */ +contract OwnerTest { + + address private owner; + + // event for EVM logging + event OwnerSet(address indexed oldOwner, address indexed newOwner); + + // modifier to check if caller is owner + modifier isOwner() { + // If the first argument of 'require' evaluates to 'false', execution terminates and all + // changes to the state and to Ether balances are reverted. + // This used to consume all gas in old EVM versions, but not anymore. + // It is often a good idea to use 'require' to check if functions are called correctly. + // As a second argument, you can also provide an explanation about what went wrong. + require(msg.sender == owner, "Caller is not owner"); + _; + } + + /** + * @dev Set contract deployer as owner + */ + constructor() { + console.log("constructor"); + owner = msg.sender; // 'msg.sender' is sender of current call, contract deployer for a constructor + emit OwnerSet(address(0), owner); + } + + /** + * @dev Change owner + * @param newOwner address of new owner + */ + function changeOwner(address newOwner) public isOwner { + console.log("inside changeOwner"); + emit OwnerSet(owner, newOwner); + owner = newOwner; + } + + /** + * @dev Return owner address + * @return address of owner + */ + function getOwner() external view returns (address) { + console.log("inside getOwner"); + return owner; + } +}` diff --git a/apps/remix-ide-e2e/src/types/index.d.ts b/apps/remix-ide-e2e/src/types/index.d.ts index 7033bf2057..f5f9287065 100644 --- a/apps/remix-ide-e2e/src/types/index.d.ts +++ b/apps/remix-ide-e2e/src/types/index.d.ts @@ -23,7 +23,7 @@ declare module 'nightwatch' { journalLastChildIncludes(val: string): NightwatchBrowser, executeScript(script: string): NightwatchBrowser, clearEditableContent(cssSelector: string): NightwatchBrowser, - journalChildIncludes(val: string): NightwatchBrowser, + journalChildIncludes(val: string, opts = { shouldHaveOnlyOneOccurence: boolean }): NightwatchBrowser, debugTransaction(index: number): NightwatchBrowser, checkElementStyle(cssSelector: string, styleProperty: string, expectedResult: string): NightwatchBrowser, openFile(name: string): NightwatchBrowser, diff --git a/apps/remix-ide/.babelrc b/apps/remix-ide/.babelrc index 2b7bafa5fa..e60d3036a3 100644 --- a/apps/remix-ide/.babelrc +++ b/apps/remix-ide/.babelrc @@ -1,3 +1,4 @@ { - "presets": ["@babel/preset-env", "@babel/preset-react"] + "presets": ["@babel/preset-env", "@babel/preset-react"], + "plugins": ["@babel/plugin-proposal-class-properties"] } diff --git a/apps/remix-ide/src/app/editor/editor.js b/apps/remix-ide/src/app/editor/editor.js index a6ea53ca9b..3adf7911db 100644 --- a/apps/remix-ide/src/app/editor/editor.js +++ b/apps/remix-ide/src/app/editor/editor.js @@ -97,7 +97,7 @@ class Editor extends Plugin { this.emit(name, ...params) // plugin stack } - onActivation () { + async onActivation () { this.activated = true this.on('sidePanel', 'focusChanged', (name) => { this.keepDecorationsFor(name, 'sourceAnnotationsPerFile') @@ -108,14 +108,15 @@ class Editor extends Plugin { }) const translateTheme = (theme) => this._themes[theme.name === 'Dark' ? 'remixDark' : theme.quality] - this.on('theme', 'themeChanged', (theme) => { - this.currentTheme = translateTheme(theme) - this.renderComponent() - }) - this.call('theme', 'currentTheme', (theme) => { + this.on('theme', 'themeLoaded', (theme) => { this.currentTheme = translateTheme(theme) this.renderComponent() }) + try { + this.currentTheme = translateTheme(await this.call('theme', 'currentTheme')) + } catch (e) { + console.log('unable to select the theme ' + e.message) + } this.renderComponent() } diff --git a/apps/remix-ide/src/app/files/fileProvider.js b/apps/remix-ide/src/app/files/fileProvider.js index ea46dd8b72..d5873e43ef 100644 --- a/apps/remix-ide/src/app/files/fileProvider.js +++ b/apps/remix-ide/src/app/files/fileProvider.js @@ -17,8 +17,9 @@ class FileProvider { } addNormalizedName (path, url) { - this.providerExternalsStorage.set(this.type + '/' + path, url) - this.providerExternalsStorage.set(this.reverseKey + url, this.type + '/' + path) + if (this.type) path = this.type + '/' + path + this.providerExternalsStorage.set(path, url) + this.providerExternalsStorage.set(this.reverseKey + url, path) } removeNormalizedName (path) { diff --git a/apps/remix-ide/src/app/panels/tab-proxy.js b/apps/remix-ide/src/app/panels/tab-proxy.js index 7c652ab78b..5b65254830 100644 --- a/apps/remix-ide/src/app/panels/tab-proxy.js +++ b/apps/remix-ide/src/app/panels/tab-proxy.js @@ -191,13 +191,6 @@ export class TabProxy extends Plugin { } } - switchToActiveTab () { - const active = this.tabsApi.active() - if (active && this._handlers[active]) { - this.switchTab(active) - } - } - renameTab (oldName, newName) { this.addTab(newName, '', () => { this.fileManager.open(newName) @@ -236,19 +229,21 @@ export class TabProxy extends Plugin { }) formatPath.shift() if (formatPath.length > 0) { - const duplicateTabName = this.loadedTabs.find(({ title }) => title === formatPath.join('/')).name - const duplicateTabPath = duplicateTabName.split('/') - const duplicateTabFormatPath = [...duplicateTabPath].reverse() - const duplicateTabTitle = duplicateTabFormatPath.slice(0, titleLength).reverse().join('/') - - this.loadedTabs.push({ - id: duplicateTabName, - name: duplicateTabName, - title: duplicateTabTitle, - icon, - tooltip: duplicateTabName, - iconClass: helper.getPathIcon(duplicateTabName) - }) + const index = this.loadedTabs.findIndex(({ title }) => title === formatPath.join('/')) + if (index > -1) { + const duplicateTabName = this.loadedTabs[index].name + const duplicateTabPath = duplicateTabName.split('/') + const duplicateTabFormatPath = [...duplicateTabPath].reverse() + const duplicateTabTitle = duplicateTabFormatPath.slice(0, titleLength).reverse().join('/') + this.loadedTabs[index] = { + id: duplicateTabName, + name: duplicateTabName, + title: duplicateTabTitle, + icon, + tooltip: duplicateTabName, + iconClass: helper.getPathIcon(duplicateTabName) + } + } } break } @@ -271,10 +266,14 @@ export class TabProxy extends Plugin { removeTab (name) { delete this._handlers[name] - this.switchToActiveTab() - this.loadedTabs = this.loadedTabs.filter(tab => tab.name !== name) + let previous = null + this.loadedTabs = this.loadedTabs.filter((tab, index) => { + if (tab.name === name) previous = this.loadedTabs[index - 1] + return tab.name !== name + }) this.renderComponent() this.updateImgStyles() + if (previous) this.switchTab(previous.name) } addHandler (type, fn) { diff --git a/apps/remix-ide/src/app/panels/terminal.js b/apps/remix-ide/src/app/panels/terminal.js index 0f358b2611..df0510b150 100644 --- a/apps/remix-ide/src/app/panels/terminal.js +++ b/apps/remix-ide/src/app/panels/terminal.js @@ -86,8 +86,6 @@ class Terminal extends Plugin { this.call('menuicons', 'select', 'debugger') this.call('debugger', 'debug', hash) }) - this.logHtmlResponse = [] - this.logResponse = [] } onActivation () { @@ -102,23 +100,11 @@ class Terminal extends Plugin { } logHtml (html) { - this.logHtmlResponse.push(html.innerText) - this.renderComponent() - this.resetLogHtml() - } - - resetLogHtml () { - this.logHtmlResponse = [] + this.terminalApi.logHtml(html) } log (message) { - this.logResponse.push(message) - this.renderComponent() - this.resetLog() - } - - resetLog () { - this.logResponse = [] + this.terminalApi.log(message) } render () { @@ -126,9 +112,11 @@ class Terminal extends Plugin { } renderComponent () { + const onReady = (api) => { this.terminalApi = api } ReactDOM.render( , this.element ) diff --git a/apps/remix-ide/src/app/tabs/runTab/contractDropdown.js b/apps/remix-ide/src/app/tabs/runTab/contractDropdown.js index a111147672..650b35b8c1 100644 --- a/apps/remix-ide/src/app/tabs/runTab/contractDropdown.js +++ b/apps/remix-ide/src/app/tabs/runTab/contractDropdown.js @@ -56,6 +56,7 @@ class ContractDropdownUI { } this.exEnvironment = this.blockchain.getProvider() this.networkName = network.name + this.networkId = network.id const savedConfig = window.localStorage.getItem(`ipfs/${this.exEnvironment}/${this.networkName}`) @@ -309,10 +310,10 @@ class ContractDropdownUI { const data = self.runView.compilersArtefacts.getCompilerAbstract(contractObject.contract.file) self.runView.compilersArtefacts.addResolvedContract(helper.addressToString(address), data) if (self.ipfsCheckedState) { - _paq.push(['trackEvent', 'udapp', 'DeployAndPublish', this.networkName]) + _paq.push(['trackEvent', 'udapp', 'DeployAndPublish', this.networkName, this.networkId]) publishToStorage('ipfs', self.runView.fileProvider, self.runView.fileManager, selectedContract) } else { - _paq.push(['trackEvent', 'udapp', 'DeployOnly', this.networkName]) + _paq.push(['trackEvent', 'udapp', 'DeployOnly', this.networkName, this.networkId]) } } @@ -346,7 +347,7 @@ class ContractDropdownUI { } deployContract (selectedContract, args, contractMetadata, compilerContracts, callbacks, confirmationCb) { - _paq.push(['trackEvent', 'udapp', 'DeployContractTo', this.networkName]) + _paq.push(['trackEvent', 'udapp', 'DeployContractTo', this.networkName, this.networkId]) const { statusCb } = callbacks if (!contractMetadata || (contractMetadata && contractMetadata.autoDeployLib)) { return this.blockchain.deployContractAndLibraries(selectedContract, args, contractMetadata, compilerContracts, callbacks, confirmationCb) diff --git a/apps/remix-ide/src/app/tabs/test-tab.js b/apps/remix-ide/src/app/tabs/test-tab.js index 1995abe1f6..c82a2bcc1d 100644 --- a/apps/remix-ide/src/app/tabs/test-tab.js +++ b/apps/remix-ide/src/app/tabs/test-tab.js @@ -7,7 +7,7 @@ var async = require('async') var tooltip = require('../ui/tooltip') var Renderer = require('../ui/renderer') var css = require('./styles/test-tab-styles') -var { UnitTestRunner } = require('@remix-project/remix-tests') +var { UnitTestRunner, assertLibCode } = require('@remix-project/remix-tests') const _paq = window._paq = window._paq || [] @@ -16,7 +16,7 @@ const TestTabLogic = require('./testTab/testTab') const profile = { name: 'solidityUnitTesting', displayName: 'Solidity unit testing', - methods: ['testFromPath', 'testFromSource', 'setTestFolderPath'], + methods: ['testFromPath', 'testFromSource', 'setTestFolderPath', 'getTestlibs'], events: [], icon: 'assets/img/unitTesting.webp', description: 'Fast tool to generate unit tests for your contracts', @@ -42,7 +42,7 @@ module.exports = class TestTab extends ViewPlugin { this.areTestsRunning = false this.defaultPath = 'tests' this.offsetToLineColumnConverter = offsetToLineColumnConverter - this.allFilesInvolved = [] + this.allFilesInvolved = ['.deps/remix-tests/remix_tests.sol', '.deps/remix-tests/remix_accounts.sol'] this.isDebugging = false this.currentErrors = [] @@ -74,11 +74,25 @@ module.exports = class TestTab extends ViewPlugin { } } + getTestlibs () { + return { assertLibCode, accountsLibCode: this.testRunner.accountsLibCode } + } + + async createTestLibs () { + const provider = await this.fileManager.currentFileProvider() + if (provider) { + provider.addExternal('.deps/remix-tests/remix_tests.sol', assertLibCode, 'remix_tests.sol') + provider.addExternal('.deps/remix-tests/remix_accounts.sol', this.testRunner.accountsLibCode, 'remix_accounts.sol') + } + } + async onActivation () { const isSolidityActive = await this.call('manager', 'isActive', 'solidity') if (!isSolidityActive) { await this.call('manager', 'activatePlugin', 'solidity') } + await this.testRunner.init() + await this.createTestLibs() this.updateRunAction() } @@ -110,9 +124,13 @@ module.exports = class TestTab extends ViewPlugin { this.setCurrentPath(this.defaultPath) }) + this.on('filePanel', 'workspaceCreated', async () => { + this.createTestLibs() + }) + this.testRunner.event.on('compilationFinished', (success, data, source) => { if (success) { - this.allFilesInvolved = Object.keys(data.sources) + this.allFilesInvolved.push(...Object.keys(data.sources)) // forwarding the event to the appManager infra // This is listened by compilerArtefacts to show data while debugging this.emit('compilationFinished', source.target, source, 'soljson', data) @@ -526,8 +544,8 @@ module.exports = class TestTab extends ViewPlugin { this.fileManager.readFile(testFilePath).then((content) => { const runningTests = {} runningTests[testFilePath] = { content } - const { currentVersion, evmVersion, optimize, runs } = this.compileTab.getCurrentCompilerConfig() - const currentCompilerUrl = urlFromVersion(currentVersion) + const { currentVersion, evmVersion, optimize, runs, isUrl } = this.compileTab.getCurrentCompilerConfig() + const currentCompilerUrl = isUrl ? currentVersion : urlFromVersion(currentVersion) const compilerConfig = { currentCompilerUrl, evmVersion, diff --git a/apps/remix-ide/src/app/tabs/theme-module.js b/apps/remix-ide/src/app/tabs/theme-module.js index b3f62cd905..1aaadda952 100644 --- a/apps/remix-ide/src/app/tabs/theme-module.js +++ b/apps/remix-ide/src/app/tabs/theme-module.js @@ -79,10 +79,17 @@ export class ThemeModule extends Plugin { throw new Error(`Theme ${themeName} doesn't exist`) } const next = themeName || this.active // Name + if (next === this.active) return _paq.push(['trackEvent', 'themeModule', 'switchTo', next]) const nextTheme = this.themes[next] // Theme if (!this.forced) this._deps.config.set('settings/theme', next) - document.getElementById('theme-link').setAttribute('href', nextTheme.url) + document.getElementById('theme-link').remove() + const theme = yo`` + theme.addEventListener('load', () => { + this.emit('themeLoaded', nextTheme) + this.events.emit('themeLoaded', nextTheme) + }) + document.head.insertBefore(theme, document.head.firstChild) document.documentElement.style.setProperty('--theme', nextTheme.quality) if (themeName) this.active = themeName // TODO: Only keep `this.emit` (issue#2210) diff --git a/apps/remix-ide/webpack.config.js b/apps/remix-ide/webpack.config.js index 02143af15b..4af33b4f3a 100644 --- a/apps/remix-ide/webpack.config.js +++ b/apps/remix-ide/webpack.config.js @@ -21,7 +21,7 @@ module.exports = config => { mode: 'production', devtool: 'source-map', optimization: { - minimize: true, + minimize: false, minimizer: [new TerserPlugin()] } } diff --git a/apps/solidity-compiler/src/app/compiler-api.ts b/apps/solidity-compiler/src/app/compiler-api.ts index 8082002ff4..733ee0026f 100644 --- a/apps/solidity-compiler/src/app/compiler-api.ts +++ b/apps/solidity-compiler/src/app/compiler-api.ts @@ -143,12 +143,17 @@ export const CompilerApiMixin = (Base) => class extends Base { // This function is used for passing the compiler configuration to 'remix-tests' getCurrentCompilerConfig () { const compilerState = this.getCompilerState() - return { + let compilerDetails: any = { currentVersion: compilerState.currentVersion, evmVersion: compilerState.evmVersion, optimize: compilerState.optimize, runs: compilerState.runs } + if (this.data.loading) { + compilerDetails.currentVersion = this.data.loadingUrl + compilerDetails.isUrl = true + } + return compilerDetails } /** @@ -193,8 +198,9 @@ export const CompilerApiMixin = (Base) => class extends Base { if (this.onContentChanged) this.onContentChanged() }) - this.data.eventHandlers.onLoadingCompiler = () => { + this.data.eventHandlers.onLoadingCompiler = (url) => { this.data.loading = true + this.data.loadingUrl = url this.emit('statusChanged', { key: 'loading', title: 'loading compiler...', type: 'info' }) } this.compiler.event.register('loadingCompiler', this.data.eventHandlers.onLoadingCompiler) diff --git a/babel.config.js b/babel.config.js index 41068f8f4f..6196b51b81 100644 --- a/babel.config.js +++ b/babel.config.js @@ -1,4 +1,4 @@ module.exports = { "presets": ["@babel/preset-react", "@babel/preset-typescript"], - "plugins": ["@babel/plugin-transform-modules-commonjs"] + "plugins": ["@babel/plugin-transform-modules-commonjs", "@babel/plugin-proposal-class-properties"] } \ No newline at end of file diff --git a/libs/remix-core-plugin/src/lib/compiler-content-imports.ts b/libs/remix-core-plugin/src/lib/compiler-content-imports.ts index 2e20d5ebb7..8c243e37a8 100644 --- a/libs/remix-core-plugin/src/lib/compiler-content-imports.ts +++ b/libs/remix-core-plugin/src/lib/compiler-content-imports.ts @@ -1,7 +1,6 @@ 'use strict' import { Plugin } from '@remixproject/engine' import { RemixURLResolver } from '@remix-project/remix-url-resolver' -const remixTests = require('@remix-project/remix-tests') const profile = { name: 'contentImport', @@ -117,7 +116,6 @@ export class CompilerImports extends Plugin { * @returns {Promise} - string content */ async resolveAndSave (url, targetPath) { - if (url.indexOf('remix_tests.sol') !== -1) return remixTests.assertLibCode try { const provider = await this.call('fileManager', 'getProviderOf', url) if (provider) { diff --git a/libs/remix-lib/src/execution/txRunnerVM.ts b/libs/remix-lib/src/execution/txRunnerVM.ts index 901652da0d..428f377b39 100644 --- a/libs/remix-lib/src/execution/txRunnerVM.ts +++ b/libs/remix-lib/src/execution/txRunnerVM.ts @@ -15,6 +15,7 @@ export class TxRunnerVM { blocks logsManager commonContext + nextNonceForCall: number getVMObject: () => any constructor (vmaccounts, api, getVMObject) { @@ -31,6 +32,13 @@ export class TxRunnerVM { this.vmaccounts = vmaccounts this.queusTxs = [] this.blocks = [] + /* + txHash is generated using the nonce, + in order to have unique transaction hash, we need to keep using different nonce (in case of a call) + so we increment this value after each call. + For this to function we also need to skip nonce validation, in the vm: `{ skipNonce: true }` + */ + this.nextNonceForCall = 0 } execute (args, confirmationCb, gasEstimationForceSend, promptCb, callback) { @@ -75,7 +83,7 @@ export class TxRunnerVM { let tx if (!EIP1559) { tx = Transaction.fromTxData({ - nonce: new BN(res.nonce), + nonce: useCall ? this.nextNonceForCall : new BN(res.nonce), gasPrice: '0x1', gasLimit: gasLimit, to: to, @@ -84,7 +92,7 @@ export class TxRunnerVM { }, { common: this.commonContext }).sign(account.privateKey) } else { tx = FeeMarketEIP1559Transaction.fromTxData({ - nonce: new BN(res.nonce), + nonce: useCall ? this.nextNonceForCall : new BN(res.nonce), maxPriorityFeePerGas: '0x01', maxFeePerGas: '0x1', gasLimit: gasLimit, @@ -93,6 +101,7 @@ export class TxRunnerVM { data: Buffer.from(data.slice(2), 'hex') }).sign(account.privateKey) } + if (useCall) this.nextNonceForCall++ const coinbases = ['0x0e9281e9c6a0808672eaba6bd1220e144c9bb07a', '0x8945a1288dc78a6d8952a92c77aee6730b414778', '0x94d76e24f818426ae84aa404140e8d5f60e10e7e'] const difficulties = [new BN('69762765929000', 10), new BN('70762765929000', 10), new BN('71762765929000', 10)] @@ -127,7 +136,7 @@ export class TxRunnerVM { } runBlockInVm (tx, block, callback) { - this.getVMObject().vm.runBlock({ block: block, generate: true, skipBlockValidation: true, skipBalance: false }).then((results) => { + this.getVMObject().vm.runBlock({ block: block, generate: true, skipBlockValidation: true, skipBalance: false, skipNonce: true }).then((results) => { const result = results.results[0] if (result) { const status = result.execResult.exceptionError ? 0 : 1 diff --git a/libs/remix-lib/src/web3Provider/web3VmProvider.ts b/libs/remix-lib/src/web3Provider/web3VmProvider.ts index f29e225e5a..49fa4d80a9 100644 --- a/libs/remix-lib/src/web3Provider/web3VmProvider.ts +++ b/libs/remix-lib/src/web3Provider/web3VmProvider.ts @@ -304,7 +304,9 @@ export class Web3VmProvider { nextKey: null }) } - cb('unable to retrieve storage ' + txIndex + ' ' + address) + // Before https://github.com/ethereum/remix-project/pull/1703, it used to throw error as + // 'unable to retrieve storage ' + txIndex + ' ' + address + cb(null, { storage: {} }) } getBlockNumber (cb) { cb(null, 'vm provider') } diff --git a/libs/remix-tests/sol/tests_accounts.sol.ts b/libs/remix-tests/sol/tests_accounts.sol.ts index d97aae8f34..847e37f083 100644 --- a/libs/remix-tests/sol/tests_accounts.sol.ts +++ b/libs/remix-tests/sol/tests_accounts.sol.ts @@ -3,7 +3,7 @@ module.exports = `// SPDX-License-Identifier: GPL-3.0 pragma solidity >=0.4.22 <0.9.0; library TestsAccounts { - function getAccount(uint index) public returns (address) { + function getAccount(uint index) pure public returns (address) { >accounts< return accounts[index]; } diff --git a/libs/remix-tests/src/compiler.ts b/libs/remix-tests/src/compiler.ts index e15bb656e4..f4b9250ecf 100644 --- a/libs/remix-tests/src/compiler.ts +++ b/libs/remix-tests/src/compiler.ts @@ -12,13 +12,13 @@ function regexIndexOf (inputString: string, regex: RegExp, startpos = 0) { return (indexOf >= 0) ? (indexOf + (startpos)) : indexOf } -function writeTestAccountsContract (accounts: string[]) { +export function writeTestAccountsContract (accounts: string[]) { const testAccountContract = require('../sol/tests_accounts.sol') let body = `address[${accounts.length}] memory accounts;` if (!accounts.length) body += ';' else { accounts.map((address, index) => { - body += `\naccounts[${index}] = ${address};\n` + body += `\n\t\taccounts[${index}] = ${address};\n` }) } return testAccountContract.replace('>accounts<', body) @@ -172,14 +172,7 @@ export function compileFileOrFiles (filename: string, isDirectory: boolean, opts */ export function compileContractSources (sources: SrcIfc, compilerConfig: CompilerConfiguration, importFileCb: any, opts: any, cb): void { let compiler - const accounts: string[] = opts.accounts || [] const filepath = opts.testFilePath || '' - // Iterate over sources keys. Inject test libraries. Inject test library import statements. - if (!('remix_tests.sol' in sources) && !('tests.sol' in sources)) { - sources['tests.sol'] = { content: require('../sol/tests.sol.js') } - sources['remix_tests.sol'] = { content: require('../sol/tests.sol.js') } - sources['remix_accounts.sol'] = { content: writeTestAccountsContract(accounts) } - } const testFileImportRegEx = /^(import)\s['"](remix_tests.sol|tests.sol)['"];/gm const includeTestLibs = '\nimport \'remix_tests.sol\';\n' diff --git a/libs/remix-tests/src/deployer.ts b/libs/remix-tests/src/deployer.ts index d700b08de5..505281fe00 100644 --- a/libs/remix-tests/src/deployer.ts +++ b/libs/remix-tests/src/deployer.ts @@ -11,18 +11,12 @@ import { compilationInterface } from './types' * @param callback Callback */ -export function deployAll (compileResult: compilationInterface, web3: Web3, withDoubleGas: boolean, deployCb, callback) { +export function deployAll (compileResult: compilationInterface, web3: Web3, testsAccounts, withDoubleGas: boolean, deployCb, callback) { const compiledObject = {} const contracts = {} - let accounts: string[] = [] + const accounts: string[] = testsAccounts async.waterfall([ - function getAccountList (next) { - web3.eth.getAccounts((_err, _accounts) => { - accounts = _accounts - next() - }) - }, function getContractData (next) { for (const contractFile in compileResult) { for (const contractName in compileResult[contractFile]) { diff --git a/libs/remix-tests/src/index.ts b/libs/remix-tests/src/index.ts index 9552d5b5ee..37862a47c6 100644 --- a/libs/remix-tests/src/index.ts +++ b/libs/remix-tests/src/index.ts @@ -3,3 +3,4 @@ export { UnitTestRunner } from './runTestSources' export { runTest } from './testRunner' export * from './types' export const assertLibCode = require('../sol/tests.sol') +export { writeTestAccountsContract } from './compiler' diff --git a/libs/remix-tests/src/runTestFiles.ts b/libs/remix-tests/src/runTestFiles.ts index d9ed0c2b6e..dbf2b16e46 100644 --- a/libs/remix-tests/src/runTestFiles.ts +++ b/libs/remix-tests/src/runTestFiles.ts @@ -61,13 +61,13 @@ export function runTestFiles (filepath: string, isDirectory: boolean, web3: Web3 for (const filename in asts) { if (filename.endsWith('_test.sol')) { sourceASTs[filename] = asts[filename].ast } } - deployAll(compilationResult, web3, false, null, (err, contracts) => { + deployAll(compilationResult, web3, accounts, false, null, (err, contracts) => { if (err) { // If contract deployment fails because of 'Out of Gas' error, try again with double gas // This is temporary, should be removed when remix-tests will have a dedicated UI to // accept deployment params from UI if (err.message.includes('The contract code couldn\'t be stored, please check your gas limit')) { - deployAll(compilationResult, web3, true, null, (error, contracts) => { + deployAll(compilationResult, web3, accounts, true, null, (error, contracts) => { if (error) next([{ message: 'contract deployment failed after trying twice: ' + error.message, severity: 'error' }]) // IDE expects errors in array else next(null, compilationResult, contracts) }) diff --git a/libs/remix-tests/src/runTestSources.ts b/libs/remix-tests/src/runTestSources.ts index ecc35aa3f2..9d8b8ba400 100644 --- a/libs/remix-tests/src/runTestSources.ts +++ b/libs/remix-tests/src/runTestSources.ts @@ -1,9 +1,7 @@ import async, { ErrorCallback } from 'async' - -import { compileContractSources } from './compiler' +import { compileContractSources, writeTestAccountsContract } from './compiler' import { deployAll } from './deployer' import { runTest } from './testRunner' - import Web3 from 'web3' import { EventEmitter } from 'events' import { Provider, extend } from '@remix-project/remix-simulator' @@ -15,13 +13,22 @@ require('colors') export class UnitTestRunner { event + accountsLibCode + testsAccounts: string[] | null + web3 constructor () { this.event = new EventEmitter() } - async createWeb3Provider () { - const web3 = new Web3() + async init (web3 = null, accounts = null) { + this.web3 = await this.createWeb3Provider(web3) + this.testsAccounts = accounts || await this.web3.eth.getAccounts() + this.accountsLibCode = writeTestAccountsContract(this.testsAccounts) + } + + async createWeb3Provider (optWeb3) { + const web3 = optWeb3 || new Web3() const provider: any = new Provider() await provider.init() web3.setProvider(provider) @@ -42,30 +49,23 @@ export class UnitTestRunner { async runTestSources (contractSources: SrcIfc, compilerConfig: CompilerConfiguration, testCallback, resultCallback, deployCb:any, finalCallback: any, importFileCb, opts: Options) { opts = opts || {} const sourceASTs: any = {} - const web3 = opts.web3 || await this.createWeb3Provider() - let accounts: string[] | null = opts.accounts || null + if (opts.web3 || opts.accounts) this.init(opts.web3, opts.accounts) + async.waterfall([ - function getAccountList (next) { - if (accounts) return next() - web3.eth.getAccounts((_err, _accounts) => { - accounts = _accounts - next() - }) - }, (next) => { - compileContractSources(contractSources, compilerConfig, importFileCb, { accounts, testFilePath: opts.testFilePath, event: this.event }, next) + compileContractSources(contractSources, compilerConfig, importFileCb, { accounts: this.testsAccounts, testFilePath: opts.testFilePath, event: this.event }, next) }, - function deployAllContracts (compilationResult: compilationInterface, asts: ASTInterface, next) { + (compilationResult: compilationInterface, asts: ASTInterface, next) => { for (const filename in asts) { if (filename.endsWith('_test.sol')) { sourceASTs[filename] = asts[filename].ast } } - deployAll(compilationResult, web3, false, deployCb, (err, contracts) => { + deployAll(compilationResult, this.web3, this.testsAccounts, false, deployCb, (err, contracts) => { if (err) { // If contract deployment fails because of 'Out of Gas' error, try again with double gas // This is temporary, should be removed when remix-tests will have a dedicated UI to // accept deployment params from UI if (err.message.includes('The contract code couldn\'t be stored, please check your gas limit')) { - deployAll(compilationResult, web3, true, deployCb, (error, contracts) => { + deployAll(compilationResult, this.web3, this.testsAccounts, true, deployCb, (error, contracts) => { if (error) next([{ message: 'contract deployment failed after trying twice: ' + error.message, severity: 'error' }]) // IDE expects errors in array else next(null, compilationResult, contracts) }) @@ -88,7 +88,7 @@ export class UnitTestRunner { } next(null, contractsToTest, contractsToTestDetails, contracts) }, - function runTests (contractsToTest: string[], contractsToTestDetails: any[], contracts: any, next) { + (contractsToTest: string[], contractsToTestDetails: any[], contracts: any, next) => { let totalPassing = 0 let totalFailing = 0 let totalTime = 0 @@ -111,7 +111,7 @@ export class UnitTestRunner { async.eachOfLimit(contractsToTest, 1, (contractName: string, index: string | number, cb: ErrorCallback) => { const fileAST: AstNode = sourceASTs[contracts[contractName]['filename']] - runTest(contractName, contracts[contractName], contractsToTestDetails[index], fileAST, { accounts, web3 }, _testCallback, (err, result) => { + runTest(contractName, contracts[contractName], contractsToTestDetails[index], fileAST, { accounts: this.testsAccounts, web3: this.web3 }, _testCallback, (err, result) => { if (err) { return cb(err) } diff --git a/libs/remix-tests/tests/testRunner.spec.ts b/libs/remix-tests/tests/testRunner.spec.ts index 928491faa5..2f6d363a31 100644 --- a/libs/remix-tests/tests/testRunner.spec.ts +++ b/libs/remix-tests/tests/testRunner.spec.ts @@ -67,7 +67,7 @@ async function compileAndDeploy(filename: string, callback: Function) { } try { compilationData = compilationResult - deployAll(compilationResult, web3, false, null, next) + deployAll(compilationResult, web3, accounts, false, null, next) } catch (e) { throw e } 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 fceb98d971..a9124bb866 100644 --- a/libs/remix-ui/editor/src/lib/remix-ui-editor.tsx +++ b/libs/remix-ui/editor/src/lib/remix-ui-editor.tsx @@ -1,6 +1,7 @@ import React, { useState, useRef, useEffect, useReducer } from 'react' // eslint-disable-line import Editor from '@monaco-editor/react' import { reducerActions, reducerListener, initialState } from './actions/editor' +import { language } from './syntax' import './remix-ui-editor.css' @@ -77,13 +78,39 @@ export const EditorUI = (props: EditorUIProps) => { const [editorModelsState, dispatch] = useReducer(reducerActions, initialState) + const defineAndSetDarkTheme = (monaco) => { + // see https://microsoft.github.io/monaco-editor/playground.html#customizing-the-appearence-exposed-colors + const lightColor = window.getComputedStyle(document.documentElement).getPropertyValue('--light').trim() + const infoColor = window.getComputedStyle(document.documentElement).getPropertyValue('--info').trim() + const darkColor = window.getComputedStyle(document.documentElement).getPropertyValue('--dark').trim() + const grayColor = window.getComputedStyle(document.documentElement).getPropertyValue('--gray-dark').trim() + monaco.editor.defineTheme('remix-dark', { + base: 'vs-dark', + inherit: true, // can also be false to completely replace the builtin rules + rules: [ + { background: darkColor.replace('#', '') }, + { token: 'keyword.external', foreground: infoColor } + ], + colors: { + 'editor.background': darkColor, + 'editorSuggestWidget.background': lightColor, + 'editorSuggestWidget.selectedBackground': lightColor, + 'editorSuggestWidget.highlightForeground': infoColor, + 'editor.lineHighlightBorder': lightColor, + 'editor.lineHighlightBackground': grayColor, + 'editorGutter.background': lightColor + } + }) + monacoRef.current.editor.setTheme('remix-dark') + } + useEffect(() => { if (!monacoRef.current) return - monacoRef.current.editor.setTheme(props.theme) + if (props.theme === 'remix-dark') { + defineAndSetDarkTheme(monacoRef.current) + } else monacoRef.current.editor.setTheme(props.theme) }, [props.theme]) - if (monacoRef.current) monacoRef.current.editor.setTheme(props.theme) - const setAnnotationsbyFile = (uri) => { if (props.sourceAnnotationsPerFile[uri]) { const model = editorModelsState[uri]?.model @@ -137,8 +164,10 @@ export const EditorUI = (props: EditorUIProps) => { useEffect(() => { if (!editorRef.current) return currentFileRef.current = props.currentFile - editorRef.current.setModel(editorModelsState[props.currentFile].model) + const file = editorModelsState[props.currentFile] + editorRef.current.setModel(file.model) editorRef.current.updateOptions({ readOnly: editorModelsState[props.currentFile].readOnly }) + if (file.language === 'sol') monacoRef.current.editor.setModelLanguage(file.model, 'remix-solidity') setAnnotationsbyFile(props.currentFile) setMarkerbyFile(props.currentFile) }, [props.currentFile]) @@ -207,7 +236,9 @@ export const EditorUI = (props: EditorUIProps) => { function handleEditorDidMount (editor) { editorRef.current = editor - monacoRef.current.editor.setTheme(props.theme) + if (props.theme === 'remix-dark') { + defineAndSetDarkTheme(monacoRef.current) + } else monacoRef.current.editor.setTheme(props.theme) reducerListener(props.plugin, dispatch, monacoRef.current, editorRef.current, props.events) props.events.onEditorMounted() editor.onMouseUp((e) => { @@ -215,29 +246,20 @@ export const EditorUI = (props: EditorUIProps) => { (window as any).addRemixBreakpoint(e.target.position) } }) + editor.addCommand(monacoRef.current.KeyMod.CtrlCmd | monacoRef.current.KeyCode.US_EQUAL, () => { + editor.updateOptions({ fontSize: editor.getOption(42).fontSize + 1 }) + }) + editor.addCommand(monacoRef.current.KeyMod.CtrlCmd | monacoRef.current.KeyCode.US_MINUS, () => { + editor.updateOptions({ fontSize: editor.getOption(42).fontSize - 1 }) + }) } function handleEditorWillMount (monaco) { monacoRef.current = monaco - // see https://microsoft.github.io/monaco-editor/playground.html#customizing-the-appearence-exposed-colors - const lightColor = window.getComputedStyle(document.documentElement).getPropertyValue('--light').trim() - const infoColor = window.getComputedStyle(document.documentElement).getPropertyValue('--info').trim() - const darkColor = window.getComputedStyle(document.documentElement).getPropertyValue('--dark').trim() - const grayColor = window.getComputedStyle(document.documentElement).getPropertyValue('--gray-dark').trim() - monaco.editor.defineTheme('remix-dark', { - base: 'vs-dark', - inherit: true, // can also be false to completely replace the builtin rules - rules: [{ background: darkColor.replace('#', '') }], - colors: { - 'editor.background': darkColor, - 'editorSuggestWidget.background': lightColor, - 'editorSuggestWidget.selectedBackground': lightColor, - 'editorSuggestWidget.highlightForeground': infoColor, - 'editor.lineHighlightBorder': lightColor, - 'editor.lineHighlightBackground': grayColor, - 'editorGutter.background': lightColor - } - }) + // Register a new language + monacoRef.current.languages.register({ id: 'remix-solidity' }) + // Register a tokens provider for the language + monacoRef.current.languages.setMonarchTokensProvider('remix-solidity', language) } return ( diff --git a/libs/remix-ui/editor/src/lib/syntax.ts b/libs/remix-ui/editor/src/lib/syntax.ts new file mode 100644 index 0000000000..ccc53716d6 --- /dev/null +++ b/libs/remix-ui/editor/src/lib/syntax.ts @@ -0,0 +1,1384 @@ +/* eslint-disable */ +export const conf = { + comments: { + lineComment: '//', + blockComment: ['/*', '*/'] + }, + brackets: [ + ['{', '}'], + ['[', ']'], + ['(', ')'], + ['<', '>'] + ], + autoClosingPairs: [ + { open: '"', close: '"', notIn: ['string', 'comment'] }, + { open: '{', close: '}', notIn: ['string', 'comment'] }, + { open: '[', close: ']', notIn: ['string', 'comment'] }, + { open: '(', close: ')', notIn: ['string', 'comment'] } + ] +} + +export const language = { + defaultToken: '', + tokenPostfix: '.sol', + + brackets: [ + { token: 'delimiter.curly', open: '{', close: '}' }, + { token: 'delimiter.parenthesis', open: '(', close: ')' }, + { token: 'delimiter.square', open: '[', close: ']' }, + { token: 'delimiter.angle', open: '<', close: '>' } + ], + + keywords: [ + // Main keywords + 'pragma', + 'solidity', + 'contract', + 'library', + 'using', + 'struct', + 'function', + 'modifier', + 'constructor', + // Built-in types + 'address', + 'string', + 'bool', + // Other types + 'Int', + 'Uint', + 'Byte', + 'Fixed', + 'Ufixed', + // All int + 'int', + 'int8', + 'int16', + 'int24', + 'int32', + 'int40', + 'int48', + 'int56', + 'int64', + 'int72', + 'int80', + 'int88', + 'int96', + 'int104', + 'int112', + 'int120', + 'int128', + 'int136', + 'int144', + 'int152', + 'int160', + 'int168', + 'int176', + 'int184', + 'int192', + 'int200', + 'int208', + 'int216', + 'int224', + 'int232', + 'int240', + 'int248', + 'int256', + // All uint + 'uint', + 'uint8', + 'uint16', + 'uint24', + 'uint32', + 'uint40', + 'uint48', + 'uint56', + 'uint64', + 'uint72', + 'uint80', + 'uint88', + 'uint96', + 'uint104', + 'uint112', + 'uint120', + 'uint128', + 'uint136', + 'uint144', + 'uint152', + 'uint160', + 'uint168', + 'uint176', + 'uint184', + 'uint192', + 'uint200', + 'uint208', + 'uint216', + 'uint224', + 'uint232', + 'uint240', + 'uint248', + 'uint256', + // All Byte + 'byte', + 'bytes', + 'bytes1', + 'bytes2', + 'bytes3', + 'bytes4', + 'bytes5', + 'bytes6', + 'bytes7', + 'bytes8', + 'bytes9', + 'bytes10', + 'bytes11', + 'bytes12', + 'bytes13', + 'bytes14', + 'bytes15', + 'bytes16', + 'bytes17', + 'bytes18', + 'bytes19', + 'bytes20', + 'bytes21', + 'bytes22', + 'bytes23', + 'bytes24', + 'bytes25', + 'bytes26', + 'bytes27', + 'bytes28', + 'bytes29', + 'bytes30', + 'bytes31', + 'bytes32', + // All fixed + 'fixed', + 'fixed0x8', + 'fixed0x16', + 'fixed0x24', + 'fixed0x32', + 'fixed0x40', + 'fixed0x48', + 'fixed0x56', + 'fixed0x64', + 'fixed0x72', + 'fixed0x80', + 'fixed0x88', + 'fixed0x96', + 'fixed0x104', + 'fixed0x112', + 'fixed0x120', + 'fixed0x128', + 'fixed0x136', + 'fixed0x144', + 'fixed0x152', + 'fixed0x160', + 'fixed0x168', + 'fixed0x176', + 'fixed0x184', + 'fixed0x192', + 'fixed0x200', + 'fixed0x208', + 'fixed0x216', + 'fixed0x224', + 'fixed0x232', + 'fixed0x240', + 'fixed0x248', + 'fixed0x256', + 'fixed8x8', + 'fixed8x16', + 'fixed8x24', + 'fixed8x32', + 'fixed8x40', + 'fixed8x48', + 'fixed8x56', + 'fixed8x64', + 'fixed8x72', + 'fixed8x80', + 'fixed8x88', + 'fixed8x96', + 'fixed8x104', + 'fixed8x112', + 'fixed8x120', + 'fixed8x128', + 'fixed8x136', + 'fixed8x144', + 'fixed8x152', + 'fixed8x160', + 'fixed8x168', + 'fixed8x176', + 'fixed8x184', + 'fixed8x192', + 'fixed8x200', + 'fixed8x208', + 'fixed8x216', + 'fixed8x224', + 'fixed8x232', + 'fixed8x240', + 'fixed8x248', + 'fixed16x8', + 'fixed16x16', + 'fixed16x24', + 'fixed16x32', + 'fixed16x40', + 'fixed16x48', + 'fixed16x56', + 'fixed16x64', + 'fixed16x72', + 'fixed16x80', + 'fixed16x88', + 'fixed16x96', + 'fixed16x104', + 'fixed16x112', + 'fixed16x120', + 'fixed16x128', + 'fixed16x136', + 'fixed16x144', + 'fixed16x152', + 'fixed16x160', + 'fixed16x168', + 'fixed16x176', + 'fixed16x184', + 'fixed16x192', + 'fixed16x200', + 'fixed16x208', + 'fixed16x216', + 'fixed16x224', + 'fixed16x232', + 'fixed16x240', + 'fixed24x8', + 'fixed24x16', + 'fixed24x24', + 'fixed24x32', + 'fixed24x40', + 'fixed24x48', + 'fixed24x56', + 'fixed24x64', + 'fixed24x72', + 'fixed24x80', + 'fixed24x88', + 'fixed24x96', + 'fixed24x104', + 'fixed24x112', + 'fixed24x120', + 'fixed24x128', + 'fixed24x136', + 'fixed24x144', + 'fixed24x152', + 'fixed24x160', + 'fixed24x168', + 'fixed24x176', + 'fixed24x184', + 'fixed24x192', + 'fixed24x200', + 'fixed24x208', + 'fixed24x216', + 'fixed24x224', + 'fixed24x232', + 'fixed32x8', + 'fixed32x16', + 'fixed32x24', + 'fixed32x32', + 'fixed32x40', + 'fixed32x48', + 'fixed32x56', + 'fixed32x64', + 'fixed32x72', + 'fixed32x80', + 'fixed32x88', + 'fixed32x96', + 'fixed32x104', + 'fixed32x112', + 'fixed32x120', + 'fixed32x128', + 'fixed32x136', + 'fixed32x144', + 'fixed32x152', + 'fixed32x160', + 'fixed32x168', + 'fixed32x176', + 'fixed32x184', + 'fixed32x192', + 'fixed32x200', + 'fixed32x208', + 'fixed32x216', + 'fixed32x224', + 'fixed40x8', + 'fixed40x16', + 'fixed40x24', + 'fixed40x32', + 'fixed40x40', + 'fixed40x48', + 'fixed40x56', + 'fixed40x64', + 'fixed40x72', + 'fixed40x80', + 'fixed40x88', + 'fixed40x96', + 'fixed40x104', + 'fixed40x112', + 'fixed40x120', + 'fixed40x128', + 'fixed40x136', + 'fixed40x144', + 'fixed40x152', + 'fixed40x160', + 'fixed40x168', + 'fixed40x176', + 'fixed40x184', + 'fixed40x192', + 'fixed40x200', + 'fixed40x208', + 'fixed40x216', + 'fixed48x8', + 'fixed48x16', + 'fixed48x24', + 'fixed48x32', + 'fixed48x40', + 'fixed48x48', + 'fixed48x56', + 'fixed48x64', + 'fixed48x72', + 'fixed48x80', + 'fixed48x88', + 'fixed48x96', + 'fixed48x104', + 'fixed48x112', + 'fixed48x120', + 'fixed48x128', + 'fixed48x136', + 'fixed48x144', + 'fixed48x152', + 'fixed48x160', + 'fixed48x168', + 'fixed48x176', + 'fixed48x184', + 'fixed48x192', + 'fixed48x200', + 'fixed48x208', + 'fixed56x8', + 'fixed56x16', + 'fixed56x24', + 'fixed56x32', + 'fixed56x40', + 'fixed56x48', + 'fixed56x56', + 'fixed56x64', + 'fixed56x72', + 'fixed56x80', + 'fixed56x88', + 'fixed56x96', + 'fixed56x104', + 'fixed56x112', + 'fixed56x120', + 'fixed56x128', + 'fixed56x136', + 'fixed56x144', + 'fixed56x152', + 'fixed56x160', + 'fixed56x168', + 'fixed56x176', + 'fixed56x184', + 'fixed56x192', + 'fixed56x200', + 'fixed64x8', + 'fixed64x16', + 'fixed64x24', + 'fixed64x32', + 'fixed64x40', + 'fixed64x48', + 'fixed64x56', + 'fixed64x64', + 'fixed64x72', + 'fixed64x80', + 'fixed64x88', + 'fixed64x96', + 'fixed64x104', + 'fixed64x112', + 'fixed64x120', + 'fixed64x128', + 'fixed64x136', + 'fixed64x144', + 'fixed64x152', + 'fixed64x160', + 'fixed64x168', + 'fixed64x176', + 'fixed64x184', + 'fixed64x192', + 'fixed72x8', + 'fixed72x16', + 'fixed72x24', + 'fixed72x32', + 'fixed72x40', + 'fixed72x48', + 'fixed72x56', + 'fixed72x64', + 'fixed72x72', + 'fixed72x80', + 'fixed72x88', + 'fixed72x96', + 'fixed72x104', + 'fixed72x112', + 'fixed72x120', + 'fixed72x128', + 'fixed72x136', + 'fixed72x144', + 'fixed72x152', + 'fixed72x160', + 'fixed72x168', + 'fixed72x176', + 'fixed72x184', + 'fixed80x8', + 'fixed80x16', + 'fixed80x24', + 'fixed80x32', + 'fixed80x40', + 'fixed80x48', + 'fixed80x56', + 'fixed80x64', + 'fixed80x72', + 'fixed80x80', + 'fixed80x88', + 'fixed80x96', + 'fixed80x104', + 'fixed80x112', + 'fixed80x120', + 'fixed80x128', + 'fixed80x136', + 'fixed80x144', + 'fixed80x152', + 'fixed80x160', + 'fixed80x168', + 'fixed80x176', + 'fixed88x8', + 'fixed88x16', + 'fixed88x24', + 'fixed88x32', + 'fixed88x40', + 'fixed88x48', + 'fixed88x56', + 'fixed88x64', + 'fixed88x72', + 'fixed88x80', + 'fixed88x88', + 'fixed88x96', + 'fixed88x104', + 'fixed88x112', + 'fixed88x120', + 'fixed88x128', + 'fixed88x136', + 'fixed88x144', + 'fixed88x152', + 'fixed88x160', + 'fixed88x168', + 'fixed96x8', + 'fixed96x16', + 'fixed96x24', + 'fixed96x32', + 'fixed96x40', + 'fixed96x48', + 'fixed96x56', + 'fixed96x64', + 'fixed96x72', + 'fixed96x80', + 'fixed96x88', + 'fixed96x96', + 'fixed96x104', + 'fixed96x112', + 'fixed96x120', + 'fixed96x128', + 'fixed96x136', + 'fixed96x144', + 'fixed96x152', + 'fixed96x160', + 'fixed104x8', + 'fixed104x16', + 'fixed104x24', + 'fixed104x32', + 'fixed104x40', + 'fixed104x48', + 'fixed104x56', + 'fixed104x64', + 'fixed104x72', + 'fixed104x80', + 'fixed104x88', + 'fixed104x96', + 'fixed104x104', + 'fixed104x112', + 'fixed104x120', + 'fixed104x128', + 'fixed104x136', + 'fixed104x144', + 'fixed104x152', + 'fixed112x8', + 'fixed112x16', + 'fixed112x24', + 'fixed112x32', + 'fixed112x40', + 'fixed112x48', + 'fixed112x56', + 'fixed112x64', + 'fixed112x72', + 'fixed112x80', + 'fixed112x88', + 'fixed112x96', + 'fixed112x104', + 'fixed112x112', + 'fixed112x120', + 'fixed112x128', + 'fixed112x136', + 'fixed112x144', + 'fixed120x8', + 'fixed120x16', + 'fixed120x24', + 'fixed120x32', + 'fixed120x40', + 'fixed120x48', + 'fixed120x56', + 'fixed120x64', + 'fixed120x72', + 'fixed120x80', + 'fixed120x88', + 'fixed120x96', + 'fixed120x104', + 'fixed120x112', + 'fixed120x120', + 'fixed120x128', + 'fixed120x136', + 'fixed128x8', + 'fixed128x16', + 'fixed128x24', + 'fixed128x32', + 'fixed128x40', + 'fixed128x48', + 'fixed128x56', + 'fixed128x64', + 'fixed128x72', + 'fixed128x80', + 'fixed128x88', + 'fixed128x96', + 'fixed128x104', + 'fixed128x112', + 'fixed128x120', + 'fixed128x128', + 'fixed136x8', + 'fixed136x16', + 'fixed136x24', + 'fixed136x32', + 'fixed136x40', + 'fixed136x48', + 'fixed136x56', + 'fixed136x64', + 'fixed136x72', + 'fixed136x80', + 'fixed136x88', + 'fixed136x96', + 'fixed136x104', + 'fixed136x112', + 'fixed136x120', + 'fixed144x8', + 'fixed144x16', + 'fixed144x24', + 'fixed144x32', + 'fixed144x40', + 'fixed144x48', + 'fixed144x56', + 'fixed144x64', + 'fixed144x72', + 'fixed144x80', + 'fixed144x88', + 'fixed144x96', + 'fixed144x104', + 'fixed144x112', + 'fixed152x8', + 'fixed152x16', + 'fixed152x24', + 'fixed152x32', + 'fixed152x40', + 'fixed152x48', + 'fixed152x56', + 'fixed152x64', + 'fixed152x72', + 'fixed152x80', + 'fixed152x88', + 'fixed152x96', + 'fixed152x104', + 'fixed160x8', + 'fixed160x16', + 'fixed160x24', + 'fixed160x32', + 'fixed160x40', + 'fixed160x48', + 'fixed160x56', + 'fixed160x64', + 'fixed160x72', + 'fixed160x80', + 'fixed160x88', + 'fixed160x96', + 'fixed168x8', + 'fixed168x16', + 'fixed168x24', + 'fixed168x32', + 'fixed168x40', + 'fixed168x48', + 'fixed168x56', + 'fixed168x64', + 'fixed168x72', + 'fixed168x80', + 'fixed168x88', + 'fixed176x8', + 'fixed176x16', + 'fixed176x24', + 'fixed176x32', + 'fixed176x40', + 'fixed176x48', + 'fixed176x56', + 'fixed176x64', + 'fixed176x72', + 'fixed176x80', + 'fixed184x8', + 'fixed184x16', + 'fixed184x24', + 'fixed184x32', + 'fixed184x40', + 'fixed184x48', + 'fixed184x56', + 'fixed184x64', + 'fixed184x72', + 'fixed192x8', + 'fixed192x16', + 'fixed192x24', + 'fixed192x32', + 'fixed192x40', + 'fixed192x48', + 'fixed192x56', + 'fixed192x64', + 'fixed200x8', + 'fixed200x16', + 'fixed200x24', + 'fixed200x32', + 'fixed200x40', + 'fixed200x48', + 'fixed200x56', + 'fixed208x8', + 'fixed208x16', + 'fixed208x24', + 'fixed208x32', + 'fixed208x40', + 'fixed208x48', + 'fixed216x8', + 'fixed216x16', + 'fixed216x24', + 'fixed216x32', + 'fixed216x40', + 'fixed224x8', + 'fixed224x16', + 'fixed224x24', + 'fixed224x32', + 'fixed232x8', + 'fixed232x16', + 'fixed232x24', + 'fixed240x8', + 'fixed240x16', + 'fixed248x8', + // All ufixed + 'ufixed', + 'ufixed0x8', + 'ufixed0x16', + 'ufixed0x24', + 'ufixed0x32', + 'ufixed0x40', + 'ufixed0x48', + 'ufixed0x56', + 'ufixed0x64', + 'ufixed0x72', + 'ufixed0x80', + 'ufixed0x88', + 'ufixed0x96', + 'ufixed0x104', + 'ufixed0x112', + 'ufixed0x120', + 'ufixed0x128', + 'ufixed0x136', + 'ufixed0x144', + 'ufixed0x152', + 'ufixed0x160', + 'ufixed0x168', + 'ufixed0x176', + 'ufixed0x184', + 'ufixed0x192', + 'ufixed0x200', + 'ufixed0x208', + 'ufixed0x216', + 'ufixed0x224', + 'ufixed0x232', + 'ufixed0x240', + 'ufixed0x248', + 'ufixed0x256', + 'ufixed8x8', + 'ufixed8x16', + 'ufixed8x24', + 'ufixed8x32', + 'ufixed8x40', + 'ufixed8x48', + 'ufixed8x56', + 'ufixed8x64', + 'ufixed8x72', + 'ufixed8x80', + 'ufixed8x88', + 'ufixed8x96', + 'ufixed8x104', + 'ufixed8x112', + 'ufixed8x120', + 'ufixed8x128', + 'ufixed8x136', + 'ufixed8x144', + 'ufixed8x152', + 'ufixed8x160', + 'ufixed8x168', + 'ufixed8x176', + 'ufixed8x184', + 'ufixed8x192', + 'ufixed8x200', + 'ufixed8x208', + 'ufixed8x216', + 'ufixed8x224', + 'ufixed8x232', + 'ufixed8x240', + 'ufixed8x248', + 'ufixed16x8', + 'ufixed16x16', + 'ufixed16x24', + 'ufixed16x32', + 'ufixed16x40', + 'ufixed16x48', + 'ufixed16x56', + 'ufixed16x64', + 'ufixed16x72', + 'ufixed16x80', + 'ufixed16x88', + 'ufixed16x96', + 'ufixed16x104', + 'ufixed16x112', + 'ufixed16x120', + 'ufixed16x128', + 'ufixed16x136', + 'ufixed16x144', + 'ufixed16x152', + 'ufixed16x160', + 'ufixed16x168', + 'ufixed16x176', + 'ufixed16x184', + 'ufixed16x192', + 'ufixed16x200', + 'ufixed16x208', + 'ufixed16x216', + 'ufixed16x224', + 'ufixed16x232', + 'ufixed16x240', + 'ufixed24x8', + 'ufixed24x16', + 'ufixed24x24', + 'ufixed24x32', + 'ufixed24x40', + 'ufixed24x48', + 'ufixed24x56', + 'ufixed24x64', + 'ufixed24x72', + 'ufixed24x80', + 'ufixed24x88', + 'ufixed24x96', + 'ufixed24x104', + 'ufixed24x112', + 'ufixed24x120', + 'ufixed24x128', + 'ufixed24x136', + 'ufixed24x144', + 'ufixed24x152', + 'ufixed24x160', + 'ufixed24x168', + 'ufixed24x176', + 'ufixed24x184', + 'ufixed24x192', + 'ufixed24x200', + 'ufixed24x208', + 'ufixed24x216', + 'ufixed24x224', + 'ufixed24x232', + 'ufixed32x8', + 'ufixed32x16', + 'ufixed32x24', + 'ufixed32x32', + 'ufixed32x40', + 'ufixed32x48', + 'ufixed32x56', + 'ufixed32x64', + 'ufixed32x72', + 'ufixed32x80', + 'ufixed32x88', + 'ufixed32x96', + 'ufixed32x104', + 'ufixed32x112', + 'ufixed32x120', + 'ufixed32x128', + 'ufixed32x136', + 'ufixed32x144', + 'ufixed32x152', + 'ufixed32x160', + 'ufixed32x168', + 'ufixed32x176', + 'ufixed32x184', + 'ufixed32x192', + 'ufixed32x200', + 'ufixed32x208', + 'ufixed32x216', + 'ufixed32x224', + 'ufixed40x8', + 'ufixed40x16', + 'ufixed40x24', + 'ufixed40x32', + 'ufixed40x40', + 'ufixed40x48', + 'ufixed40x56', + 'ufixed40x64', + 'ufixed40x72', + 'ufixed40x80', + 'ufixed40x88', + 'ufixed40x96', + 'ufixed40x104', + 'ufixed40x112', + 'ufixed40x120', + 'ufixed40x128', + 'ufixed40x136', + 'ufixed40x144', + 'ufixed40x152', + 'ufixed40x160', + 'ufixed40x168', + 'ufixed40x176', + 'ufixed40x184', + 'ufixed40x192', + 'ufixed40x200', + 'ufixed40x208', + 'ufixed40x216', + 'ufixed48x8', + 'ufixed48x16', + 'ufixed48x24', + 'ufixed48x32', + 'ufixed48x40', + 'ufixed48x48', + 'ufixed48x56', + 'ufixed48x64', + 'ufixed48x72', + 'ufixed48x80', + 'ufixed48x88', + 'ufixed48x96', + 'ufixed48x104', + 'ufixed48x112', + 'ufixed48x120', + 'ufixed48x128', + 'ufixed48x136', + 'ufixed48x144', + 'ufixed48x152', + 'ufixed48x160', + 'ufixed48x168', + 'ufixed48x176', + 'ufixed48x184', + 'ufixed48x192', + 'ufixed48x200', + 'ufixed48x208', + 'ufixed56x8', + 'ufixed56x16', + 'ufixed56x24', + 'ufixed56x32', + 'ufixed56x40', + 'ufixed56x48', + 'ufixed56x56', + 'ufixed56x64', + 'ufixed56x72', + 'ufixed56x80', + 'ufixed56x88', + 'ufixed56x96', + 'ufixed56x104', + 'ufixed56x112', + 'ufixed56x120', + 'ufixed56x128', + 'ufixed56x136', + 'ufixed56x144', + 'ufixed56x152', + 'ufixed56x160', + 'ufixed56x168', + 'ufixed56x176', + 'ufixed56x184', + 'ufixed56x192', + 'ufixed56x200', + 'ufixed64x8', + 'ufixed64x16', + 'ufixed64x24', + 'ufixed64x32', + 'ufixed64x40', + 'ufixed64x48', + 'ufixed64x56', + 'ufixed64x64', + 'ufixed64x72', + 'ufixed64x80', + 'ufixed64x88', + 'ufixed64x96', + 'ufixed64x104', + 'ufixed64x112', + 'ufixed64x120', + 'ufixed64x128', + 'ufixed64x136', + 'ufixed64x144', + 'ufixed64x152', + 'ufixed64x160', + 'ufixed64x168', + 'ufixed64x176', + 'ufixed64x184', + 'ufixed64x192', + 'ufixed72x8', + 'ufixed72x16', + 'ufixed72x24', + 'ufixed72x32', + 'ufixed72x40', + 'ufixed72x48', + 'ufixed72x56', + 'ufixed72x64', + 'ufixed72x72', + 'ufixed72x80', + 'ufixed72x88', + 'ufixed72x96', + 'ufixed72x104', + 'ufixed72x112', + 'ufixed72x120', + 'ufixed72x128', + 'ufixed72x136', + 'ufixed72x144', + 'ufixed72x152', + 'ufixed72x160', + 'ufixed72x168', + 'ufixed72x176', + 'ufixed72x184', + 'ufixed80x8', + 'ufixed80x16', + 'ufixed80x24', + 'ufixed80x32', + 'ufixed80x40', + 'ufixed80x48', + 'ufixed80x56', + 'ufixed80x64', + 'ufixed80x72', + 'ufixed80x80', + 'ufixed80x88', + 'ufixed80x96', + 'ufixed80x104', + 'ufixed80x112', + 'ufixed80x120', + 'ufixed80x128', + 'ufixed80x136', + 'ufixed80x144', + 'ufixed80x152', + 'ufixed80x160', + 'ufixed80x168', + 'ufixed80x176', + 'ufixed88x8', + 'ufixed88x16', + 'ufixed88x24', + 'ufixed88x32', + 'ufixed88x40', + 'ufixed88x48', + 'ufixed88x56', + 'ufixed88x64', + 'ufixed88x72', + 'ufixed88x80', + 'ufixed88x88', + 'ufixed88x96', + 'ufixed88x104', + 'ufixed88x112', + 'ufixed88x120', + 'ufixed88x128', + 'ufixed88x136', + 'ufixed88x144', + 'ufixed88x152', + 'ufixed88x160', + 'ufixed88x168', + 'ufixed96x8', + 'ufixed96x16', + 'ufixed96x24', + 'ufixed96x32', + 'ufixed96x40', + 'ufixed96x48', + 'ufixed96x56', + 'ufixed96x64', + 'ufixed96x72', + 'ufixed96x80', + 'ufixed96x88', + 'ufixed96x96', + 'ufixed96x104', + 'ufixed96x112', + 'ufixed96x120', + 'ufixed96x128', + 'ufixed96x136', + 'ufixed96x144', + 'ufixed96x152', + 'ufixed96x160', + 'ufixed104x8', + 'ufixed104x16', + 'ufixed104x24', + 'ufixed104x32', + 'ufixed104x40', + 'ufixed104x48', + 'ufixed104x56', + 'ufixed104x64', + 'ufixed104x72', + 'ufixed104x80', + 'ufixed104x88', + 'ufixed104x96', + 'ufixed104x104', + 'ufixed104x112', + 'ufixed104x120', + 'ufixed104x128', + 'ufixed104x136', + 'ufixed104x144', + 'ufixed104x152', + 'ufixed112x8', + 'ufixed112x16', + 'ufixed112x24', + 'ufixed112x32', + 'ufixed112x40', + 'ufixed112x48', + 'ufixed112x56', + 'ufixed112x64', + 'ufixed112x72', + 'ufixed112x80', + 'ufixed112x88', + 'ufixed112x96', + 'ufixed112x104', + 'ufixed112x112', + 'ufixed112x120', + 'ufixed112x128', + 'ufixed112x136', + 'ufixed112x144', + 'ufixed120x8', + 'ufixed120x16', + 'ufixed120x24', + 'ufixed120x32', + 'ufixed120x40', + 'ufixed120x48', + 'ufixed120x56', + 'ufixed120x64', + 'ufixed120x72', + 'ufixed120x80', + 'ufixed120x88', + 'ufixed120x96', + 'ufixed120x104', + 'ufixed120x112', + 'ufixed120x120', + 'ufixed120x128', + 'ufixed120x136', + 'ufixed128x8', + 'ufixed128x16', + 'ufixed128x24', + 'ufixed128x32', + 'ufixed128x40', + 'ufixed128x48', + 'ufixed128x56', + 'ufixed128x64', + 'ufixed128x72', + 'ufixed128x80', + 'ufixed128x88', + 'ufixed128x96', + 'ufixed128x104', + 'ufixed128x112', + 'ufixed128x120', + 'ufixed128x128', + 'ufixed136x8', + 'ufixed136x16', + 'ufixed136x24', + 'ufixed136x32', + 'ufixed136x40', + 'ufixed136x48', + 'ufixed136x56', + 'ufixed136x64', + 'ufixed136x72', + 'ufixed136x80', + 'ufixed136x88', + 'ufixed136x96', + 'ufixed136x104', + 'ufixed136x112', + 'ufixed136x120', + 'ufixed144x8', + 'ufixed144x16', + 'ufixed144x24', + 'ufixed144x32', + 'ufixed144x40', + 'ufixed144x48', + 'ufixed144x56', + 'ufixed144x64', + 'ufixed144x72', + 'ufixed144x80', + 'ufixed144x88', + 'ufixed144x96', + 'ufixed144x104', + 'ufixed144x112', + 'ufixed152x8', + 'ufixed152x16', + 'ufixed152x24', + 'ufixed152x32', + 'ufixed152x40', + 'ufixed152x48', + 'ufixed152x56', + 'ufixed152x64', + 'ufixed152x72', + 'ufixed152x80', + 'ufixed152x88', + 'ufixed152x96', + 'ufixed152x104', + 'ufixed160x8', + 'ufixed160x16', + 'ufixed160x24', + 'ufixed160x32', + 'ufixed160x40', + 'ufixed160x48', + 'ufixed160x56', + 'ufixed160x64', + 'ufixed160x72', + 'ufixed160x80', + 'ufixed160x88', + 'ufixed160x96', + 'ufixed168x8', + 'ufixed168x16', + 'ufixed168x24', + 'ufixed168x32', + 'ufixed168x40', + 'ufixed168x48', + 'ufixed168x56', + 'ufixed168x64', + 'ufixed168x72', + 'ufixed168x80', + 'ufixed168x88', + 'ufixed176x8', + 'ufixed176x16', + 'ufixed176x24', + 'ufixed176x32', + 'ufixed176x40', + 'ufixed176x48', + 'ufixed176x56', + 'ufixed176x64', + 'ufixed176x72', + 'ufixed176x80', + 'ufixed184x8', + 'ufixed184x16', + 'ufixed184x24', + 'ufixed184x32', + 'ufixed184x40', + 'ufixed184x48', + 'ufixed184x56', + 'ufixed184x64', + 'ufixed184x72', + 'ufixed192x8', + 'ufixed192x16', + 'ufixed192x24', + 'ufixed192x32', + 'ufixed192x40', + 'ufixed192x48', + 'ufixed192x56', + 'ufixed192x64', + 'ufixed200x8', + 'ufixed200x16', + 'ufixed200x24', + 'ufixed200x32', + 'ufixed200x40', + 'ufixed200x48', + 'ufixed200x56', + 'ufixed208x8', + 'ufixed208x16', + 'ufixed208x24', + 'ufixed208x32', + 'ufixed208x40', + 'ufixed208x48', + 'ufixed216x8', + 'ufixed216x16', + 'ufixed216x24', + 'ufixed216x32', + 'ufixed216x40', + 'ufixed224x8', + 'ufixed224x16', + 'ufixed224x24', + 'ufixed224x32', + 'ufixed232x8', + 'ufixed232x16', + 'ufixed232x24', + 'ufixed240x8', + 'ufixed240x16', + 'ufixed248x8', + 'event', + 'enum', + 'let', + 'mapping', + 'private', + 'public', + 'external', + 'view', + 'pure', + 'inherited', + 'payable', + 'true', + 'false', + 'var', + 'import', + 'constant', + 'if', + 'else', + 'for', + 'else', + 'for', + 'while', + 'do', + 'break', + 'continue', + 'throw', + 'returns', + 'return', + 'suicide', + 'new', + 'is', + 'this', + 'super' + ], + + operators: [ + '=', + '>', + '<', + '!', + '~', + '?', + ':', + '==', + '<=', + '>=', + '!=', + '&&', + '||', + '++', + '--', + '+', + '-', + '*', + '/', + '&', + '|', + '^', + '%', + '<<', + '>>', + '>>>', + '+=', + '-=', + '*=', + '/=', + '&=', + '|=', + '^=', + '%=', + '<<=', + '>>=', + '>>>=' + ], + + // we include these common regular expressions + symbols: /[=>](?!@symbols)/, '@brackets'], + [ + /@symbols/, + { + cases: { + '@operators': 'delimiter', + '@default': '' + } + } + ], + + // numbers + [/\d*\d+[eE]([\-+]?\d+)?(@floatsuffix)/, 'number.float'], + [/\d*\.\d+([eE][\-+]?\d+)?(@floatsuffix)/, 'number.float'], + [/0[xX][0-9a-fA-F']*[0-9a-fA-F](@integersuffix)/, 'number.hex'], + [/0[0-7']*[0-7](@integersuffix)/, 'number.octal'], + [/0[bB][0-1']*[0-1](@integersuffix)/, 'number.binary'], + [/\d[\d']*\d(@integersuffix)/, 'number'], + [/\d(@integersuffix)/, 'number'], + + // delimiter: after number because of .\d floats + [/[;,.]/, 'delimiter'], + + // strings + [/"([^"\\]|\\.)*$/, 'string.invalid'], // non-teminated string + [/"/, 'string', '@string'], + + // characters + [/'[^\\']'/, 'string'], + [/(')(@escapes)(')/, ['string', 'string.escape', 'string']], + [/'/, 'string.invalid'] + ], + + whitespace: [ + [/[ \t\r\n]+/, ''], + [/\/\*\*(?!\/)/, 'comment.doc', '@doccomment'], + [/\/\*/, 'comment', '@comment'], + [/\/\/.*$/, 'comment'] + ], + + comment: [ + [/[^\/*]+/, 'comment'], + [/\*\//, 'comment', '@pop'], + [/[\/*]/, 'comment'] + ], + // Identical copy of comment above, except for the addition of .doc + doccomment: [ + [/[^\/*]+/, 'comment.doc'], + [/\*\//, 'comment.doc', '@pop'], + [/[\/*]/, 'comment.doc'] + ], + + string: [ + [/[^\\"]+/, 'string'], + [/@escapes/, 'string.escape'], + [/\\./, 'string.escape.invalid'], + [/"/, 'string', '@pop'] + ] + } +} diff --git a/libs/remix-ui/tabs/.babelrc b/libs/remix-ui/tabs/.babelrc index cf7ddd99c6..adc03892c3 100644 --- a/libs/remix-ui/tabs/.babelrc +++ b/libs/remix-ui/tabs/.babelrc @@ -1,3 +1,4 @@ { - "presets": [["@nrwl/web/babel", { "useBuiltIns": "usage" }]] + "presets": [["@nrwl/web/babel", { "useBuiltIns": "usage" }]], + "plugins": ["@babel/plugin-proposal-class-properties"] } diff --git a/libs/remix-ui/tabs/react-tabs b/libs/remix-ui/tabs/react-tabs new file mode 160000 index 0000000000..5a1fb0dc4b --- /dev/null +++ b/libs/remix-ui/tabs/react-tabs @@ -0,0 +1 @@ +Subproject commit 5a1fb0dc4b79f811e63e2f029786ff36b68bcbf4 diff --git a/libs/remix-ui/tabs/src/lib/remix-ui-tabs.css b/libs/remix-ui/tabs/src/lib/remix-ui-tabs.css index ba745eed46..7df5e07993 100644 --- a/libs/remix-ui/tabs/src/lib/remix-ui-tabs.css +++ b/libs/remix-ui/tabs/src/lib/remix-ui-tabs.css @@ -1,6 +1,6 @@ .remix-ui-tabs { display: -webkit-box; - max-height: 42px + max-height: 2.15rem; } .remix-ui-tabs li { display: inline-block; @@ -28,12 +28,11 @@ } .close-tabs { visibility: hidden; - padding-top: 4px; font-size: medium; } .iconImage { - width: 16px; - height: 16px; + width: 1rem; + height: 1rem; } .active { border: 1px solid transparent; @@ -44,11 +43,6 @@ overflow-x: auto; overflow-y: hidden; white-space: nowrap; - max-width: 1000px; -} -.left-icon { - width: 70px; - height: 49px; } /* Hide scrollbar for Chrome, Safari and Opera */ diff --git a/libs/remix-ui/tabs/src/lib/remix-ui-tabs.tsx b/libs/remix-ui/tabs/src/lib/remix-ui-tabs.tsx index 5875cc24ab..276c691448 100644 --- a/libs/remix-ui/tabs/src/lib/remix-ui-tabs.tsx +++ b/libs/remix-ui/tabs/src/lib/remix-ui-tabs.tsx @@ -1,6 +1,5 @@ import React, { useState, useRef, useEffect, useReducer } from 'react' // eslint-disable-line -import { Tab, Tabs, TabList, TabPanel } from 'react-tabs' - +import { Tab, Tabs, TabList, TabPanel } from '../../react-tabs/src/index' import './remix-ui-tabs.css' /* eslint-disable-next-line */ @@ -33,12 +32,12 @@ export const TabsUI = (props: TabsUIProps) => { const renderTab = (tab, index) => { const classNameImg = 'my-1 mr-1 text-dark ' + tab.iconClass - const classNameTab = 'nav-item nav-link tab' + (index === currentIndexRef.current ? ' active' : '') + const classNameTab = 'nav-item nav-link d-flex justify-content-center align-items-center px-2 py-1 tab' + (index === currentIndexRef.current ? ' active' : '') return ( -
{ tabsRef.current[index] = el }} className={classNameTab} title={tab.tooltip}> +
{ props.onSelect(index); currentIndexRef.current = index; setSelectedIndex(index) }} ref={el => { tabsRef.current[index] = el }} className={classNameTab} title={tab.tooltip}> {tab.icon ? () : ()} {tab.title} - props.onClose(index)}> + { props.onClose(index); event.stopPropagation() }}>
@@ -62,16 +61,23 @@ export const TabsUI = (props: TabsUIProps) => { }, []) return ( -
-
- props.onZoomOut()}> - props.onZoomIn()}> - +
+
+
+ props.onZoomOut()}> + props.onZoomIn()}> +
+ + + {props.tabs.map((tab, i) => {renderTab(tab, i)})} + + {props.tabs.map((tab) => )} +
- { props.onSelect(index); currentIndexRef.current = index; setSelectedIndex(index) }} > - {props.tabs.map((tab, i) => {renderTab(tab, i)})} - {props.tabs.map((tab) => )} - +
) } diff --git a/libs/remix-ui/terminal/src/lib/components/Context.tsx b/libs/remix-ui/terminal/src/lib/components/Context.tsx index a354c4cab4..b51d9385e6 100644 --- a/libs/remix-ui/terminal/src/lib/components/Context.tsx +++ b/libs/remix-ui/terminal/src/lib/components/Context.tsx @@ -12,7 +12,7 @@ const Context = ({ opts, blockchain }) => { const val = data.value let hash = data.hash ? helper.shortenHexData(data.hash) : '' const input = data.input ? helper.shortenHexData(data.input) : '' - const logs = data.logs && data.logs.decoded && data.logs.decoded.length ? data.logs.decoded.length : 0 + const logs = opts.logs && opts.logs.decoded && opts.logs.decoded.length ? opts.logs.decoded.length : 0 const block = data.receipt ? data.receipt.blockNumber : data.blockNumber || '' const i = data.receipt ? data.transactionIndex : data.transactionIndex const value = val ? typeConversion.toInt(val) : 0 diff --git a/libs/remix-ui/terminal/src/lib/components/RenderKnownTransactions.tsx b/libs/remix-ui/terminal/src/lib/components/RenderKnownTransactions.tsx index 79e1ba4c8a..c8508a676c 100644 --- a/libs/remix-ui/terminal/src/lib/components/RenderKnownTransactions.tsx +++ b/libs/remix-ui/terminal/src/lib/components/RenderKnownTransactions.tsx @@ -21,7 +21,7 @@ const RenderKnownTransactions = ({ tx, receipt, resolvedData, logs, index, plugi const from = tx.from const to = resolvedData.contractName + '.' + resolvedData.fn const txType = 'knownTx' - const options = { from, to, tx } + const options = { from, to, tx, logs } return (
txDetails(event, tx)}> diff --git a/libs/remix-ui/terminal/src/lib/components/Table.tsx b/libs/remix-ui/terminal/src/lib/components/Table.tsx index 33e9cabf58..948b9b1e59 100644 --- a/libs/remix-ui/terminal/src/lib/components/Table.tsx +++ b/libs/remix-ui/terminal/src/lib/components/Table.tsx @@ -36,125 +36,219 @@ const showTable = (opts, showTableHash) => { } const val = opts.val != null ? typeConversion.toInt(opts.val) : 0 return ( - +
- - - - - {opts.hash ? ( - - - ) : null } - { - opts.contractAddress ? ( - - - - - ) : null - } - { - opts.from ? ( - - - - - ) : null - } - { - opts.to ? ( - - - - - ) : null - } - { - opts.gas ? ( - - - - - ) : null - } - { - opts.transactionCost ? ( - - - - - ) : null - } - { - opts.executionCost ? ( - - - - - ) : null - } + {opts.status !== undefined ? ( + + + + + ) : null} + {opts.hash && !opts.isCall ? ( + + + + + ) : null} + {opts.contractAddress ? ( + + + + + ) : null} + {opts.from ? ( + + + + + ) : null} + {opts.to ? ( + + + + + ) : null} + {opts.gas ? ( + + + + + ) : null} + {opts.transactionCost ? ( + + + + + ) : null} + {opts.executionCost ? ( + + + + + ) : null} {opts.hash ? ( - - - + + ) : null} {opts.input ? ( - - - + + ) : null} {opts['decoded input'] ? ( - - - + + ) : null} {opts['decoded output'] ? ( - - - + + ) : null} {opts.logs ? ( - - - + + ) : null} {opts.val ? ( - - - + + ) : null} diff --git a/libs/remix-ui/terminal/src/lib/remix-ui-terminal.tsx b/libs/remix-ui/terminal/src/lib/remix-ui-terminal.tsx index 8b729f07d4..3c13ed625c 100644 --- a/libs/remix-ui/terminal/src/lib/remix-ui-terminal.tsx +++ b/libs/remix-ui/terminal/src/lib/remix-ui-terminal.tsx @@ -24,7 +24,7 @@ export interface ClipboardEvent extends SyntheticEvent { } export const RemixUiTerminal = (props: RemixUiTerminalProps) => { - const { call, _deps, on, config, event, gistHandler, logHtmlResponse, logResponse, version } = props.plugin + const { call, _deps, on, config, event, gistHandler, version } = props.plugin const [toggleDownUp, setToggleDownUp] = useState('fa-angle-double-down') const [_cmdIndex, setCmdIndex] = useState(-1) const [_cmdTemp, setCmdTemp] = useState('') @@ -84,12 +84,15 @@ export const RemixUiTerminal = (props: RemixUiTerminalProps) => { } useEffect(() => { - scriptRunnerDispatch({ type: 'html', payload: { message: logHtmlResponse } }) - }, [logHtmlResponse]) - - useEffect(() => { - scriptRunnerDispatch({ type: 'log', payload: { message: logResponse } }) - }, [logResponse]) + props.onReady({ + logHtml: (html) => { + scriptRunnerDispatch({ type: 'html', payload: { message: [html.innerText] } }) + }, + log: (message) => { + scriptRunnerDispatch({ type: 'log', payload: { message: [message] } }) + } + }) + }, []) // events useEffect(() => { @@ -115,7 +118,7 @@ export const RemixUiTerminal = (props: RemixUiTerminalProps) => { useEffect(() => { scrollToBottom() - }, [newstate.journalBlocks.length, logHtmlResponse.length, toaster]) + }, [newstate.journalBlocks.length, toaster]) function execute (file, cb) { function _execute (content, cb) { diff --git a/libs/remix-ui/terminal/src/lib/types/terminalTypes.ts b/libs/remix-ui/terminal/src/lib/types/terminalTypes.ts index 66eea4d655..7a77b11dab 100644 --- a/libs/remix-ui/terminal/src/lib/types/terminalTypes.ts +++ b/libs/remix-ui/terminal/src/lib/types/terminalTypes.ts @@ -24,5 +24,6 @@ export const LISTEN_ON_NETWORK = 'listenOnNetWork' export const CMD_HISTORY = 'cmdHistory' export interface RemixUiTerminalProps { - plugin: any + plugin: any, + onReady: (api: any) => void } diff --git a/libs/remix-ui/workspace/src/lib/reducers/workspace.ts b/libs/remix-ui/workspace/src/lib/reducers/workspace.ts index b92d7b041e..152b283fa9 100644 --- a/libs/remix-ui/workspace/src/lib/reducers/workspace.ts +++ b/libs/remix-ui/workspace/src/lib/reducers/workspace.ts @@ -504,11 +504,11 @@ export const browserReducer = (state = browserInitialState, action: Action) => { ...state, browser: { ...state.browser, - contextMenu: state.mode === 'browser' ? addContextMenuItem(state, payload) : state.browser.contextMenu + contextMenu: addContextMenuItem(state, payload) }, localhost: { ...state.localhost, - contextMenu: state.mode === 'localhost' ? addContextMenuItem(state, payload) : state.localhost.contextMenu + contextMenu: addContextMenuItem(state, payload) } } } @@ -520,11 +520,11 @@ export const browserReducer = (state = browserInitialState, action: Action) => { ...state, browser: { ...state.browser, - contextMenu: state.mode === 'browser' ? removeContextMenuItem(state, payload) : state.browser.contextMenu + contextMenu: removeContextMenuItem(state, payload) }, localhost: { ...state.localhost, - contextMenu: state.mode === 'localhost' ? removeContextMenuItem(state, payload) : state.localhost.contextMenu + contextMenu: removeContextMenuItem(state, payload) } } } @@ -536,11 +536,11 @@ export const browserReducer = (state = browserInitialState, action: Action) => { ...state, browser: { ...state.browser, - expandPath: state.mode === 'browser' ? payload : state.browser.expandPath + expandPath: payload }, localhost: { ...state.localhost, - expandPath: state.mode === 'localhost' ? payload : state.localhost.expandPath + expandPath: payload } } } diff --git a/package.json b/package.json index 8faaf4c999..e3fdd751bb 100644 --- a/package.json +++ b/package.json @@ -136,6 +136,7 @@ ] }, "dependencies": { + "@babel/plugin-proposal-class-properties": "^7.16.0", "@erebos/bzz-node": "^0.13.0", "@ethereumjs/block": "^3.5.1", "@ethereumjs/common": "^2.5.0",
status{`${opts.status} ${msg}`}
transaction hash{opts.hash} - -
contract address{opts.contractAddress} - -
from{opts.from} - -
to{toHash} - -
gas{opts.gas} gas - -
transaction cost{opts.transactionCost} gas {callWarning} - -
execution cost{opts.executionCost} gas {callWarning} - -
+ status + {`${opts.status} ${msg}`}
+ transaction hash + + {opts.hash} + +
+ contract address + + {opts.contractAddress} + +
+ from + + {opts.from} + +
+ to + + {toHash} + +
+ gas + + {opts.gas} gas + +
+ transaction cost + + {opts.transactionCost} gas {callWarning} + +
+ execution cost + + {opts.executionCost} gas {callWarning} + +
hash{opts.hash} - +
+ hash + + {opts.hash} +
input{helper.shortenHexData(opts.input)} - +
+ input + + {helper.shortenHexData(opts.input)} +
decoded input{opts['decoded input'].trim()} - +
+ decoded input + + {opts['decoded input'].trim()} +
decoded output{opts['decoded output']} - +
+ decoded output + + {opts['decoded output']} +
logs +
+ logs + {JSON.stringify(stringified, null, '\t')} - - + +
val{val} wei - +
+ val + + {val} wei +