diff --git a/.github/workflows/pr-reminder.yml b/.github/workflows/pr-reminder.yml index 00e75b8384..233c3dce79 100644 --- a/.github/workflows/pr-reminder.yml +++ b/.github/workflows/pr-reminder.yml @@ -14,4 +14,4 @@ jobs: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: webhook-url: ${{ secrets.DISCORD_WEBHOOK_URL }} - freeze-date: '2024-05-20T18:00:00Z' + freeze-date: '2024-06-03T18:00:00Z' diff --git a/apps/remix-ide-e2e/src/tests/layout.test.ts b/apps/remix-ide-e2e/src/tests/layout.test.ts new file mode 100644 index 0000000000..23398171d8 --- /dev/null +++ b/apps/remix-ide-e2e/src/tests/layout.test.ts @@ -0,0 +1,79 @@ +'use strict' +import { NightwatchBrowser } from 'nightwatch' +import init from '../helpers/init' + +module.exports = { + before: function (browser: NightwatchBrowser, done: VoidFunction) { + init(browser, done) + }, + + '@sources': function () { + return sources + }, + + 'Should pin solidity compiler plugin to the right and switch focus for left side panel to the file-explorer': function (browser: NightwatchBrowser) { + browser.waitForElementVisible('[data-id="movePluginToRight"]') + .click('[data-id="movePluginToRight"]') + .waitForElementVisible('[data-id="movePluginToLeft"]') + .waitForElementVisible('.pinned-panel h6[data-id="sidePanelSwapitTitle"]') + .assert.containsText('.sidepanel h6[data-id="sidePanelSwapitTitle"]', 'FILE EXPLORER') + .assert.containsText('.pinned-panel h6[data-id="sidePanelSwapitTitle"]', 'SOLIDITY COMPILER') + }, + 'Should unpin and focus on solidity compiler in the left side panel': function (browser: NightwatchBrowser) { + browser.waitForElementVisible('[data-id="movePluginToLeft"]') + .click('[data-id="movePluginToLeft"]') + .waitForElementVisible('[data-id="movePluginToRight"]') + .assert.containsText('.sidepanel h6[data-id="sidePanelSwapitTitle"]', 'SOLIDITY COMPILER') + .waitForElementNotVisible('.pinned-panel h6[data-id="sidePanelSwapitTitle"]') + }, + 'Should pin a plugin while a another plugin is already pinned': function (browser: NightwatchBrowser) { + browser.waitForElementVisible('[data-id="movePluginToRight"]') + .click('[data-id="movePluginToRight"]') + .waitForElementVisible('[data-id="movePluginToLeft"]') + .waitForElementVisible('.pinned-panel h6[data-id="sidePanelSwapitTitle"]') + .assert.containsText('.pinned-panel h6[data-id="sidePanelSwapitTitle"]', 'SOLIDITY COMPILER') + .clickLaunchIcon('udapp') + .click('[data-id="movePluginToRight"]') + .waitForElementVisible('[data-id="movePluginToLeft"]') + .assert.containsText('.pinned-panel h6[data-id="sidePanelSwapitTitle"]', 'DEPLOY & RUN TRANSACTIONS') + .assert.containsText('.sidepanel h6[data-id="sidePanelSwapitTitle"]', 'SOLIDITY COMPILER') + }, + 'Should pin a pinned plugin to the right after reloading the page': function (browser: NightwatchBrowser) { + browser.refreshPage() + .waitForElementVisible('.pinned-panel h6[data-id="sidePanelSwapitTitle"]') + .assert.containsText('.pinned-panel h6[data-id="sidePanelSwapitTitle"]', 'DEPLOY & RUN TRANSACTIONS') + }, + 'Should maintain logged state of udapp plugin after pinning and unpinning': function (browser: NightwatchBrowser) { + browser.waitForElementVisible('*[data-id="treeViewLitreeViewItemcontracts"]') + .click('*[data-id="treeViewLitreeViewItemcontracts"]') + .openFile('contracts/1_Storage.sol') + .pause(5000) + .waitForElementPresent('*[data-id="Deploy - transact (not payable)"]') + .click('*[data-id="Deploy - transact (not payable)"]') + .waitForElementPresent('#instance0xd9145CCE52D386f254917e481eB44e9943F39138') + .clickInstance(0) + .clickFunction('store - transact (not payable)', { types: 'uint256 num', values: '10' }) + .clickFunction('retrieve - call') + .click('[data-id="movePluginToLeft"]') + .waitForElementVisible('[data-id="movePluginToRight"]') + .clickInstance(0) + .waitForElementContainsText('[data-id="treeViewLi0"]', 'uint256: 10') + }, + 'Should maintain logged state of search plugin after pinning and unpinning': function (browser: NightwatchBrowser) { + browser.clickLaunchIcon('search') + .waitForElementVisible('*[id="search_input"]') + .waitForElementVisible('*[id="search_include"]') + .setValue('*[id="search_include"]', ', *.*').pause(2000) + .setValue('*[id="search_input"]', 'read').sendKeys('*[id="search_input"]', browser.Keys.ENTER) + .pause(1000) + .waitForElementContainsText('*[data-id="search_results"]', '3_BALLOT.SOL', 60000) + .waitForElementContainsText('*[data-id="search_results"]', 'contracts', 60000) + .waitForElementContainsText('*[data-id="search_results"]', 'README.TXT', 60000) + .click('[data-id="movePluginToRight"]') + .waitForElementContainsText('*[data-id="search_results"]', '3_BALLOT.SOL') + .waitForElementContainsText('*[data-id="search_results"]', 'contracts') + .waitForElementContainsText('*[data-id="search_results"]', 'README.TXT') + } +} + +const sources = [] diff --git a/apps/remix-ide-e2e/src/tests/url.test.ts b/apps/remix-ide-e2e/src/tests/url.test.ts index 04b0cb9642..679fb72951 100644 --- a/apps/remix-ide-e2e/src/tests/url.test.ts +++ b/apps/remix-ide-e2e/src/tests/url.test.ts @@ -70,7 +70,7 @@ module.exports = { .getEditorValue((content) => { browser.assert.ok(content && content.indexOf( 'https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/access/Ownable.sol') !== -1, - 'code has not been loaded') + 'code has not been loaded') }) }, @@ -87,7 +87,7 @@ module.exports = { .getEditorValue((content) => { browser.assert.ok(content && content.indexOf( 'proposals.length = _numProposals;') !== -1, - 'url has not been loaded') + 'url has not been loaded') }) }, @@ -121,8 +121,8 @@ module.exports = { }) }, - - 'Should load Blockscout verified contracts from URL "address" and "blockscout" params (multiple sources)': function (browser: NightwatchBrowser) { + //Disabled due to failure from blockscout api + 'Should load Blockscout verified contracts from URL "address" and "blockscout" params (multiple sources)': '' + function (browser: NightwatchBrowser) { browser .url('http://127.0.0.1:8080/#address=0x7Fc66500c84A76Ad7e9c93437bFc5Ac33E2DDaE9&blockscout=eth.blockscout.com') .refreshPage() @@ -162,7 +162,7 @@ module.exports = { .getEditorValue((content) => { browser.assert.ok(content && content.indexOf( 'proposals.length = _numProposals;') !== -1, - 'code has been loaded') + 'code has been loaded') }) .url('http://127.0.0.1:8080') // refresh without loading the code sample .currentWorkspaceIs('default_workspace') diff --git a/apps/remix-ide/src/app.js b/apps/remix-ide/src/app.js index 03dd0f5082..5a2ddf2a47 100644 --- a/apps/remix-ide/src/app.js +++ b/apps/remix-ide/src/app.js @@ -7,8 +7,10 @@ import {LocaleModule} from './app/tabs/locale-module' import {NetworkModule} from './app/tabs/network-module' import {Web3ProviderModule} from './app/tabs/web3-provider' import {CompileAndRun} from './app/tabs/compile-and-run' +import {PluginStateLogger} from './app/tabs/state-logger' import {SidePanel} from './app/components/side-panel' import {HiddenPanel} from './app/components/hidden-panel' +import {PinnedPanel} from './app/components/pinned-panel' import {VerticalIcons} from './app/components/vertical-icons' import {LandingPage} from './app/ui/landing-page/landing-page' import {MainPanel} from './app/components/main-panel' @@ -297,7 +299,8 @@ class AppComponent { this.layout = new Layout() const permissionHandler = new PermissionHandlerPlugin() - + // ----------------- run script after each compilation results ----------- + const pluginStateLogger = new PluginStateLogger() this.engine.register([ permissionHandler, @@ -347,6 +350,7 @@ class AppComponent { solidityScript, templates, solcoder, + pluginStateLogger ]) //---- fs plugin @@ -378,13 +382,14 @@ class AppComponent { this.menuicons = new VerticalIcons() this.sidePanel = new SidePanel() this.hiddenPanel = new HiddenPanel() + this.pinnedPanel = new PinnedPanel() const pluginManagerComponent = new PluginManagerComponent(appManager, this.engine) const filePanel = new FilePanel(appManager) const landingPage = new LandingPage(appManager, this.menuicons, fileManager, filePanel, contentImport) this.settings = new SettingsTab(Registry.getInstance().get('config').api, editor, appManager) - this.engine.register([this.menuicons, landingPage, this.hiddenPanel, this.sidePanel, filePanel, pluginManagerComponent, this.settings]) + this.engine.register([this.menuicons, landingPage, this.hiddenPanel, this.sidePanel, filePanel, pluginManagerComponent, this.settings, this.pinnedPanel]) // CONTENT VIEWS & DEFAULT PLUGINS const openZeppelinProxy = new OpenZeppelinProxy(blockchain) @@ -462,10 +467,12 @@ class AppComponent { 'compilerArtefacts', 'network', 'web3Provider', - 'offsetToLineColumnConverter' + 'offsetToLineColumnConverter', + 'pluginStateLogger' ]) await this.appManager.activatePlugin(['mainPanel', 'menuicons', 'tabs']) await this.appManager.activatePlugin(['sidePanel']) // activating host plugin separately + await this.appManager.activatePlugin(['pinnedPanel']) await this.appManager.activatePlugin(['home']) await this.appManager.activatePlugin(['settings', 'config']) await this.appManager.activatePlugin([ @@ -560,6 +567,12 @@ class AppComponent { } } } + }).then(async () => { + const lastPinned = localStorage.getItem('pinnedPlugin') + + if (lastPinned) { + this.appManager.call('sidePanel', 'pinView', JSON.parse(lastPinned)) + } }) .catch(console.error) } @@ -568,6 +581,14 @@ class AppComponent { document.body.appendChild(loadedElement) }) + this.appManager.on('pinnedPanel', 'pinnedPlugin', (pluginProfile) => { + localStorage.setItem('pinnedPlugin', JSON.stringify(pluginProfile)) + }) + + this.appManager.on('pinnedPanel', 'unPinnedPlugin', () => { + localStorage.setItem('pinnedPlugin', '') + }) + // activate solidity plugin this.appManager.activatePlugin(['solidity', 'udapp', 'deploy-libraries', 'link-libraries', 'openzeppelin-proxy']) } diff --git a/apps/remix-ide/src/app/components/panel.ts b/apps/remix-ide/src/app/components/panel.ts index a90b8c3ee3..bf80f3614d 100644 --- a/apps/remix-ide/src/app/components/panel.ts +++ b/apps/remix-ide/src/app/components/panel.ts @@ -15,9 +15,11 @@ export class AbstractPanel extends HostPlugin { } currentFocus (): string { - return Object.values(this.plugins).find(plugin => { + const activePlugin = Object.values(this.plugins).find(plugin => { return plugin.active - }).profile.name + }) + + return activePlugin ? activePlugin.profile.name : null } addView (profile, view) { @@ -26,6 +28,7 @@ export class AbstractPanel extends HostPlugin { profile: profile, view: view, active: false, + pinned: false, class: 'plugItIn active' } } diff --git a/apps/remix-ide/src/app/components/pinned-panel.tsx b/apps/remix-ide/src/app/components/pinned-panel.tsx new file mode 100644 index 0000000000..03fc6e1050 --- /dev/null +++ b/apps/remix-ide/src/app/components/pinned-panel.tsx @@ -0,0 +1,84 @@ +// eslint-disable-next-line no-use-before-define +import React from 'react' +import { AbstractPanel } from './panel' +import { PluginRecord, RemixPluginPanel } from '@remix-ui/panel' +import packageJson from '../../../../../package.json' +import { RemixUIPanelHeader } from '@remix-ui/panel' +import { PluginViewWrapper } from '@remix-ui/helper' + +const pinnedPanel = { + name: 'pinnedPanel', + displayName: 'Pinned Panel', + description: 'Remix IDE pinned panel', + version: packageJson.version, + methods: ['addView', 'removeView', 'currentFocus', 'pinView', 'unPinView'] +} + +export class PinnedPanel extends AbstractPanel { + dispatch: React.Dispatch = () => {} + loggedState: any + + constructor() { + super(pinnedPanel) + } + + onActivation() { + this.renderComponent() + this.on('sidePanel', 'pluginDisabled', (name) => { + if (this.plugins[name] && this.plugins[name].active) { + this.emit('unPinnedPlugin', name) + this.events.emit('unPinnedPlugin', name) + super.remove(name) + } + }) + } + + async pinView (profile, view) { + const activePlugin = this.currentFocus() + + if (activePlugin === profile.name) throw new Error(`Plugin ${profile.name} already pinned`) + if (activePlugin) { + await this.call('sidePanel', 'unPinView', this.plugins[activePlugin].profile, this.plugins[activePlugin].view) + this.remove(activePlugin) + } + this.loggedState = await this.call('pluginStateLogger', 'getPluginState', profile.name) + this.addView(profile, view) + this.plugins[profile.name].pinned = true + this.plugins[profile.name].active = true + this.renderComponent() + this.events.emit('pinnedPlugin', profile) + this.emit('pinnedPlugin', profile) + } + + async unPinView (profile) { + const activePlugin = this.currentFocus() + + if (activePlugin !== profile.name) throw new Error(`Plugin ${profile.name} is not pinned`) + await this.call('sidePanel', 'unPinView', profile, this.plugins[profile.name].view) + super.remove(profile.name) + this.renderComponent() + this.events.emit('unPinnedPlugin', profile) + this.emit('unPinnedPlugin', profile) + } + + setDispatch (dispatch: React.Dispatch) { + this.dispatch = dispatch + } + + render() { + return ( +
+ ) + } + + updateComponent(state: any) { + return } plugins={state.plugins} pluginState={state.pluginState} /> + } + + renderComponent() { + this.dispatch({ + plugins: this.plugins, + pluginState: this.loggedState + }) + } +} diff --git a/apps/remix-ide/src/app/components/side-panel.tsx b/apps/remix-ide/src/app/components/side-panel.tsx index 8b30bf200b..3e62f8bea1 100644 --- a/apps/remix-ide/src/app/components/side-panel.tsx +++ b/apps/remix-ide/src/app/components/side-panel.tsx @@ -12,12 +12,14 @@ const sidePanel = { displayName: 'Side Panel', description: 'Remix IDE side panel', version: packageJson.version, - methods: ['addView', 'removeView', 'currentFocus'] + methods: ['addView', 'removeView', 'currentFocus', 'pinView', 'unPinView'] } export class SidePanel extends AbstractPanel { sideelement: any + loggedState: any dispatch: React.Dispatch = () => {} + constructor() { super(sidePanel) this.sideelement = document.createElement('section') @@ -56,10 +58,8 @@ export class SidePanel extends AbstractPanel { } removeView(profile) { - if (this.plugins[profile.name].active) this.call('menuicons', 'select', 'filePanel') + if (this.plugins[profile.name] && this.plugins[profile.name].active) this.call('menuicons', 'select', 'filePanel') super.removeView(profile) - this.emit('pluginDisabled', profile.name) - this.call('menuicons', 'unlinkContent', profile) this.renderComponent() } @@ -69,6 +69,26 @@ export class SidePanel extends AbstractPanel { this.renderComponent() } + async pinView (profile) { + await this.call('pinnedPanel', 'pinView', profile, this.plugins[profile.name].view) + if (this.plugins[profile.name].active) this.call('menuicons', 'select', 'filePanel') + super.remove(profile.name) + this.call('menuicons', 'unlinkContent', profile) + this.renderComponent() + } + + async unPinView (profile, view) { + const activePlugin = this.currentFocus() + + if (activePlugin === profile.name) throw new Error(`Plugin ${profile.name} already unpinned`) + this.loggedState = await this.call('pluginStateLogger', 'getPluginState', profile.name) + super.addView(profile, view) + this.plugins[activePlugin].active = false + this.plugins[profile.name].active = true + await this.call('menuicons', 'linkContent', profile) + this.showContent(profile.name) + } + /** * Display content and update the header * @param {String} name The name of the plugin to display @@ -93,12 +113,13 @@ export class SidePanel extends AbstractPanel { } updateComponent(state: any) { - return } plugins={state.plugins} /> + return } plugins={state.plugins} pluginState={state.pluginState} /> } renderComponent() { this.dispatch({ - plugins: this.plugins + plugins: this.plugins, + pluginState: this.loggedState }) } } diff --git a/apps/remix-ide/src/app/panels/layout.ts b/apps/remix-ide/src/app/panels/layout.ts index 1db8f29dfb..fa564370f4 100644 --- a/apps/remix-ide/src/app/panels/layout.ts +++ b/apps/remix-ide/src/app/panels/layout.ts @@ -6,7 +6,7 @@ import { QueryParams } from '@remix-project/remix-lib' const profile: Profile = { name: 'layout', description: 'layout', - methods: ['minimize', 'maximiseSidePanel', 'resetSidePanel', 'maximizeTerminal'] + methods: ['minimize', 'maximiseSidePanel', 'resetSidePanel', 'maximizeTerminal', 'maximisePinnedPanel', 'resetPinnedPanel'] } interface panelState { @@ -74,6 +74,16 @@ export class Layout extends Plugin { this.event.emit('resetsidepanel') } }) + + this.on('pinnedPanel', 'pinnedPlugin', async (name) => { + const current = await this.call('pinnedPanel', 'currentFocus') + if (this.maximised[current]) { + this.event.emit('maximisepinnedpanel') + } else { + this.event.emit('resetpinnedpanel') + } + }) + document.addEventListener('keypress', e => { if (e.shiftKey && e.ctrlKey) { if (e.code === 'KeyF') { @@ -110,6 +120,12 @@ export class Layout extends Plugin { this.maximised[current] = true } + async maximisePinnedPanel () { + this.event.emit('maximisepinnedpanel') + const current = await this.call('pinnedPanel', 'currentFocus') + this.maximised[current] = true + } + async maximizeTerminal() { this.panels.terminal.minimized = false this.event.emit('change', this.panels) @@ -121,4 +137,10 @@ export class Layout extends Plugin { const current = await this.call('sidePanel', 'currentFocus') this.maximised[current] = false } + + async resetPinnedPanel () { + this.event.emit('resetpinnedpanel') + const current = await this.call('pinnedPanel', 'currentFocus') + this.maximised[current] = false + } } diff --git a/apps/remix-ide/src/app/plugins/contractFlattener.tsx b/apps/remix-ide/src/app/plugins/contractFlattener.tsx index f8205351c6..46176cc07c 100644 --- a/apps/remix-ide/src/app/plugins/contractFlattener.tsx +++ b/apps/remix-ide/src/app/plugins/contractFlattener.tsx @@ -1,3 +1,4 @@ +/* eslint-disable prefer-const */ import React from 'react' import { Plugin } from '@remixproject/engine' import { customAction } from '@remixproject/plugin-api' @@ -57,11 +58,12 @@ export class ContractFlattener extends Plugin { let sorted let result let sources + let order: string[] = [] try { - dependencyGraph = getDependencyGraph(ast, filePath, input.settings.remappings) + dependencyGraph = getDependencyGraph(ast, filePath, input.settings.remappings, order) sorted = dependencyGraph.isEmpty() ? [filePath] : dependencyGraph.sort().reverse() sources = source.sources - result = concatSourceFiles(sorted, sources) + result = concatSourceFiles(sorted, sources, order) } catch (err) { console.warn(err) } diff --git a/apps/remix-ide/src/app/tabs/locales/en/panel.json b/apps/remix-ide/src/app/tabs/locales/en/panel.json index 013a68e199..f9a9b2ffd1 100644 --- a/apps/remix-ide/src/app/tabs/locales/en/panel.json +++ b/apps/remix-ide/src/app/tabs/locales/en/panel.json @@ -4,6 +4,8 @@ "panel.documentation": "Documentation", "panel.description": "Description", "panel.maintainedByRemix": "Maintained by Remix", + "panel.pinnedMsg": "Click to move plugin to the right side panel", + "panel.unPinnedMsg": "Click to return plugin to the left side panel", "panel.maintainedExternally": "Not maintained by Remix", "panel.pluginInfo": "Plugin info", "panel.linkToDoc": "Link to documentation", diff --git a/apps/remix-ide/src/app/tabs/locales/en/udapp.json b/apps/remix-ide/src/app/tabs/locales/en/udapp.json index 2f0ba01f02..bfab9f9759 100644 --- a/apps/remix-ide/src/app/tabs/locales/en/udapp.json +++ b/apps/remix-ide/src/app/tabs/locales/en/udapp.json @@ -123,6 +123,7 @@ "udapp.llIError6": "Both 'receive' and 'fallback' functions are not defined", "udapp.llIError7": "Please define a 'Fallback' function to send calldata and a either 'Receive' or payable 'Fallback' to send ethers", "udapp.copy": "Copy", + "udapp.copyAddress": "Copy Address", "udapp._comment_mainnet.tsx": "libs/remix-ui/run-tab/src/lib/components/mainnet.tsx", "udapp.mainnetText1": "You are about to create a transaction on {name} Network. Confirm the details to send the info to your provider.", diff --git a/apps/remix-ide/src/app/tabs/locales/es/panel.json b/apps/remix-ide/src/app/tabs/locales/es/panel.json deleted file mode 100644 index 44aab809a3..0000000000 --- a/apps/remix-ide/src/app/tabs/locales/es/panel.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "panel.author": "Autor", - "panel.maintainedBy": "Mantenido por", - "panel.documentation": "Documentación", - "panel.description": "Descripción", - "panel.maintainedByRemix": "Mantenido por Remix", - "panel.pluginInfo": "Información del Complemento", - "panel.linkToDoc": "Enlace a la documentación", - "panel.makeAnissue": "Crear un asunto" -} diff --git a/apps/remix-ide/src/app/tabs/locales/es/udapp.json b/apps/remix-ide/src/app/tabs/locales/es/udapp.json index 5ed17d12f9..84dcdf5f5c 100644 --- a/apps/remix-ide/src/app/tabs/locales/es/udapp.json +++ b/apps/remix-ide/src/app/tabs/locales/es/udapp.json @@ -102,6 +102,7 @@ "udapp.llIError6": "Las funciones 'receive' y 'fallback' no están definidas", "udapp.llIError7": "Por favor defina una función 'Fallback' para enviar calldata y un 'Receice' o payable 'Fallback' para enviar ethers", "udapp.copy": "Copiar", + "udapp.copyAddress": "Copy Address", "udapp._comment_mainnet.tsx": "libs/remix-ui/run-tab/src/lib/components/mainnet.tsx", "udapp.mainnetText1": "Estás a punto de crear una transacción en la Red {name}. Confirma los detalles para enviar la información a tu proveedor.", "udapp.mainnetText2": "El proveedor para muchos usuarios es MetaMask. El proveedor le pedirá que firme la transacción antes de enviarla a la Red {name}.", diff --git a/apps/remix-ide/src/app/tabs/locales/fr/panel.json b/apps/remix-ide/src/app/tabs/locales/fr/panel.json deleted file mode 100644 index 8deab3169a..0000000000 --- a/apps/remix-ide/src/app/tabs/locales/fr/panel.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "panel.author": "Auteur", - "panel.maintainedBy": "Maintenu par :", - "panel.documentation": "Documentation", - "panel.description": "Description", - "panel.maintainedByRemix": "Maintenu par Remix", - "panel.pluginInfo": "Informations sur l'extension", - "panel.linkToDoc": "Lien vers la documentation", - "panel.makeAnissue": "Faire un ticket" -} diff --git a/apps/remix-ide/src/app/tabs/locales/fr/udapp.json b/apps/remix-ide/src/app/tabs/locales/fr/udapp.json index 80b22f2cc3..337d856791 100644 --- a/apps/remix-ide/src/app/tabs/locales/fr/udapp.json +++ b/apps/remix-ide/src/app/tabs/locales/fr/udapp.json @@ -102,6 +102,7 @@ "udapp.llIError6": "Les fonctions 'receive' et 'fallback' ne sont pas définies", "udapp.llIError7": "Définissez s'il vous plaît une fonction 'Fallback' pour envoyer des calldata et soit une fonction 'Receive' ou une fonction 'Fallback' payable pour envoyer des ethers", "udapp.copy": "Copier", + "udapp.copyAddress": "Copy Address", "udapp._comment_mainnet.tsx": "libs/remix-ui/run-tab/src/lib/components/mainnet.tsx", "udapp.mainnetText1": "Vous êtes sur le point de créer une transaction sur le réseau {name} . Confirmez les détails pour envoyer les informations à votre fournisseur.", "udapp.mainnetText2": "Le provider pour de nombreux utilisateurs est MetaMask. Le provider vous demandera de signer la transaction avant qu'elle ne soit envoyée au réseau {name}.", diff --git a/apps/remix-ide/src/app/tabs/locales/it/panel.json b/apps/remix-ide/src/app/tabs/locales/it/panel.json deleted file mode 100644 index 400b39508e..0000000000 --- a/apps/remix-ide/src/app/tabs/locales/it/panel.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "panel.author": "Autore", - "panel.maintainedBy": "Mantenuto Da", - "panel.documentation": "Documentazione", - "panel.description": "Descrizione", - "panel.maintainedByRemix": "Mantenuto da Remix", - "panel.pluginInfo": "Informazioni sul plugin", - "panel.linkToDoc": "Link alla documentazione", - "panel.makeAnissue": "Crea una Issue" -} diff --git a/apps/remix-ide/src/app/tabs/locales/it/udapp.json b/apps/remix-ide/src/app/tabs/locales/it/udapp.json index bb00090309..06c650b01a 100644 --- a/apps/remix-ide/src/app/tabs/locales/it/udapp.json +++ b/apps/remix-ide/src/app/tabs/locales/it/udapp.json @@ -102,6 +102,7 @@ "udapp.llIError6": "Le funzioni 'receive' e 'fallback' non sono definite", "udapp.llIError7": "Definisci una funzione 'Fallback' per inviare Calldata e una 'Receive' o 'Fallback' payable per inviare ETH", "udapp.copy": "Copia", + "udapp.copyAddress": "Copy Address", "udapp._comment_mainnet.tsx": "libs/remix-ui/run-tab/src/lib/components/mainnet.tsx", "udapp.mainnetText1": "Stai per creare una transazione su {name} Network. Conferma i dettagli per inviare le informazioni al tuo provider.", "udapp.mainnetText2": "Il provider per molti utenti è MetaMask. Il provider ti chiederà di firmare la transazione prima che venga inviata alla rete {name}.", diff --git a/apps/remix-ide/src/app/tabs/locales/ru/panel.json b/apps/remix-ide/src/app/tabs/locales/ru/panel.json deleted file mode 100644 index 066069d5bd..0000000000 --- a/apps/remix-ide/src/app/tabs/locales/ru/panel.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "panel.author": "Автор", - "panel.maintainedBy": "Поддерживается", - "panel.documentation": "Документация", - "panel.description": "Описание", - "panel.maintainedByRemix": "Поддерживается Remix", - "panel.pluginInfo": "Информация о плагине", - "panel.linkToDoc": "Ссылка на документацию", - "panel.makeAnissue": "Создать задачу" -} diff --git a/apps/remix-ide/src/app/tabs/locales/ru/udapp.json b/apps/remix-ide/src/app/tabs/locales/ru/udapp.json index 4e69c9e02a..32f5de3cb2 100644 --- a/apps/remix-ide/src/app/tabs/locales/ru/udapp.json +++ b/apps/remix-ide/src/app/tabs/locales/ru/udapp.json @@ -103,6 +103,7 @@ "udapp.llIError6": "Обе функции 'receive' и 'fallback' не определены", "udapp.llIError7": "Пожалуйста, определите функцию 'Fallback' для отправки вызываемых данных или 'Receive' или оплачиваемую функцию 'Fallback' для отправки эфира", "udapp.copy": "Копировать", + "udapp.copyAddress": "Copy Address", "udapp._comment_mainnet.tsx": "libs/remix-ui/run-tab/src/lib/components/mainnet.tsx", "udapp.mainnetText1": "Вы собираетесь создать транзакцию в сети {name}. Подтвердите данные для отправки информации вашему провайдеру.", "udapp.mainnetText2": "Провайдером для многих пользователей является MetaMask. Провайдер попросит вас подписать транзакцию перед отправкой ее в сеть {name}.", diff --git a/apps/remix-ide/src/app/tabs/locales/zh/panel.json b/apps/remix-ide/src/app/tabs/locales/zh/panel.json deleted file mode 100644 index e1a63ff928..0000000000 --- a/apps/remix-ide/src/app/tabs/locales/zh/panel.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "panel.author": "作者", - "panel.maintainedBy": "维护者", - "panel.documentation": "文档", - "panel.description": "描述", - "panel.maintainedByRemix": "由 Remix 维护", - "panel.pluginInfo": "插件信息", - "panel.linkToDoc": "文档链接", - "panel.makeAnissue": "提交 issue" -} diff --git a/apps/remix-ide/src/app/tabs/locales/zh/udapp.json b/apps/remix-ide/src/app/tabs/locales/zh/udapp.json index 3e7b4e3ba8..e504a1d9a1 100644 --- a/apps/remix-ide/src/app/tabs/locales/zh/udapp.json +++ b/apps/remix-ide/src/app/tabs/locales/zh/udapp.json @@ -102,6 +102,7 @@ "udapp.llIError6": "'receive' 和 'fallback' 函数都未定义", "udapp.llIError7": "定义一个 'Fallback' 函数来发送 calldata ,并且定义一个 'Receive' 或 可支付的 'Fallback' 来发送以太币", "udapp.copy": "复制", + "udapp.copyAddress": "Copy Address", "udapp._comment_mainnet.tsx": "libs/remix-ui/run-tab/src/lib/components/mainnet.tsx", "udapp.mainnetText1": "您即将在 {name} 网络上创建一笔交易。确认详细信息,将信息发送给您的 provider 。", "udapp.mainnetText2": "许多用户的 provider 是 MetaMask。在将交易发送到 {name} 网络之前,provider 会要求您签署交易。", diff --git a/apps/remix-ide/src/app/tabs/state-logger.js b/apps/remix-ide/src/app/tabs/state-logger.js new file mode 100644 index 0000000000..fa5d371dfb --- /dev/null +++ b/apps/remix-ide/src/app/tabs/state-logger.js @@ -0,0 +1,26 @@ +import { Plugin } from "@remixproject/engine" +import { EventEmitter } from 'events' +import * as packageJson from '../../../../../package.json' + +const profile = { + name: 'pluginStateLogger', + events: [], + methods: ['logPluginState', 'getPluginState'], + version: packageJson.version, +} + +export class PluginStateLogger extends Plugin { + constructor() { + super(profile) + this.events = new EventEmitter() + this.stateLogs = {} + } + + logPluginState(name, state) { + this.stateLogs[name] = state + } + + getPluginState(name) { + return this.stateLogs[name] + } +} \ No newline at end of file diff --git a/apps/remix-ide/src/assets/fontawesome/css/all.css b/apps/remix-ide/src/assets/fontawesome/css/all.css index 7513e4fc29..59c32c20ed 100644 --- a/apps/remix-ide/src/assets/fontawesome/css/all.css +++ b/apps/remix-ide/src/assets/fontawesome/css/all.css @@ -19391,6 +19391,8 @@ readers do not read off random characters that represent icons */ .fak.fa-cairo::before, .fa-kit.fa-cairo::before { content: "\e000"; } .fak.fa-circom::before, .fa-kit.fa-circom::before { content: "\e001"; } +.fak.fa-fa-dock-l::before, .fa-kit.fa-fa-dock-l::before { content: "\e007"; } +.fak.fa-fa-dock-r::before, .fa-kit.fa-fa-dock-r::before { content: "\e006"; } .fak.fa-lexon::before, .fa-kit.fa-lexon::before { content: "\e004"; } .fak.fa-solidity-mono::before, .fa-kit.fa-solidity-mono::before { content: "\e005"; } .fak.fa-ts-logo::before, .fa-kit.fa-ts-logo::before { content: "\e003"; } diff --git a/apps/remix-ide/src/assets/fontawesome/webfonts/custom-icons.ttf b/apps/remix-ide/src/assets/fontawesome/webfonts/custom-icons.ttf index 1be7447613..cd8888a51e 100644 Binary files a/apps/remix-ide/src/assets/fontawesome/webfonts/custom-icons.ttf and b/apps/remix-ide/src/assets/fontawesome/webfonts/custom-icons.ttf differ diff --git a/apps/remix-ide/src/assets/fontawesome/webfonts/custom-icons.woff2 b/apps/remix-ide/src/assets/fontawesome/webfonts/custom-icons.woff2 index 70babe6b97..d107b4d1c3 100644 Binary files a/apps/remix-ide/src/assets/fontawesome/webfonts/custom-icons.woff2 and b/apps/remix-ide/src/assets/fontawesome/webfonts/custom-icons.woff2 differ diff --git a/apps/remix-ide/src/remixAppManager.js b/apps/remix-ide/src/remixAppManager.js index 6d9f2ac36f..c52b50c081 100644 --- a/apps/remix-ide/src/remixAppManager.js +++ b/apps/remix-ide/src/remixAppManager.js @@ -76,7 +76,9 @@ let requiredModules = [ // services + layout views + system views 'doc-viewer', 'doc-gen', 'remix-templates', - 'solhint' + 'solhint', + 'pinnedPanel', + 'pluginStateLogger' ] diff --git a/libs/ghaction-helper/package.json b/libs/ghaction-helper/package.json index 6f14e60078..f72dc335a0 100644 --- a/libs/ghaction-helper/package.json +++ b/libs/ghaction-helper/package.json @@ -1,6 +1,6 @@ { "name": "@remix-project/ghaction-helper", - "version": "0.1.28", + "version": "0.1.29", "description": "Solidity Tests GitHub Action Helper", "main": "src/index.js", "scripts": { @@ -19,17 +19,17 @@ }, "homepage": "https://github.com/ethereum/remix-project#readme", "devDependencies": { - "@remix-project/remix-solidity": "^0.5.34", + "@remix-project/remix-solidity": "^0.5.35", "@types/chai": "^4.3.4", "typescript": "^4.9.3" }, "dependencies": { "@ethereum-waffle/chai": "^3.4.4", - "@remix-project/remix-simulator": "^0.2.48", + "@remix-project/remix-simulator": "^0.2.49", "chai": "^4.3.7", "ethers": "^5.7.2", "web3": "^4.1.1" }, "types": "./src/index.d.ts", - "gitHead": "4f5c2cb88bc38099f71c5407265a23ab3bb7508e" + "gitHead": "9a971b0c22009d7405ca158b0847faf0d91ce9a7" } \ No newline at end of file diff --git a/libs/remix-analyzer/package.json b/libs/remix-analyzer/package.json index 7fa6b4ae1d..48054eba4e 100644 --- a/libs/remix-analyzer/package.json +++ b/libs/remix-analyzer/package.json @@ -1,6 +1,6 @@ { "name": "@remix-project/remix-analyzer", - "version": "0.5.57", + "version": "0.5.58", "description": "Tool to perform static analysis on Solidity smart contracts", "scripts": { "test": "./../../node_modules/.bin/ts-node --project ../../tsconfig.base.json --require tsconfig-paths/register ./../../node_modules/.bin/tape ./test/tests.ts" @@ -25,8 +25,8 @@ "@ethereumjs/tx": "5.3.0", "@ethereumjs/util": "9.0.3", "@ethereumjs/vm": "8.0.0", - "@remix-project/remix-astwalker": "^0.0.78", - "@remix-project/remix-lib": "^0.5.55", + "@remix-project/remix-astwalker": "^0.0.79", + "@remix-project/remix-lib": "^0.5.56", "async": "^2.6.2", "ethers": "^5.4.2", "ethjs-util": "^0.1.6", @@ -50,6 +50,6 @@ "typescript": "^3.7.5" }, "typings": "src/index.d.ts", - "gitHead": "4f5c2cb88bc38099f71c5407265a23ab3bb7508e", + "gitHead": "9a971b0c22009d7405ca158b0847faf0d91ce9a7", "main": "./src/index.js" } \ No newline at end of file diff --git a/libs/remix-astwalker/package.json b/libs/remix-astwalker/package.json index a63c6113f6..73b7b3e761 100644 --- a/libs/remix-astwalker/package.json +++ b/libs/remix-astwalker/package.json @@ -1,6 +1,6 @@ { "name": "@remix-project/remix-astwalker", - "version": "0.0.78", + "version": "0.0.79", "description": "Tool to walk through Solidity AST", "main": "src/index.js", "scripts": { @@ -37,7 +37,7 @@ "@ethereumjs/tx": "5.3.0", "@ethereumjs/util": "9.0.3", "@ethereumjs/vm": "8.0.0", - "@remix-project/remix-lib": "^0.5.55", + "@remix-project/remix-lib": "^0.5.56", "@types/tape": "^4.2.33", "async": "^2.6.2", "ethers": "^5.4.2", @@ -53,6 +53,6 @@ "tap-spec": "^5.0.0" }, "typings": "src/index.d.ts", - "gitHead": "4f5c2cb88bc38099f71c5407265a23ab3bb7508e", + "gitHead": "9a971b0c22009d7405ca158b0847faf0d91ce9a7", "types": "./src/index.d.ts" } \ No newline at end of file diff --git a/libs/remix-debug/package.json b/libs/remix-debug/package.json index 6cfd28d530..27decde0bb 100644 --- a/libs/remix-debug/package.json +++ b/libs/remix-debug/package.json @@ -1,6 +1,6 @@ { "name": "@remix-project/remix-debug", - "version": "0.5.48", + "version": "0.5.49", "description": "Tool to debug Ethereum transactions", "contributors": [ { @@ -26,10 +26,10 @@ "@ethereumjs/tx": "5.3.0", "@ethereumjs/util": "9.0.3", "@ethereumjs/vm": "8.0.0", - "@remix-project/remix-astwalker": "^0.0.78", - "@remix-project/remix-lib": "^0.5.55", - "@remix-project/remix-simulator": "^0.2.48", - "@remix-project/remix-solidity": "^0.5.34", + "@remix-project/remix-astwalker": "^0.0.79", + "@remix-project/remix-lib": "^0.5.56", + "@remix-project/remix-simulator": "^0.2.49", + "@remix-project/remix-solidity": "^0.5.35", "ansi-gray": "^0.1.1", "async": "^2.6.2", "color-support": "^1.1.3", @@ -69,6 +69,6 @@ }, "homepage": "https://github.com/ethereum/remix-project/tree/master/libs/remix-debug#readme", "typings": "src/index.d.ts", - "gitHead": "4f5c2cb88bc38099f71c5407265a23ab3bb7508e", + "gitHead": "9a971b0c22009d7405ca158b0847faf0d91ce9a7", "types": "./src/index.d.ts" } \ No newline at end of file diff --git a/libs/remix-lib/package.json b/libs/remix-lib/package.json index 79af78a96b..d3a97f6ea1 100644 --- a/libs/remix-lib/package.json +++ b/libs/remix-lib/package.json @@ -1,6 +1,6 @@ { "name": "@remix-project/remix-lib", - "version": "0.5.55", + "version": "0.5.56", "description": "Library to various Remix tools", "contributors": [ { @@ -55,6 +55,6 @@ }, "homepage": "https://github.com/ethereum/remix-project/tree/master/libs/remix-lib#readme", "typings": "src/index.d.ts", - "gitHead": "4f5c2cb88bc38099f71c5407265a23ab3bb7508e", + "gitHead": "9a971b0c22009d7405ca158b0847faf0d91ce9a7", "types": "./src/index.d.ts" } \ No newline at end of file diff --git a/libs/remix-simulator/bin/ethsim b/libs/remix-simulator/bin/ethsim index 814e2a6a46..dd7a9c6538 100755 --- a/libs/remix-simulator/bin/ethsim +++ b/libs/remix-simulator/bin/ethsim @@ -23,22 +23,37 @@ program .command('start') .option('-p, --port [port]', 'specify port', 8545) .option('-b, --ip [host]', 'specify host', '127.0.0.1') - .option('-c, --coinbase [coinbase]', 'specify coinbase', '0x0000000000000000000000000000000000000000') .option('--rpc', 'run rpc server only', true) .option('--details', 'display payloads for every requests and their responses', false) - .action((option) => { - console.log('coinbase: ', option.coinbase) + .option('-c, --coinbase [coinbase]', 'specify coinbase', '0x0000000000000000000000000000000000000000') + .option('-f, --fork [fork]', 'specify fork name') + .option('-n, --nodeUrl [nodeUrl]', 'specify node url') + .option('-bn, --blockNumber [blockNumber]', 'specify block Number') + .option('-s, --stateDb [stateDb]', 'specify state database') + .option('-bs, --blocks [blocks]', 'specify blocks') + .action((option, env) => { + env.outputHelp() + console.log('\n') + console.log('Usage:') + console.log('remix-simulator start') + console.log('remix-simulator start -n -f cancun -bn latest') + console.log('\n') + console.log('Command line options:') + console.log('port: ', option.port) + console.log('host: ', option.ip) console.log('rpc: ', option.rpc) console.log('details: ', option.details) - console.log('host: ', option.ip) - console.log('port: ', option.port) - const Server = require('../src/server') - const server = new Server({ - coinbase: option.coinbase, - rpc: option.rpc, - logDetails: option.details - }) - server.start(option.ip, option.port) + console.log('\n') + console.log('Provider options:') + console.log('coinbase: ', option.coinbase) + console.log('fork: ', option.fork) + console.log('nodeUrl: ', option.nodeUrl) + console.log('blockNumber: ', option.blockNumber) + console.log('stateDb: ', option.stateDb) + console.log('blocks: ', option.blocks) + const { Server } = require('../src/server') + const server = new Server(option) + server.start(option) }) program.parse(process.argv) diff --git a/libs/remix-simulator/package.json b/libs/remix-simulator/package.json index fbc6655efa..03e60ee57a 100644 --- a/libs/remix-simulator/package.json +++ b/libs/remix-simulator/package.json @@ -1,6 +1,6 @@ { "name": "@remix-project/remix-simulator", - "version": "0.2.48", + "version": "0.2.49", "description": "Ethereum IDE and tools for the web", "contributors": [ { @@ -22,7 +22,7 @@ "@ethereumjs/tx": "5.3.0", "@ethereumjs/util": "9.0.3", "@ethereumjs/vm": "8.0.0", - "@remix-project/remix-lib": "^0.5.55", + "@remix-project/remix-lib": "^0.5.56", "ansi-gray": "^0.1.1", "async": "^3.1.0", "body-parser": "^1.18.2", @@ -70,6 +70,6 @@ }, "homepage": "https://github.com/ethereum/remix-project/tree/master/libs/remix-simulator#readme", "typings": "src/index.d.ts", - "gitHead": "4f5c2cb88bc38099f71c5407265a23ab3bb7508e", + "gitHead": "9a971b0c22009d7405ca158b0847faf0d91ce9a7", "types": "./src/index.d.ts" } \ No newline at end of file diff --git a/libs/remix-simulator/src/index.ts b/libs/remix-simulator/src/index.ts index e49f4baf86..35835a8ee2 100644 --- a/libs/remix-simulator/src/index.ts +++ b/libs/remix-simulator/src/index.ts @@ -1 +1,2 @@ export { Provider, extend, JSONRPCRequestPayload, JSONRPCResponsePayload, JSONRPCResponseCallback } from './provider' +export { Server } from './server' diff --git a/libs/remix-simulator/src/provider.ts b/libs/remix-simulator/src/provider.ts index 39b1cd1c5f..27918a4359 100644 --- a/libs/remix-simulator/src/provider.ts +++ b/libs/remix-simulator/src/provider.ts @@ -35,7 +35,7 @@ export type ProviderOptions = { nodeUrl?: string, blockNumber?: number | 'latest', stateDb?: State, - logDetails?: boolean + details?: boolean blocks?: string[], coinbase?: string } @@ -90,12 +90,12 @@ export class Provider { return } const method = this.methods[payload.method] - if (this.options.logDetails) { + if (this.options.details) { info(payload) } if (method) { return method.call(method, payload, (err, result) => { - if (this.options.logDetails) { + if (this.options.details) { info(err) info(result) } diff --git a/libs/remix-simulator/src/server.ts b/libs/remix-simulator/src/server.ts index 15636cac5a..0f285609d1 100644 --- a/libs/remix-simulator/src/server.ts +++ b/libs/remix-simulator/src/server.ts @@ -1,26 +1,32 @@ -import express from 'express' import cors from 'cors' import bodyParser from 'body-parser' -import expressWs from 'express-ws' -import { Provider } from './provider' -import { log } from './utils/logs' -const app = express() +import { Provider, ProviderOptions } from './provider' +import { log, error } from './utils/logs' -class Server { +export type CliOptions = { + rpc?: boolean, + port: number + ip: string +} + +export class Server { provider - rpcOnly - constructor (options) { + constructor (options?: ProviderOptions) { this.provider = new Provider(options) this.provider.init().then(() => { log('Provider initiated') + log('Test accounts:') + log(Object.keys(this.provider.Accounts.accounts)) }).catch((error) => { log(error) }) - this.rpcOnly = options.rpc } - start (host, port) { + async start (cliOptions: CliOptions) { + const expressWs = (await import('express-ws')).default + const express = (await import('express')).default + const app = express() const wsApp = expressWs(app) app.use(cors()) @@ -31,22 +37,39 @@ class Server { res.send('Welcome to remix-simulator') }) - if (this.rpcOnly) { + if (cliOptions.rpc) { app.use((req, res) => { + if (req && req.body && (req.body.method === 'eth_sendTransaction' || req.body.method === 'eth_call')) { + log('Receiving call/transaction:') + log(req.body.params) + } this.provider.sendAsync(req.body, (err, jsonResponse) => { if (err) { + error(err) return res.send(JSON.stringify({ error: err })) } + if (req && req.body && (req.body.method === 'eth_sendTransaction' || req.body.method === 'eth_call')) { + log(jsonResponse) + } res.send(jsonResponse) }) }) } else { wsApp.app.ws('/', (ws, req) => { ws.on('message', (msg) => { - this.provider.sendAsync(JSON.parse(msg.toString()), (err, jsonResponse) => { + const body = JSON.parse(msg.toString()) + if (body && (body.method === 'eth_sendTransaction' || body.method === 'eth_call')) { + log('Receiving call/transaction:') + log(body.params) + } + this.provider.sendAsync(body, (err, jsonResponse) => { if (err) { + error(err) return ws.send(JSON.stringify({ error: err })) } + if (body && (body.method === 'eth_sendTransaction' || body.method === 'eth_call')) { + log(jsonResponse) + } ws.send(JSON.stringify(jsonResponse)) }) }) @@ -57,13 +80,14 @@ class Server { }) } - app.listen(port, host, () => { - log('Remix Simulator listening on ws://' + host + ':' + port) - if (!this.rpcOnly) { + app.listen(cliOptions.port, cliOptions.ip, () => { + if (!cliOptions.rpc) { + log('Remix Simulator listening on ws://' + cliOptions.ip + ':' + cliOptions.port) log('http json-rpc is deprecated and disabled by default. To enable it use --rpc') + } else { + log('Remix Simulator listening on http://' + cliOptions.ip + ':' + cliOptions.port) } }) } } -module.exports = Server diff --git a/libs/remix-solidity/package.json b/libs/remix-solidity/package.json index 0177ed3072..a8e2d61ca2 100644 --- a/libs/remix-solidity/package.json +++ b/libs/remix-solidity/package.json @@ -1,6 +1,6 @@ { "name": "@remix-project/remix-solidity", - "version": "0.5.34", + "version": "0.5.35", "description": "Tool to load and run Solidity compiler", "main": "src/index.js", "types": "src/index.d.ts", @@ -19,7 +19,7 @@ "@ethereumjs/tx": "5.3.0", "@ethereumjs/util": "9.0.3", "@ethereumjs/vm": "8.0.0", - "@remix-project/remix-lib": "^0.5.55", + "@remix-project/remix-lib": "^0.5.56", "async": "^2.6.2", "eslint-scope": "^5.0.0", "ethers": "^5.4.2", @@ -57,5 +57,5 @@ }, "homepage": "https://github.com/ethereum/remix-project/tree/master/libs/remix-solidity#readme", "typings": "src/index.d.ts", - "gitHead": "4f5c2cb88bc38099f71c5407265a23ab3bb7508e" + "gitHead": "9a971b0c22009d7405ca158b0847faf0d91ce9a7" } \ No newline at end of file diff --git a/libs/remix-tests/package.json b/libs/remix-tests/package.json index 043353cd26..6005d13cb4 100644 --- a/libs/remix-tests/package.json +++ b/libs/remix-tests/package.json @@ -1,6 +1,6 @@ { "name": "@remix-project/remix-tests", - "version": "0.2.48", + "version": "0.2.49", "description": "Tool to test Solidity smart contracts", "main": "src/index.js", "types": "./src/index.d.ts", @@ -41,9 +41,9 @@ "@ethereumjs/tx": "5.3.0", "@ethereumjs/util": "9.0.3", "@ethereumjs/vm": "8.0.0", - "@remix-project/remix-lib": "^0.5.55", - "@remix-project/remix-simulator": "^0.2.48", - "@remix-project/remix-solidity": "^0.5.34", + "@remix-project/remix-lib": "^0.5.56", + "@remix-project/remix-simulator": "^0.2.49", + "@remix-project/remix-solidity": "^0.5.35", "@remix-project/remix-url-resolver": "^0.0.42", "ansi-gray": "^0.1.1", "async": "^2.6.0", @@ -89,5 +89,5 @@ "@ethereumjs/trie": "6.2.0" }, "typings": "src/index.d.ts", - "gitHead": "4f5c2cb88bc38099f71c5407265a23ab3bb7508e" + "gitHead": "9a971b0c22009d7405ca158b0847faf0d91ce9a7" } \ No newline at end of file diff --git a/libs/remix-ui/app/src/lib/remix-app/components/dragbar/dragbar.tsx b/libs/remix-ui/app/src/lib/remix-app/components/dragbar/dragbar.tsx index 24c8099606..4bbb9066e8 100644 --- a/libs/remix-ui/app/src/lib/remix-app/components/dragbar/dragbar.tsx +++ b/libs/remix-ui/app/src/lib/remix-app/components/dragbar/dragbar.tsx @@ -9,6 +9,7 @@ interface IRemixDragBarUi { minWidth: number maximiseTrigger: number resetTrigger: number + layoutPosition: 'left' | 'right' } const DragBar = (props: IRemixDragBarUi) => { @@ -19,35 +20,61 @@ const DragBar = (props: IRemixDragBarUi) => { const nodeRef = React.useRef(null) // fix for strictmode useEffect(() => { - setDragBarPosX(offset + (props.hidden ? 0 : props.refObject.current.offsetWidth)) + if (props.hidden) { + setDragBarPosX(offset) + } else if (props.layoutPosition === 'left') { + setDragBarPosX(offset + props.refObject.current.offsetWidth) + } else if (props.layoutPosition === 'right') { + setDragBarPosX(offset) + } }, [props.hidden, offset]) useEffect(() => { initialWidth.current = props.refObject.current.clientWidth if (props.maximiseTrigger > 0) { - const width = 0.4 * window.innerWidth - if (width > props.refObject.current.offsetWidth) { - props.refObject.current.style.width = width + 'px' - setTimeout(() => { - setDragBarPosX(offset + width) - }, 300) + if (props.layoutPosition === 'left') { + const width = 0.4 * window.innerWidth + + if (width > props.refObject.current.offsetWidth) { + props.refObject.current.style.width = width + 'px' + setTimeout(() => { + setDragBarPosX(offset + width) + }, 300) + } + } else if (props.layoutPosition === 'right') { + const width = 0.4 * window.innerWidth + + if (width > props.refObject.current.offsetWidth) { + props.refObject.current.style.width = width + 'px' + setTimeout(() => { + setDragBarPosX(window.innerWidth - width) + }, 300) + } } } }, [props.maximiseTrigger]) useEffect(() => { if (props.maximiseTrigger > 0) { - props.refObject.current.style.width = initialWidth.current + 'px' - setTimeout(() => { - setDragBarPosX(offset + initialWidth.current) - }, 300) + if (props.layoutPosition === 'left') { + props.refObject.current.style.width = initialWidth.current + 'px' + setTimeout(() => { + setDragBarPosX(offset + initialWidth.current) + }, 300) + } else if (props.layoutPosition === 'right') { + props.refObject.current.style.width = props.minWidth + 'px' + setTimeout(() => { + setDragBarPosX(window.innerWidth - props.minWidth) + }, 300) + } } }, [props.resetTrigger]) const handleResize = () => { if (!props.refObject.current) return setOffSet(props.refObject.current.offsetLeft) - setDragBarPosX(props.refObject.current.offsetLeft + props.refObject.current.offsetWidth) + if (props.layoutPosition === 'left') setDragBarPosX(props.refObject.current.offsetLeft + props.refObject.current.offsetWidth) + else if (props.layoutPosition === 'right') setDragBarPosX(props.refObject.current.offsetLeft) } useEffect(() => { @@ -59,15 +86,28 @@ const DragBar = (props: IRemixDragBarUi) => { function stopDrag(data: any) { setDragState(false) - if (data.x < props.minWidth + offset) { - setDragBarPosX(offset) - props.setHideStatus(true) - } else { - props.refObject.current.style.width = data.x - offset + 'px' - setTimeout(() => { + if (props.layoutPosition === 'left') { + if (data.x < props.minWidth + offset) { + setDragBarPosX(offset) + props.setHideStatus(true) + } else { + props.refObject.current.style.width = data.x - offset + 'px' + setTimeout(() => { + props.setHideStatus(false) + setDragBarPosX(offset + props.refObject.current.offsetWidth) + }, 300) + } + } else if (props.layoutPosition === 'right') { + if (window.innerWidth - data.x < props.minWidth) { + setDragBarPosX(props.refObject.current.offsetLeft) props.setHideStatus(false) - setDragBarPosX(offset + props.refObject.current.offsetWidth) - }, 300) + } else { + props.refObject.current.style.width = (window.innerWidth - data.x) + 'px' + setTimeout(() => { + props.setHideStatus(false) + setDragBarPosX(props.refObject.current.offsetLeft) + }, 300) + } } } diff --git a/libs/remix-ui/app/src/lib/remix-app/remix-app.tsx b/libs/remix-ui/app/src/lib/remix-app/remix-app.tsx index 864819c62b..29dc3de3fd 100644 --- a/libs/remix-ui/app/src/lib/remix-app/remix-app.tsx +++ b/libs/remix-ui/app/src/lib/remix-app/remix-app.tsx @@ -27,14 +27,18 @@ const RemixApp = (props: IRemixAppUi) => { const [appReady, setAppReady] = useState(false) const [showEnterDialog, setShowEnterDialog] = useState(false) const [hideSidePanel, setHideSidePanel] = useState(false) - const [maximiseTrigger, setMaximiseTrigger] = useState(0) - const [resetTrigger, setResetTrigger] = useState(0) + const [hidePinnedPanel, setHidePinnedPanel] = useState(true) + const [maximiseLeftTrigger, setMaximiseLeftTrigger] = useState(0) + const [resetLeftTrigger, setResetLeftTrigger] = useState(0) + const [maximiseRightTrigger, setMaximiseRightTrigger] = useState(0) + const [resetRightTrigger, setResetRightTrigger] = useState(0) const [online, setOnline] = useState(true) const [locale, setLocale] = useState<{ code: string; messages: any }>({ code: 'en', messages: {} }) const sidePanelRef = useRef(null) + const pinnedPanelRef = useRef(null) useEffect(() => { async function activateApp() { @@ -81,20 +85,41 @@ const RemixApp = (props: IRemixAppUi) => { }) props.app.layout.event.on('maximisesidepanel', () => { - setMaximiseTrigger((prev) => { + setMaximiseLeftTrigger((prev) => { return prev + 1 }) }) props.app.layout.event.on('resetsidepanel', () => { - setResetTrigger((prev) => { + setResetLeftTrigger((prev) => { return prev + 1 }) }) + + props.app.layout.event.on('maximisepinnedpanel', () => { + setMaximiseRightTrigger((prev) => { + return prev + 1 + }) + }) + + props.app.layout.event.on('resetpinnedpanel', () => { + setResetRightTrigger((prev) => { + return prev + 1 + }) + }) + props.app.localeModule.events.on('localeChanged', (nextLocale) => { setLocale(nextLocale) }) + props.app.pinnedPanel.events.on('pinnedPlugin', () => { + setHidePinnedPanel(false) + }) + + props.app.pinnedPanel.events.on('unPinnedPlugin', () => { + setHidePinnedPanel(true) + }) + setInterval(() => { setOnline(window.navigator.onLine) }, 1000) @@ -166,19 +191,32 @@ const RemixApp = (props: IRemixAppUi) => { {props.app.sidePanel.render()}
- }> -
-
+
+ {props.app.pinnedPanel.render()} +
+ { + !hidePinnedPanel && + + }
{props.app.hiddenPanel.render()}
diff --git a/libs/remix-ui/app/src/lib/remix-app/style/remix-app.css b/libs/remix-ui/app/src/lib/remix-app/style/remix-app.css index 70686d632f..a66b49dcff 100644 --- a/libs/remix-ui/app/src/lib/remix-app/style/remix-app.css +++ b/libs/remix-ui/app/src/lib/remix-app/style/remix-app.css @@ -34,6 +34,10 @@ pre { width : 320px; transition : width 0.25s; } +.pinnedpanel { + width : 320px; + transition : width 0.25s; +} .highlightcode { position : absolute; z-index : 20; diff --git a/libs/remix-ui/debugger-ui/src/lib/api/debugger-api.ts b/libs/remix-ui/debugger-ui/src/lib/api/debugger-api.ts index 577974a282..0f444a7464 100644 --- a/libs/remix-ui/debugger-ui/src/lib/api/debugger-api.ts +++ b/libs/remix-ui/debugger-ui/src/lib/api/debugger-api.ts @@ -183,14 +183,26 @@ export const DebuggerApiMixin = (Base) => class extends Base { showMessage (title: string, message: string) {} - onStartDebugging (debuggerBackend: any) { - this.call('layout', 'maximiseSidePanel') + async onStartDebugging (debuggerBackend: any) { + const pinnedPlugin = await this.call('pinnedPanel', 'currentFocus') + + if (pinnedPlugin === 'debugger') { + this.call('layout', 'maximisePinnedPanel') + } else { + this.call('layout', 'maximiseSidePanel') + } this.emit('startDebugging') this.debuggerBackend = debuggerBackend } - onStopDebugging () { - this.call('layout', 'resetSidePanel') + async onStopDebugging () { + const pinnedPlugin = await this.call('pinnedPanel', 'currentFocus') + + if (pinnedPlugin === 'debugger') { + this.call('layout', 'resetPinnedPanel') + } else { + this.call('layout', 'resetSidePanel') + } this.emit('stopDebugging') this.debuggerBackend = null } diff --git a/libs/remix-ui/editor/src/lib/providers/completion/completionGlobals.ts b/libs/remix-ui/editor/src/lib/providers/completion/completionGlobals.ts index cd8fc48e67..2a21068ba8 100644 --- a/libs/remix-ui/editor/src/lib/providers/completion/completionGlobals.ts +++ b/libs/remix-ui/editor/src/lib/providers/completion/completionGlobals.ts @@ -524,7 +524,7 @@ export function GeCompletionUnits(range: monacoTypes.IRange, monaco): monacoType if (unit !== 'years') { completionItem.detail = unit + ': time unit'; } else { - completionItem.detail = 'DEPRECATED: ' + unit + ': time unit'; + completionItem.detail = 'REMOVED in v0.5.0: ' + unit + ': time unit'; } completionItems.push(completionItem); }); diff --git a/libs/remix-ui/panel/src/lib/main/main-panel.tsx b/libs/remix-ui/panel/src/lib/main/main-panel.tsx index ac7033fe15..0786a644fd 100644 --- a/libs/remix-ui/panel/src/lib/main/main-panel.tsx +++ b/libs/remix-ui/panel/src/lib/main/main-panel.tsx @@ -30,7 +30,8 @@ const RemixUIMainPanel = (props: RemixUIMainPanelProps) => { active: panel.active, view: panel.plugin.profile.name === 'tabs' ? panel.plugin.renderTabsbar() : panel.plugin.render(), class: panel.plugin.profile.name + '-wrap ' + (panel.minimized ? 'minimized ' : ' ') + ((platform === appPlatformTypes.desktop)? 'desktop' : ''), - minimized: panel.minimized + minimized: panel.minimized, + pinned: panel.pinned }) }) setPlugins(pluginPanels) 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 590893b3a5..b2d87297e2 100644 --- a/libs/remix-ui/panel/src/lib/plugins/panel-header.tsx +++ b/libs/remix-ui/panel/src/lib/plugins/panel-header.tsx @@ -1,11 +1,13 @@ -import React, {useEffect, useRef, useState} from 'react' // eslint-disable-line +import React, {useEffect, useState} from 'react' // eslint-disable-line import { FormattedMessage } from 'react-intl' import { PluginRecord } from '../types' import './panel.css' -import { CustomTooltip } from '@remix-ui/helper' +import { CustomTooltip, RenderIf, RenderIfNot } from '@remix-ui/helper' export interface RemixPanelProps { - plugins: Record + plugins: Record, + pinView?: (profile: PluginRecord['profile'], view: PluginRecord['view']) => void, + unPinView?: (profile: PluginRecord['profile']) => void } const RemixUIPanelHeader = (props: RemixPanelProps) => { const [plugin, setPlugin] = useState() @@ -25,6 +27,14 @@ const RemixUIPanelHeader = (props: RemixPanelProps) => { setToggleExpander(!toggleExpander) } + const pinPlugin = () => { + props.pinView && props.pinView(plugin.profile, plugin.view) + } + + const unPinPlugin = () => { + props.unPinView && props.unPinView(plugin.profile) + } + const tooltipChild = return ( @@ -34,21 +44,47 @@ const RemixUIPanelHeader = (props: RemixPanelProps) => { {plugin?.profile?.name && }
+ { + plugin && plugin.profile.name !== 'filePanel' && ( + + +
+ }> + + +
+
+
+ ) + }
{plugin?.profile?.maintainedBy?.toLowerCase() === 'remix' ? ( - }> + }> ) - : (}> + : (}> ) }
- } tooltipId="pluginInfoTooltip" tooltipClasses="text-nowrap"> + } tooltipId="pluginInfoTooltip" tooltipClasses="text-nowrap"> {tooltipChild}
+ { + plugin && plugin.profile.name !== 'filePanel' && ( + + +
+ }> + + +
+
+
+ ) + }
diff --git a/libs/remix-ui/panel/src/lib/plugins/panel-plugin.tsx b/libs/remix-ui/panel/src/lib/plugins/panel-plugin.tsx index 2c9d3d71ee..fa6760dc08 100644 --- a/libs/remix-ui/panel/src/lib/plugins/panel-plugin.tsx +++ b/libs/remix-ui/panel/src/lib/plugins/panel-plugin.tsx @@ -3,7 +3,9 @@ import React, {forwardRef, useEffect, useRef, useState} from 'react' // eslint-d import { PluginRecord } from '../types' import './panel.css' interface panelPLuginProps { - pluginRecord: PluginRecord + pluginRecord: PluginRecord, + initialState?: any, + children?: any } const RemixUIPanelPlugin = (props: panelPLuginProps, panelRef: any) => { @@ -14,7 +16,19 @@ const RemixUIPanelPlugin = (props: panelPLuginProps, panelRef: any) => { if (ref.current) { if (props.pluginRecord.view) { if (React.isValidElement(props.pluginRecord.view)) { - setView(props.pluginRecord.view) + let view = props.pluginRecord.view + + if (props.initialState) { + view = React.Children.map((props.pluginRecord.view.props as any).children, child => { + if (React.isValidElement(child) && typeof child.type === 'function') { + // Safe to clone and pass `initialState` + return React.cloneElement(child, { ...props, initialState: props.initialState } as any) + } + return child + }) + } + + setView(view) } else { ref.current.appendChild(props.pluginRecord.view) } 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 1b3d668a35..7ff51827bd 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 @@ -6,8 +6,9 @@ import { PluginRecord } from '../types' /* eslint-disable-next-line */ export interface RemixPanelProps { - plugins: Record - header: JSX.Element + plugins: Record, + header: JSX.Element, + pluginState?: any, } export function RemixPluginPanel(props: RemixPanelProps) { @@ -17,7 +18,7 @@ export function RemixPluginPanel(props: RemixPanelProps) {
{Object.values(props.plugins).map((pluginRecord) => { - return + return })}
diff --git a/libs/remix-ui/panel/src/lib/types/index.ts b/libs/remix-ui/panel/src/lib/types/index.ts index 67b79c4214..c3913340cc 100644 --- a/libs/remix-ui/panel/src/lib/types/index.ts +++ b/libs/remix-ui/panel/src/lib/types/index.ts @@ -4,6 +4,7 @@ export type PluginRecord = { profile: Profile view: any active: boolean + pinned: boolean class?: string minimized?: boolean } diff --git a/libs/remix-ui/run-tab/src/lib/actions/events.ts b/libs/remix-ui/run-tab/src/lib/actions/events.ts index eb2c4a8525..b5b4037eb2 100644 --- a/libs/remix-ui/run-tab/src/lib/actions/events.ts +++ b/libs/remix-ui/run-tab/src/lib/actions/events.ts @@ -12,8 +12,13 @@ import { getNetworkProxyAddresses } from "./deploy" import { shortenAddress } from "@remix-ui/helper" const _paq = window._paq = window._paq || [] +let dispatch: React.Dispatch = () => {} -export const setupEvents = (plugin: RunTab, dispatch: React.Dispatch) => { +export const setEventsDispatch = (reducerDispatch: React.Dispatch) => { + dispatch = reducerDispatch +} + +export const setupEvents = (plugin: RunTab) => { // This maintains current network state and update the pinned contracts list, // only when there is a change in provider or in chain id for same provider // as 'networkStatus' is triggered in each 10 seconds 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 b5c65e89f5..5387887f67 100644 --- a/libs/remix-ui/run-tab/src/lib/actions/index.ts +++ b/libs/remix-ui/run-tab/src/lib/actions/index.ts @@ -1,7 +1,7 @@ // eslint-disable-next-line no-unused-vars import React from 'react' import { RunTab } from '../types/run-tab' -import { resetAndInit, setupEvents } from './events' +import { resetAndInit, setupEvents, setEventsDispatch } from './events' import { createNewBlockchainAccount, setExecutionContext, signMessageWithAddress } from './account' import { clearInstances, clearPopUp, removeInstance, setAccount, setGasFee, setMatchPassphrasePrompt, setNetworkNameFromProvider, setPassphrasePrompt, setSelectedContract, setSendTransactionValue, setUnit, @@ -22,11 +22,14 @@ declare global { const _paq = window._paq = window._paq || [] //eslint-disable-line let plugin: RunTab, dispatch: React.Dispatch = () => {} -export const initRunTab = (udapp: RunTab) => async (reducerDispatch: React.Dispatch) => { +export const initRunTab = (udapp: RunTab, resetEventsAndAccounts: boolean) => async (reducerDispatch: React.Dispatch) => { plugin = udapp dispatch = reducerDispatch - setupEvents(plugin, dispatch) - resetAndInit(plugin) + setEventsDispatch(reducerDispatch) + if (resetEventsAndAccounts) { + setupEvents(plugin) + resetAndInit(plugin) + } } export const setAccountAddress = (account: string) => setAccount(dispatch, account) diff --git a/libs/remix-ui/run-tab/src/lib/components/contractGUI.tsx b/libs/remix-ui/run-tab/src/lib/components/contractGUI.tsx index ecba6ec713..8dfafbf42a 100644 --- a/libs/remix-ui/run-tab/src/lib/components/contractGUI.tsx +++ b/libs/remix-ui/run-tab/src/lib/components/contractGUI.tsx @@ -373,10 +373,12 @@ export function ContractGUI(props: ContractGUIProps) {
diff --git a/libs/remix-ui/run-tab/src/lib/components/universalDappUI.tsx b/libs/remix-ui/run-tab/src/lib/components/universalDappUI.tsx index 447fa83596..f2dc284fae 100644 --- a/libs/remix-ui/run-tab/src/lib/components/universalDappUI.tsx +++ b/libs/remix-ui/run-tab/src/lib/components/universalDappUI.tsx @@ -267,7 +267,7 @@ export function UniversalDappUI(props: UdappProps) { ) }
- +
{ props.isPinnedContract ? (
}> 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 3e3985a772..26d87b7fc0 100644 --- a/libs/remix-ui/run-tab/src/lib/run-tab.tsx +++ b/libs/remix-ui/run-tab/src/lib/run-tab.tsx @@ -74,18 +74,25 @@ export function RunTabUI(props: RunTabProps) { storage: null, contract: null }) - runTabInitialState.selectExEnv = plugin.blockchain.getProvider() - const [runTab, dispatch] = useReducer(runTabReducer, runTabInitialState) + const initialState = props.initialState || runTabInitialState + + initialState.selectExEnv = plugin.blockchain.getProvider() + const [runTab, dispatch] = useReducer(runTabReducer, initialState) const REACT_API = { runTab } const currentfile = plugin.config.get('currentFile') useEffect(() => { - initRunTab(plugin)(dispatch) - plugin.onInitDone() + if (!props.initialState) { + initRunTab(plugin, true)(dispatch) + plugin.onInitDone() + } else { + initRunTab(plugin, false)(dispatch) + } }, [plugin]) useEffect(() => { plugin.onReady(runTab) + plugin.call('pluginStateLogger', 'logPluginState', 'udapp', runTab) }, [REACT_API]) useEffect(() => { 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 6e19d45b3a..7e4ae84533 100644 --- a/libs/remix-ui/run-tab/src/lib/types/index.ts +++ b/libs/remix-ui/run-tab/src/lib/types/index.ts @@ -5,7 +5,8 @@ import { RunTab } from './run-tab' import { SolcInput, SolcOutput } from '@openzeppelin/upgrades-core' import { LayoutCompatibilityReport } from '@openzeppelin/upgrades-core/dist/storage/report' export interface RunTabProps { - plugin: RunTab + plugin: RunTab, + initialState: RunTabState } export interface Contract { diff --git a/libs/remix-ui/search/src/lib/components/Search.tsx b/libs/remix-ui/search/src/lib/components/Search.tsx index 11dcc9addd..43b2418015 100644 --- a/libs/remix-ui/search/src/lib/components/Search.tsx +++ b/libs/remix-ui/search/src/lib/components/Search.tsx @@ -15,7 +15,7 @@ export const SearchTab = (props) => { return ( <>
- + diff --git a/libs/remix-ui/search/src/lib/context/context.tsx b/libs/remix-ui/search/src/lib/context/context.tsx index 8e50f0b53e..336955c744 100644 --- a/libs/remix-ui/search/src/lib/context/context.tsx +++ b/libs/remix-ui/search/src/lib/context/context.tsx @@ -443,6 +443,7 @@ export const SearchProvider = ({ children = [], reducer = SearchReducer, initial } })() } + plugin.call('pluginStateLogger', 'logPluginState', 'search', state) }, [state.timeStamp]) return ( 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 9786e11fbf..b1751ecb4c 100644 --- a/libs/remix-ui/solidity-compiler/src/lib/compiler-container.tsx +++ b/libs/remix-ui/solidity-compiler/src/lib/compiler-container.tsx @@ -373,7 +373,7 @@ export const CompilerContainer = (props: CompilerContainerProps) => { // Load solc compiler version according to pragma in contract file const _setCompilerVersionFromPragma = (filename: string) => { - if (!solJsonBinData.selectorList) return + if (solJsonBinData && !solJsonBinData.selectorList) return api.readFile(filename).then((data) => { if (!data) return const pragmaArr = data.match(/(pragma solidity (.+?);)/g) diff --git a/libs/remix-ui/solidity-compiler/src/lib/logic/flattenerUtilities.ts b/libs/remix-ui/solidity-compiler/src/lib/logic/flattenerUtilities.ts index 2ff814125a..26cc45d992 100644 --- a/libs/remix-ui/solidity-compiler/src/lib/logic/flattenerUtilities.ts +++ b/libs/remix-ui/solidity-compiler/src/lib/logic/flattenerUtilities.ts @@ -1,42 +1,49 @@ import type { CompilationSource, AstNode } from '@remix-project/remix-solidity' -const IMPORT_SOLIDITY_REGEX = /^\s*import(\s+).*$/gm; -const SPDX_SOLIDITY_REGEX = /^\s*\/\/ SPDX-License-Identifier:.*$/gm; +const IMPORT_SOLIDITY_REGEX = /^\s*import(\s+).*$/gm +const SPDX_SOLIDITY_REGEX = /^\s*\/\/ SPDX-License-Identifier:.*$/gm +const PRAGMA_SOLIDITY_REGEX = /^pragma experimental\s+.*$/gm type Visited = { [key: string]: number } -export function getDependencyGraph(ast: { [name: string]: CompilationSource }, target: string, remappings: string[]) { - const graph = tsort(); - const visited = {}; - visited[target] = 1; - _traverse(graph, visited, ast, target, remappings); - return graph; +export function getDependencyGraph(ast: { [name: string]: CompilationSource }, target: string, remappings: string[], order: string[]) { + const graph = tsort() + const visited = {} + visited[target] = 1 + _traverse(graph, visited, ast, target, remappings, order) + return graph } -export function concatSourceFiles(files: any[], sources: any) { - let concat = ''; - for (const file of files) { - const source = sources[file].content; - const sourceWithoutImport = source.replace(IMPORT_SOLIDITY_REGEX, ''); - const sourceWithoutSPDX = sourceWithoutImport.replace(SPDX_SOLIDITY_REGEX, ''); - concat += `\n// File: ${file}\n\n`; - concat += sourceWithoutSPDX; - } - return concat; +export function concatSourceFiles(files: any[], sources: any, order: string[]) { + let concat = '' + order.forEach((importName) => { + for (const file of files) { + if (file === importName) { + const source = sources[file].content + const sourceWithoutImport = source.replace(IMPORT_SOLIDITY_REGEX, '') + const sourceWithoutSPDX = sourceWithoutImport.replace(SPDX_SOLIDITY_REGEX, '') + const sourceWithoutDuplicatePragma = sourceWithoutSPDX.replace(PRAGMA_SOLIDITY_REGEX, '') + concat += `\n// File: ${file}\n\n` + concat += sourceWithoutDuplicatePragma + } + } + }) + return concat } -function _traverse(graph: Graph, visited: Visited, ast: { [name: string]: CompilationSource }, name: string, remappings: string[]) { +function _traverse(graph: Graph, visited: Visited, ast: { [name: string]: CompilationSource }, name: string, remappings: string[], order: string[]) { let currentAst = null currentAst = ast[name].ast - const dependencies = _getDependencies(currentAst); + const dependencies = _getDependencies(currentAst) for (const dependency of dependencies) { - const path = resolve(name, dependency, remappings); + const path = resolve(name, dependency, remappings) if (path in visited) { - // continue; // fixes wrong ordering of source in flattened file + continue; // fixes wrong ordering of source in flattened file } - visited[path] = 1; - graph.add(name, path); - _traverse(graph, visited, ast, path, remappings); + visited[path] = 1 + graph.add(name, path) + _traverse(graph, visited, ast, path, remappings, order) } + order.push(name) } function _getDependencies(ast: AstNode) { @@ -191,4 +198,4 @@ export function normalizeContractPath(contractPath: string): string[] { } const resultingPath = `${folders}${filename}` return [folders,resultingPath, filename] -} \ No newline at end of file +} diff --git a/libs/remix-url-resolver/package.json b/libs/remix-url-resolver/package.json index 970e8f909e..a0a9c3ad02 100644 --- a/libs/remix-url-resolver/package.json +++ b/libs/remix-url-resolver/package.json @@ -1,6 +1,6 @@ { "name": "@remix-project/remix-url-resolver", - "version": "0.0.77", + "version": "0.0.78", "description": "Solidity import url resolver engine", "main": "src/index.js", "types": "src/index.d.ts", @@ -41,5 +41,5 @@ "typescript": "^3.1.6" }, "typings": "src/index.d.ts", - "gitHead": "4f5c2cb88bc38099f71c5407265a23ab3bb7508e" + "gitHead": "9a971b0c22009d7405ca158b0847faf0d91ce9a7" } \ No newline at end of file diff --git a/libs/remix-ws-templates/package.json b/libs/remix-ws-templates/package.json index fc5e3f0696..a17eb760ef 100644 --- a/libs/remix-ws-templates/package.json +++ b/libs/remix-ws-templates/package.json @@ -1,6 +1,6 @@ { "name": "@remix-project/remix-ws-templates", - "version": "1.0.42", + "version": "1.0.43", "description": "Create a Remix IDE workspace using different templates", "main": "src/index.js", "types": "src/index.d.ts", @@ -24,5 +24,5 @@ "ethers": "^5.4.2", "web3": "^4.1.1" }, - "gitHead": "4f5c2cb88bc38099f71c5407265a23ab3bb7508e" + "gitHead": "9a971b0c22009d7405ca158b0847faf0d91ce9a7" } \ No newline at end of file diff --git a/libs/remixd/package.json b/libs/remixd/package.json index 62bc99caae..f70d89ab88 100644 --- a/libs/remixd/package.json +++ b/libs/remixd/package.json @@ -1,6 +1,6 @@ { "name": "@remix-project/remixd", - "version": "0.6.31", + "version": "0.6.32", "description": "remix server: allow accessing file system from remix.ethereum.org and start a dev environment (see help section)", "main": "index.js", "types": "./index.d.ts", diff --git a/package.json b/package.json index 741cbb780e..ade5becb79 100644 --- a/package.json +++ b/package.json @@ -1,10 +1,10 @@ { "name": "remix-project", - "version": "0.49.0-dev", + "version": "0.50.0-dev", "license": "MIT", "description": "Ethereum Remix Monorepo", "main": "index.js", - "defaultVersion": "soljson-v0.8.25+commit.b61c2a91.js", + "defaultVersion": "soljson-v0.8.26+commit.8a97fa7a.js", "keywords": [ "ethereum", "solidity",