Merge branch 'master' into master

pull/5370/head
yann300 3 years ago committed by GitHub
commit da38ab27b0
  1. 20
      apps/remix-ide-e2e/src/tests/plugin_api.ts
  2. 2
      apps/remix-ide-e2e/src/tests/recorder.test.ts
  3. 2
      apps/remix-ide-e2e/src/tests/url.test.ts
  4. 4
      apps/remix-ide-e2e/src/tests/workspace.test.ts
  5. 1
      apps/remix-ide/src/app/panels/tab-proxy.js
  6. 2
      apps/remix-ide/src/app/plugins/config.ts
  7. 5
      apps/remix-ide/src/remixAppManager.js
  8. 6
      apps/solidity-compiler/src/app/compiler-api.ts
  9. 27
      apps/solidity-compiler/src/app/compiler.ts
  10. 2
      libs/remix-lib/src/types/ICompilerApi.ts
  11. 6
      libs/remix-solidity/src/compiler/compiler-input.ts
  12. 46
      libs/remix-solidity/src/compiler/compiler.ts
  13. 2
      libs/remix-solidity/src/compiler/types.ts
  14. 12
      libs/remix-ui/helper/src/lib/remix-ui-helper.ts
  15. 6
      libs/remix-ui/run-tab/src/lib/components/instanceContainerUI.tsx
  16. 67
      libs/remix-ui/run-tab/src/lib/components/recorderCardUI.tsx
  17. 7
      libs/remix-ui/run-tab/src/lib/css/card.css
  18. 15
      libs/remix-ui/run-tab/src/lib/css/run-tab.css
  19. 3
      libs/remix-ui/run-tab/src/lib/types/index.ts
  20. 280
      libs/remix-ui/solidity-compiler/src/lib/compiler-container.tsx
  21. 18
      libs/remix-ui/solidity-compiler/src/lib/compilerConfiguration.tsx
  22. 9
      libs/remix-ui/solidity-compiler/src/lib/css/style.css
  23. 16
      libs/remix-ui/solidity-compiler/src/lib/logic/compileTabLogic.ts
  24. 18
      libs/remix-ui/solidity-compiler/src/lib/solidity-compiler.tsx
  25. 2
      libs/remix-ui/solidity-compiler/src/lib/types/index.ts
  26. 2
      libs/remix-ui/solidity-compiler/tsconfig.json

@ -230,7 +230,13 @@ module.exports = {
}, },
'Should get current files #group7': async function (browser: NightwatchBrowser) { 'Should get current files #group7': async function (browser: NightwatchBrowser) {
await clickAndCheckLog(browser, 'fileManager:readdir', { contracts: { isDirectory: true }, scripts: { isDirectory: true }, tests: { isDirectory: true }, 'README.txt': { isDirectory: false } }, null, '/') await clickAndCheckLog(browser, 'fileManager:readdir', {
'compiler_config.json': { isDirectory: false },
contracts: { isDirectory: true },
scripts: { isDirectory: true },
tests: { isDirectory: true },
'README.txt': { isDirectory: false }
}, null, '/')
}, },
'Should throw error on current file #group7': async function (browser: NightwatchBrowser) { 'Should throw error on current file #group7': async function (browser: NightwatchBrowser) {
await clickAndCheckLog(browser, 'fileManager:getCurrentFile', 'Error from IDE : Error: No such file or directory No file selected', null, null) await clickAndCheckLog(browser, 'fileManager:getCurrentFile', 'Error from IDE : Error: No such file or directory No file selected', null, null)
@ -280,12 +286,20 @@ module.exports = {
'Should create empty workspace #group2': async function (browser: NightwatchBrowser) { 'Should create empty workspace #group2': async function (browser: NightwatchBrowser) {
await clickAndCheckLog(browser, 'filePanel:createWorkspace', null, null, ['emptyworkspace', true]) await clickAndCheckLog(browser, 'filePanel:createWorkspace', null, null, ['emptyworkspace', true])
await clickAndCheckLog(browser, 'filePanel:getCurrentWorkspace', { name: 'emptyworkspace', isLocalhost: false, absolutePath: '.workspaces/emptyworkspace' }, null, null) await clickAndCheckLog(browser, 'filePanel:getCurrentWorkspace', { name: 'emptyworkspace', isLocalhost: false, absolutePath: '.workspaces/emptyworkspace' }, null, null)
await clickAndCheckLog(browser, 'fileManager:readdir', {}, null, '/') await clickAndCheckLog(browser, 'fileManager:readdir', {
'compiler_config.json': { isDirectory: false }
}, null, '/')
}, },
'Should create workspace #group2': async function (browser: NightwatchBrowser) { 'Should create workspace #group2': async function (browser: NightwatchBrowser) {
await clickAndCheckLog(browser, 'filePanel:createWorkspace', null, null, 'testspace') await clickAndCheckLog(browser, 'filePanel:createWorkspace', null, null, 'testspace')
await clickAndCheckLog(browser, 'filePanel:getCurrentWorkspace', { name: 'testspace', isLocalhost: false, absolutePath: '.workspaces/testspace' }, null, null) await clickAndCheckLog(browser, 'filePanel:getCurrentWorkspace', { name: 'testspace', isLocalhost: false, absolutePath: '.workspaces/testspace' }, null, null)
await clickAndCheckLog(browser, 'fileManager:readdir', { contracts: { isDirectory: true }, scripts: { isDirectory: true }, tests: { isDirectory: true }, 'README.txt': { isDirectory: false } }, null, null) await clickAndCheckLog(browser, 'fileManager:readdir', {
'compiler_config.json': { isDirectory: false },
contracts: { isDirectory: true },
scripts: { isDirectory: true },
tests: { isDirectory: true },
'README.txt': { isDirectory: false }
}, null, null)
}, },
'Should get all workspaces #group2': async function (browser: NightwatchBrowser) { 'Should get all workspaces #group2': async function (browser: NightwatchBrowser) {
await clickAndCheckLog(browser, 'filePanel:getWorkspaces', ['default_workspace', 'emptyworkspace', 'testspace'], null, null) await clickAndCheckLog(browser, 'filePanel:getWorkspaces', ['default_workspace', 'emptyworkspace', 'testspace'], null, null)

@ -17,7 +17,7 @@ module.exports = {
.pause(5000) .pause(5000)
.clickLaunchIcon('udapp') .clickLaunchIcon('udapp')
.selectAccount('0xCA35b7d915458EF540aDe6068dFe2F44E8fa733c') // this account will be used for this test suite .selectAccount('0xCA35b7d915458EF540aDe6068dFe2F44E8fa733c') // this account will be used for this test suite
.click('[data-id="udapp_arrow"]') .click('[data-id="udappRecorderTitleExpander"]')
.click('[data-id="runtransaction"]') .click('[data-id="runtransaction"]')
.clickInstance(0) .clickInstance(0)
.clickInstance(1) .clickInstance(1)

@ -81,6 +81,7 @@ module.exports = {
.refresh() .refresh()
.pause(5000) .pause(5000)
.clickLaunchIcon('solidity') .clickLaunchIcon('solidity')
.click('*[data-id="scConfigExpander"]')
.assert.containsText('#versionSelector option[data-id="selected"]', '0.7.4+commit.3f05b770') .assert.containsText('#versionSelector option[data-id="selected"]', '0.7.4+commit.3f05b770')
.assert.containsText('#evmVersionSelector option[data-id="selected"]', 'istanbul') .assert.containsText('#evmVersionSelector option[data-id="selected"]', 'istanbul')
.assert.containsText('#compilierLanguageSelector option[data-id="selected"]', 'Yul') .assert.containsText('#compilierLanguageSelector option[data-id="selected"]', 'Yul')
@ -96,6 +97,7 @@ module.exports = {
.pause(5000) .pause(5000)
.clickLaunchIcon('solidity') .clickLaunchIcon('solidity')
.pause(5000) .pause(5000)
.click('*[data-id="scConfigExpander"]')
.assert.containsText('#versionSelector option[data-id="selected"]', 'custom') .assert.containsText('#versionSelector option[data-id="selected"]', 'custom')
// default values // default values
.assert.containsText('#evmVersionSelector option[data-id="selected"]', 'default') .assert.containsText('#evmVersionSelector option[data-id="selected"]', 'default')

@ -107,8 +107,8 @@ module.exports = {
const fileList = document.querySelector('*[data-id="treeViewUltreeViewMenu"]') const fileList = document.querySelector('*[data-id="treeViewUltreeViewMenu"]')
return fileList.getElementsByTagName('li').length; return fileList.getElementsByTagName('li').length;
}, [], function(result){ }, [], function(result){
// check there are no files in FE // check there are no files in FE except config file
browser.assert.equal(result.value, 0, 'Incorrect number of files'); browser.assert.equal(result.value, 1, 'Incorrect number of files');
}); });
}, },

@ -215,6 +215,7 @@ export class TabProxy extends Plugin {
} }
renameTab (oldName, newName) { renameTab (oldName, newName) {
// The new tab is being added by FileManager
this.removeTab(oldName) this.removeTab(oldName)
} }

@ -18,7 +18,7 @@ export class ConfigPlugin extends Plugin {
const queryParams = new QueryParams() const queryParams = new QueryParams()
const params = queryParams.get() const params = queryParams.get()
const config = Registry.getInstance().get('config').api const config = Registry.getInstance().get('config').api
let param = params[name] || config.get(name) || config.get('settings/' + name) const param = params[name] || config.get(name) || config.get('settings/' + name)
if (param === 'true') return true if (param === 'true') return true
if (param === 'false') return false if (param === 'false') return false
return param return param

@ -7,7 +7,7 @@ const _paq = window._paq = window._paq || []
const requiredModules = [ // services + layout views + system views const requiredModules = [ // services + layout views + system views
'manager', 'config', 'compilerArtefacts', 'compilerMetadata', 'contextualListener', 'editor', 'offsetToLineColumnConverter', 'network', 'theme', 'manager', 'config', 'compilerArtefacts', 'compilerMetadata', 'contextualListener', 'editor', 'offsetToLineColumnConverter', 'network', 'theme',
'fileManager', 'contentImport', 'blockchain', 'web3Provider', 'scriptRunner', 'fetchAndCompile', 'mainPanel', 'hiddenPanel', 'sidePanel', 'menuicons', 'fileManager', 'contentImport', 'blockchain', 'web3Provider', 'scriptRunner', 'fetchAndCompile', 'mainPanel', 'hiddenPanel', 'sidePanel', 'menuicons',
'filePanel', 'terminal', 'settings', 'pluginManager', 'tabs', 'udapp', 'dGitProvider', 'solidity-logic', 'gistHandler', 'layout', 'filePanel', 'terminal', 'settings', 'pluginManager', 'tabs', 'udapp', 'dGitProvider', 'solidity', 'solidity-logic', 'gistHandler', 'layout',
'notification', 'permissionhandler', 'walkthrough', 'storage', 'restorebackupzip', 'link-libraries', 'deploy-libraries', 'openzeppelin-proxy', 'hardhat-provider', 'compileAndRun', 'search'] 'notification', 'permissionhandler', 'walkthrough', 'storage', 'restorebackupzip', 'link-libraries', 'deploy-libraries', 'openzeppelin-proxy', 'hardhat-provider', 'compileAndRun', 'search']
const dependentModules = ['git', 'hardhat', 'truffle', 'slither'] // module which shouldn't be manually activated (e.g git is activated by remixd) const dependentModules = ['git', 'hardhat', 'truffle', 'slither'] // module which shouldn't be manually activated (e.g git is activated by remixd)
@ -19,7 +19,8 @@ const sensitiveCalls = {
} }
export function isNative(name) { export function isNative(name) {
const nativePlugins = ['vyper', 'workshops', 'debugger', 'remixd', 'menuicons', 'solidity', 'hardhat-provider', 'solidityStaticAnalysis', 'solidityUnitTesting', 'layout', 'notification', 'hardhat-provider', 'ganache-provider'] const nativePlugins = ['vyper', 'workshops', 'debugger', 'remixd', 'menuicons', 'solidity', 'solidity-logic',
'hardhat-provider', 'solidityStaticAnalysis', 'solidityUnitTesting', 'layout', 'notification', 'hardhat-provider', 'ganache-provider']
return nativePlugins.includes(name) || requiredModules.includes(name) return nativePlugins.includes(name) || requiredModules.includes(name)
} }

@ -18,7 +18,7 @@ export const CompilerApiMixin = (Base) => class extends Base {
onCurrentFileChanged: (fileName: string) => void onCurrentFileChanged: (fileName: string) => void
// onResetResults: () => void // onResetResults: () => void
onSetWorkspace: (workspace: any) => void onSetWorkspace: (isLocalhost: boolean, workspaceName: string) => void
onNoFileSelected: () => void onNoFileSelected: () => void
onCompilationFinished: (compilationDetails: { contractMap: { file: string } | Record<string, any>, contractsDetails: Record<string, any> }) => void onCompilationFinished: (compilationDetails: { contractMap: { file: string } | Record<string, any>, contractsDetails: Record<string, any> }) => void
onSessionSwitched: () => void onSessionSwitched: () => void
@ -237,12 +237,12 @@ export const CompilerApiMixin = (Base) => class extends Base {
this.on('filePanel', 'setWorkspace', (workspace) => { this.on('filePanel', 'setWorkspace', (workspace) => {
this.resetResults() this.resetResults()
if (this.onSetWorkspace) this.onSetWorkspace(workspace.isLocalhost) if (this.onSetWorkspace) this.onSetWorkspace(workspace.isLocalhost, workspace.name)
}) })
this.on('remixd', 'rootFolderChanged', () => { this.on('remixd', 'rootFolderChanged', () => {
this.resetResults() this.resetResults()
if (this.onSetWorkspace) this.onSetWorkspace(true) if (this.onSetWorkspace) this.onSetWorkspace(true, 'localhost')
}) })
this.on('editor', 'sessionSwitched', () => { this.on('editor', 'sessionSwitched', () => {

@ -6,31 +6,14 @@ import { CompilerApiMixin } from './compiler-api'
import { ICompilerApi } from '@remix-project/remix-lib-ts' import { ICompilerApi } from '@remix-project/remix-lib-ts'
import { CompileTabLogic } from '@remix-ui/solidity-compiler' import { CompileTabLogic } from '@remix-ui/solidity-compiler'
const profile = {
name: 'solidity',
displayName: 'Solidity compiler',
icon: 'assets/img/solidity.webp',
description: 'Compile solidity contracts',
kind: 'compiler',
permission: true,
location: 'sidePanel',
documentation: 'https://remix-ide.readthedocs.io/en/latest/solidity_editor.html',
version: '0.0.1',
methods: ['getCompilationResult', 'compile', 'compileWithParameters', 'setCompilerConfig', 'compileFile', 'getCompilerState']
}
const defaultAppParameters = {
hideWarnings: false,
autoCompile: false,
includeNightlies: false
}
const defaultCompilerParameters = { const defaultCompilerParameters = {
runs: '200', runs: '200',
optimize: false, optimize: false,
version: 'soljson-v0.8.7+commit.e28d00a7', version: 'soljson-v0.8.7+commit.e28d00a7',
evmVersion: null, // compiler default evmVersion: null, // compiler default
language: 'Solidity' language: 'Solidity',
useFileConfiguration: false,
configFilePath: "compiler_config.json"
} }
export class CompilerClientApi extends CompilerApiMixin(PluginClient) implements ICompilerApi { export class CompilerClientApi extends CompilerApiMixin(PluginClient) implements ICompilerApi {
constructor () { constructor () {
@ -48,7 +31,9 @@ export class CompilerClientApi extends CompilerApiMixin(PluginClient) implements
optimize: localStorage.getItem('optimize') === 'true', optimize: localStorage.getItem('optimize') === 'true',
version: localStorage.getItem('version') || defaultCompilerParameters.version, version: localStorage.getItem('version') || defaultCompilerParameters.version,
evmVersion: localStorage.getItem('evmVersion') || defaultCompilerParameters.evmVersion, // default evmVersion: localStorage.getItem('evmVersion') || defaultCompilerParameters.evmVersion, // default
language: localStorage.getItem('language') || defaultCompilerParameters.language language: localStorage.getItem('language') || defaultCompilerParameters.language,
useFileConfiguration: localStorage.getItem('useFileConfiguration') === 'true',
configFilePath: localStorage.getItem('configFilePath') || defaultCompilerParameters.configFilePath
} }
return params return params
} }

@ -24,7 +24,7 @@ export interface ICompilerApi {
onCurrentFileChanged: (fileName: string) => void onCurrentFileChanged: (fileName: string) => void
// onResetResults: () => void, // onResetResults: () => void,
onSetWorkspace: (workspace: any) => void onSetWorkspace: (isLocalhost: boolean, workspaceName: string) => void
onNoFileSelected: () => void onNoFileSelected: () => void
onCompilationFinished: (contractsDetails: any, contractMap: any) => void onCompilationFinished: (contractsDetails: any, contractMap: any) => void
onSessionSwitched: () => void onSessionSwitched: () => void

@ -46,3 +46,9 @@ export function getValidLanguage (val: string): Language {
} }
return null return null
} }
export function compilerInputForConfigFile(sources: Source, opts)
{
opts.sources = sources
return JSON.stringify(opts)
}

@ -2,7 +2,7 @@
import { update } from 'solc/abi' import { update } from 'solc/abi'
import * as webworkify from 'webworkify-webpack' import * as webworkify from 'webworkify-webpack'
import compilerInput from './compiler-input' import compilerInput, { compilerInputForConfigFile } from './compiler-input'
import EventManager from '../lib/eventManager' import EventManager from '../lib/eventManager'
import txHelper from './helper' import txHelper from './helper'
import { import {
@ -31,6 +31,8 @@ export class Compiler {
language: 'Solidity', language: 'Solidity',
compilationStartTime: null, compilationStartTime: null,
target: null, target: null,
useFileConfiguration: false,
configFileContent: '',
lastCompilationResult: { lastCompilationResult: {
data: null, data: null,
source: null source: null
@ -111,11 +113,17 @@ export class Compiler {
return { error: 'Deferred import' } return { error: 'Deferred import' }
} }
let result: CompilationResult = {} let result: CompilationResult = {}
let input let input = ""
try { try {
if (source && source.sources) { if (source && source.sources) {
const { optimize, runs, evmVersion, language } = this.state const { optimize, runs, evmVersion, language, useFileConfiguration, configFileContent } = this.state
input = compilerInput(source.sources, { optimize, runs, evmVersion, language })
if (useFileConfiguration) {
input = compilerInputForConfigFile(source.sources, JSON.parse(configFileContent))
} else {
input = compilerInput(source.sources, { optimize, runs, evmVersion, language })
}
result = JSON.parse(compiler.compile(input, { import: missingInputsCallback })) result = JSON.parse(compiler.compile(input, { import: missingInputsCallback }))
} }
} catch (exception) { } catch (exception) {
@ -183,11 +191,17 @@ export class Compiler {
return { error: 'Deferred import' } return { error: 'Deferred import' }
} }
let result: CompilationResult = {} let result: CompilationResult = {}
let input: string let input = ""
try { try {
if (source && source.sources) { if (source && source.sources) {
const { optimize, runs, evmVersion, language } = this.state const { optimize, runs, evmVersion, language, useFileConfiguration, configFileContent } = this.state
input = compilerInput(source.sources, { optimize, runs, evmVersion, language })
if (useFileConfiguration) {
input = compilerInputForConfigFile(source.sources, JSON.parse(configFileContent))
} else {
input = compilerInput(source.sources, { optimize, runs, evmVersion, language })
}
result = JSON.parse(remoteCompiler.compile(input, { import: missingInputsCallback })) result = JSON.parse(remoteCompiler.compile(input, { import: missingInputsCallback }))
} }
} catch (exception) { } catch (exception) {
@ -289,12 +303,26 @@ export class Compiler {
this.state.compileJSON = (source: SourceWithTarget) => { this.state.compileJSON = (source: SourceWithTarget) => {
if (source && source.sources) { if (source && source.sources) {
const { optimize, runs, evmVersion, language } = this.state const { optimize, runs, evmVersion, language, useFileConfiguration, configFileContent } = this.state
jobs.push({ sources: source }) jobs.push({ sources: source })
let input = ""
try {
if (useFileConfiguration) {
input = compilerInputForConfigFile(source.sources, JSON.parse(configFileContent))
} else {
input = compilerInput(source.sources, { optimize, runs, evmVersion, language })
}
} catch (exception) {
this.onCompilationFinished({ error: { formattedMessage: exception.message } }, [], source, "", this.state.currentVersion)
return
}
this.state.worker.postMessage({ this.state.worker.postMessage({
cmd: 'compile', cmd: 'compile',
job: jobs.length - 1, job: jobs.length - 1,
input: compilerInput(source.sources, { optimize, runs, evmVersion, language }) input: input
}) })
} }
} }

@ -164,6 +164,8 @@ export interface CompilerState {
language: Language, language: Language,
compilationStartTime: number| null, compilationStartTime: number| null,
target: string | null, target: string | null,
useFileConfiguration: boolean,
configFileContent: string,
lastCompilationResult: { lastCompilationResult: {
data: CompilationResult | null, data: CompilationResult | null,
source: SourceWithTarget | null | undefined source: SourceWithTarget | null | undefined

@ -56,15 +56,15 @@ export const joinPath = (...paths) => {
export const getPathIcon = (path: string) => { export const getPathIcon = (path: string) => {
return path.endsWith('.txt') return path.endsWith('.txt')
? 'far fa-file-alt' : path.endsWith('.md') ? 'far fa-file-alt' : path.endsWith('.md')
? 'far fa-file-alt' : path.endsWith('.sol') ? 'fab fa-markdown' : path.endsWith('.sol')
? 'fak fa-solidity-mono' : path.endsWith('.js') ? 'fak fa-solidity-mono' : path.endsWith('.js')
? 'fab fa-js' : path.endsWith('.json') ? 'fab fa-js' : path.endsWith('.json')
? 'fas fa-brackets-curly' : path.endsWith('.vy') ? 'small fas fa-brackets-curly' : path.endsWith('.vy')
? 'fak fa-vyper-mono' : path.endsWith('.lex') ? 'small fak fa-vyper2' : path.endsWith('.lex')
? 'fak fa-lexon' : path.endsWith('ts') ? 'fak fa-lexon' : path.endsWith('ts')
? 'fad fa-brackets-curly' : path.endsWith('.contract') ? 'small fak fa-ts-logo' : path.endsWith('.tsc')
? 'fab fa-ethereum' : path.endsWith('.cairo') ? 'fad fa-brackets-curly' : path.endsWith('.cairo')
? 'fab fa-ethereum' : 'far fa-file' // TODO: add cairo icon ? 'small fak fa-cairo' : 'far fa-file'
} }
export const isNumeric = (value) => { export const isNumeric = (value) => {

@ -11,8 +11,8 @@ export function InstanceContainerUI (props: InstanceContainerProps) {
} }
return ( return (
<div className="udapp_instanceContainer border-0 list-group-item"> <div className="udapp_instanceContainer mt-3 border-0 list-group-item">
<div className="d-flex justify-content-between align-items-center pl-2 ml-1 mb-2" <label className="udapp_deployedContracts d-flex justify-content-between align-items-center pl-2 mb-0"
title="Autogenerated generic user interfaces for interaction with deployed contracts"> title="Autogenerated generic user interfaces for interaction with deployed contracts">
Deployed Contracts Deployed Contracts
{ instanceList.length > 0 { instanceList.length > 0
@ -20,7 +20,7 @@ export function InstanceContainerUI (props: InstanceContainerProps) {
title="Clear instances list and reset recorder" aria-hidden="true"> title="Clear instances list and reset recorder" aria-hidden="true">
</i> : null </i> : null
} }
</div> </label>
{ instanceList.length > 0 { instanceList.length > 0
? <div> { props.instances.instanceList.map((instance, index) => { ? <div> { props.instances.instanceList.map((instance, index) => {
return <UniversalDappUI return <UniversalDappUI

@ -1,27 +1,9 @@
// eslint-disable-next-line no-use-before-define // eslint-disable-next-line no-use-before-define
import React from 'react' import React, {useState} from 'react'
import { TreeView, TreeViewItem } from '@remix-ui/tree-view'
import { RecorderProps } from '../types' import { RecorderProps } from '../types'
export function RecorderUI (props: RecorderProps) { export function RecorderUI (props: RecorderProps) {
const card = (title: string, recorderCount: number) => { const [toggleExpander, setToggleExpander] = useState<boolean>(false)
return (
<div className="d-flex justify-content-between align-items-center" onClick={() => {}}>
<div className="pr-1 d-flex flex-row">
<div>{title}</div>
<div>
<div className="d-flex flex-column">
<div className="ml-2 badge badge-pill badge-primary" title="The number of recorded transactions">{recorderCount}</div>
</div>
</div>
</div>
<div>
<div><i className="udapp_arrow fas fa-angle-down" data-id="udapp_arrow"></i></div>
</div>
</div>
)
}
const triggerRecordButton = () => { const triggerRecordButton = () => {
props.storeScenario(props.scenarioPrompt) props.storeScenario(props.scenarioPrompt)
} }
@ -30,24 +12,35 @@ export function RecorderUI (props: RecorderProps) {
props.runCurrentScenario(props.gasEstimationPrompt, props.passphrasePrompt, props.mainnetPrompt, props.logBuilder) props.runCurrentScenario(props.gasEstimationPrompt, props.passphrasePrompt, props.mainnetPrompt, props.logBuilder)
} }
const toggleClass = () => {
setToggleExpander(!toggleExpander)
}
return ( return (
<div className="udapp_cardContainer list-group-item border-0"> <div className="udapp_cardContainer list-group-item border border-bottom">
<TreeView> <div className="udapp_recorderSection d-flex justify-content-between" onClick={toggleClass}>
<TreeViewItem label={card('Transactions recorded', props.count)} showIcon={false} labelClass="ml-n1"> <div className="d-flex">
<div className="d-flex flex-column"> <label className="mt-1 udapp_recorderSectionLabel">Transactions recorded</label>
<div className="udapp_recorderDescription mt-2"> <div className="ml-2 mb-2 badge badge-pill badge-primary" title="The number of recorded transactions">{props.count}</div>
All transactions (deployed contracts and function executions) in this environment can be saved and replayed in </div>
another environment. e.g Transactions created in Javascript VM can be replayed in the Injected Web3. <div>
</div> <span data-id='udappRecorderTitleExpander' onClick={toggleClass}>
<div className="udapp_transactionActions"> <i className={!toggleExpander ? 'fas fa-angle-right' : 'fas fa-angle-down'} aria-hidden="true"></i>
<i className="fas fa-save savetransaction udapp_recorder udapp_icon" </span>
onClick={triggerRecordButton} title="Save Transactions" aria-hidden="true"> </div>
</i> </div>
<i className="fas fa-play runtransaction udapp_runTxs udapp_icon" title="Run Transactions" data-id="runtransaction" aria-hidden="true" onClick={handleClickRunButton}></i> <div className={`border-bottom flex-column ${toggleExpander ? "d-flex" : "d-none"}`}>
</div> <div className="p-2 mt-2">
</div> All transactions (deployed contracts and function executions) can be saved and replayed in
</TreeViewItem> another environment. e.g Transactions created in Javascript VM can be replayed in the Injected Web3.
</TreeView> </div>
<div className="mb-2 udapp_transactionActions">
<i className="fas fa-save savetransaction udapp_recorder udapp_icon"
onClick={triggerRecordButton} title="Save Transactions" aria-hidden="true">
</i>
<i className="fas fa-play runtransaction udapp_runTxs udapp_icon" title="Run Transactions" data-id="runtransaction" aria-hidden="true" onClick={handleClickRunButton}></i>
</div>
</div>
</div> </div>
) )
} }

@ -2,11 +2,4 @@
padding : 0 24px 16px; padding : 0 24px 16px;
margin : 0; margin : 0;
background : none; background : none;
}
.udapp_arrow {
font-weight : bold;
cursor : pointer;
font-size : 14px;
}
.udapp_arrow:hover {
} }

@ -60,6 +60,9 @@
text-align: center; text-align: center;
padding: 0 14px 16px; padding: 0 14px 16px;
} }
.udapp_deployedContracts {
font-size: 1rem;
}
.udapp_pendingTxsContainer { .udapp_pendingTxsContainer {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
@ -70,9 +73,6 @@
.udapp_container { .udapp_container {
padding: 0 24px 16px; padding: 0 24px 16px;
} }
.udapp_recorderDescription {
margin: 0 15px 15px 0;
}
.udapp_contractNames { .udapp_contractNames {
width: 100%; width: 100%;
border: 1px solid border: 1px solid
@ -121,7 +121,14 @@
.udapp_ataddressinput { .udapp_ataddressinput {
padding: .25rem; padding: .25rem;
} }
.udapp_create { .udapp_recorderSection:hover {
cursor: pointer;
}.udapp_recorderSectionLabel:hover {
cursor: pointer;
}
.udapp_recorderSectionLabel {
cursor: pointer;
font-size: 1rem;
} }
.udapp_input { .udapp_input {
font-size: 10px !important; font-size: 10px !important;

@ -227,7 +227,8 @@ export interface ContractGUIProps {
clickCallBack: (inputs: { name: string, type: string }[], input: string) => void, clickCallBack: (inputs: { name: string, type: string }[], input: string) => void,
widthClass?: string, widthClass?: string,
evmBC: any, evmBC: any,
lookupOnly: boolean lookupOnly: boolean,
disabled?: boolean
} }
export interface MainnetProps { export interface MainnetProps {
network: Network, network: Network,

@ -9,6 +9,7 @@ import { resetEditorMode, listenToEvents } from './actions/compiler'
import { OverlayTrigger, Tooltip } from 'react-bootstrap' // eslint-disable-line import { OverlayTrigger, Tooltip } from 'react-bootstrap' // eslint-disable-line
import { getValidLanguage } from '@remix-project/remix-solidity' import { getValidLanguage } from '@remix-project/remix-solidity'
import { CopyToClipboard } from '@remix-ui/clipboard' import { CopyToClipboard } from '@remix-ui/clipboard'
import { configFileContent } from './compilerConfiguration'
import './css/style.css' import './css/style.css'
@ -21,10 +22,12 @@ declare global {
const _paq = window._paq = window._paq || [] //eslint-disable-line const _paq = window._paq = window._paq || [] //eslint-disable-line
export const CompilerContainer = (props: CompilerContainerProps) => { export const CompilerContainer = (props: CompilerContainerProps) => {
const { api, compileTabLogic, tooltip, modal, compiledFileName, updateCurrentVersion, configurationSettings, isHardhatProject, isTruffleProject } = props // eslint-disable-line const { api, compileTabLogic, tooltip, modal, compiledFileName, updateCurrentVersion, configurationSettings, isHardhatProject, isTruffleProject, workspaceName } = props // eslint-disable-line
const [state, setState] = useState({ const [state, setState] = useState({
hideWarnings: false, hideWarnings: false,
autoCompile: false, autoCompile: false,
configFilePath: "compiler_config.json",
useFileConfiguration: false,
matomoAutocompileOnce: true, matomoAutocompileOnce: true,
optimize: false, optimize: false,
compileTimeout: null, compileTimeout: null,
@ -39,13 +42,43 @@ export const CompilerContainer = (props: CompilerContainerProps) => {
language: 'Solidity', language: 'Solidity',
evmVersion: '' evmVersion: ''
}) })
const [showFilePathInput, setShowFilePathInput] = useState<boolean>(false)
const [toggleExpander, setToggleExpander] = useState<boolean>(false)
const [disableCompileButton, setDisableCompileButton] = useState<boolean>(false) const [disableCompileButton, setDisableCompileButton] = useState<boolean>(false)
const compileIcon = useRef(null) const compileIcon = useRef(null)
const promptMessageInput = useRef(null) const promptMessageInput = useRef(null)
const configFilePathInput = useRef(null)
const [hhCompilation, sethhCompilation] = useState(false) const [hhCompilation, sethhCompilation] = useState(false)
const [truffleCompilation, setTruffleCompilation] = useState(false) const [truffleCompilation, setTruffleCompilation] = useState(false)
const [compilerContainer, dispatch] = useReducer(compilerReducer, compilerInitialState) const [compilerContainer, dispatch] = useReducer(compilerReducer, compilerInitialState)
useEffect(() => {
api.setAppParameter('configFilePath', "/compiler_config.json")
api.fileExists("/compiler_config.json").then((exists) => {
if (!exists) createNewConfigFile()
else {
// what to do? discuss
}
})
api.setAppParameter('configFilePath', "/compiler_config.json")
setShowFilePathInput(false)
}, [workspaceName])
useEffect(() => {
const listener = (event) => {
if (configFilePathInput.current !== event.target) {
setShowFilePathInput(false)
return;
}
};
document.addEventListener("mousedown", listener);
document.addEventListener("touchstart", listener);
return () => {
document.removeEventListener("mousedown", listener);
document.removeEventListener("touchstart", listener);
}
})
useEffect(() => { useEffect(() => {
fetchAllVersion((allversions, selectedVersion, isURL) => { fetchAllVersion((allversions, selectedVersion, isURL) => {
setState(prevState => { setState(prevState => {
@ -72,6 +105,10 @@ export const CompilerContainer = (props: CompilerContainerProps) => {
const autocompile = await api.getAppParameter('autoCompile') as boolean || false const autocompile = await api.getAppParameter('autoCompile') as boolean || false
const hideWarnings = await api.getAppParameter('hideWarnings') as boolean || false const hideWarnings = await api.getAppParameter('hideWarnings') as boolean || false
const includeNightlies = await api.getAppParameter('includeNightlies') as boolean || false const includeNightlies = await api.getAppParameter('includeNightlies') as boolean || false
const useFileConfiguration = await api.getAppParameter('useFileConfiguration') as boolean || false
let configFilePath = await api.getAppParameter('configFilePath')
if (!configFilePath || configFilePath == '') configFilePath = "/compiler_config.json"
setState(prevState => { setState(prevState => {
const params = api.getCompilerParameters() const params = api.getCompilerParameters()
const optimize = params.optimize const optimize = params.optimize
@ -84,6 +121,8 @@ export const CompilerContainer = (props: CompilerContainerProps) => {
hideWarnings: hideWarnings, hideWarnings: hideWarnings,
autoCompile: autocompile, autoCompile: autocompile,
includeNightlies: includeNightlies, includeNightlies: includeNightlies,
useFileConfiguration: useFileConfiguration,
configFilePath: configFilePath,
optimize: optimize, optimize: optimize,
runs: runs, runs: runs,
evmVersion: (evmVersion !== null) && (evmVersion !== 'null') && (evmVersion !== undefined) && (evmVersion !== 'undefined') ? evmVersion : 'default', evmVersion: (evmVersion !== null) && (evmVersion !== 'null') && (evmVersion !== undefined) && (evmVersion !== 'undefined') ? evmVersion : 'default',
@ -140,12 +179,67 @@ export const CompilerContainer = (props: CompilerContainerProps) => {
} }
}, [compilerContainer.editor.mode]) }, [compilerContainer.editor.mode])
useEffect(() => {
compileTabLogic.setUseFileConfiguration(state.useFileConfiguration)
if (state.useFileConfiguration) compileTabLogic.setConfigFilePath(state.configFilePath)
}, [state.useFileConfiguration])
useEffect(() => { useEffect(() => {
if (configurationSettings) { if (configurationSettings) {
setConfiguration(configurationSettings) setConfiguration(configurationSettings)
} }
}, [configurationSettings]) }, [configurationSettings])
const toggleConfigType = () => {
setState(prevState => {
api.setAppParameter('useFileConfiguration', !state.useFileConfiguration)
return { ...prevState, useFileConfiguration: !state.useFileConfiguration }
})
}
const openFile = async () => {
api.open(state.configFilePath)
}
const createNewConfigFile = async () => {
let filePath = configFilePathInput.current && configFilePathInput.current.value !== '' ? configFilePathInput.current.value : state.configFilePath
if (!filePath.endsWith('.json')) filePath = filePath + '.json'
await api.writeFile(filePath, configFileContent)
api.setAppParameter('configFilePath', filePath)
setState(prevState => {
return { ...prevState, configFilePath: filePath }
})
compileTabLogic.setConfigFilePath(filePath)
setShowFilePathInput(false)
}
const handleConfigPathChange = async () => {
if (configFilePathInput.current.value !== '') {
if (!configFilePathInput.current.value.endsWith('.json')) configFilePathInput.current.value += '.json'
if (await api.fileExists(configFilePathInput.current.value)) {
api.setAppParameter('configFilePath', configFilePathInput.current.value)
setState(prevState => {
return { ...prevState, configFilePath: configFilePathInput.current.value }
})
compileTabLogic.setConfigFilePath(configFilePathInput.current.value)
setShowFilePathInput(false)
} else {
modal(
'New configuration file', `The file "${configFilePathInput.current.value}" you entered does not exist. Do you want to create a new one?`,
'Create',
async () => await createNewConfigFile(),
'Cancel',
() => {
setShowFilePathInput(false)
}
)
}
}
}
const _retrieveVersion = (version?) => { const _retrieveVersion = (version?) => {
if (!version) version = state.selectedVersion if (!version) version = state.selectedVersion
if (version === 'builtin') version = state.defaultVersion if (version === 'builtin') version = state.defaultVersion
@ -537,14 +631,18 @@ export const CompilerContainer = (props: CompilerContainerProps) => {
onChangeRuns(settings.runs) onChangeRuns(settings.runs)
} }
const toggleConfigurations = () => {
setToggleExpander(!toggleExpander)
}
return ( return (
<section> <section>
<article> <article>
<header className='remixui_compilerSection border-bottom'> <div className='pt-0 remixui_compilerSection'>
<div className="mb-2"> <div className="mb-1">
<label className="remixui_compilerLabel form-check-label" htmlFor="versionSelector"> <label className="remixui_compilerLabel form-check-label" htmlFor="versionSelector">
Compiler Compiler
<button className="far fa-plus-square border-0 p-0 mx-2 btn-sm" onClick={promptCompiler} title="Add a custom compiler with URL"></button> <button className="far fa-plus btn-light border-0 p-0 mx-2 btn-sm" onClick={promptCompiler} title="Add a custom compiler with URL"></button>
</label> </label>
<select value={ state.selectedVersion || state.defaultVersion } onChange={(e) => handleLoadVersion(e.target.value) } className="custom-select" id="versionSelector" disabled={state.allversions.length <= 0}> <select value={ state.selectedVersion || state.defaultVersion } onChange={(e) => handleLoadVersion(e.target.value) } className="custom-select" id="versionSelector" disabled={state.allversions.length <= 0}>
{ state.allversions.length <= 0 && <option disabled data-id={state.selectedVersion === state.defaultVersion ? 'selected' : ''}>{ state.defaultVersion }</option> } { state.allversions.length <= 0 && <option disabled data-id={state.selectedVersion === state.defaultVersion ? 'selected' : ''}>{ state.defaultVersion }</option> }
@ -558,50 +656,17 @@ export const CompilerContainer = (props: CompilerContainerProps) => {
} }
</select> </select>
</div> </div>
<div className="mb-2 remixui_nightlyBuilds custom-control custom-checkbox"> <div className="mb-2 flex-row-reverse remixui_nightlyBuilds custom-control custom-checkbox">
<input className="mr-2 custom-control-input" id="nightlies" type="checkbox" onChange={handleNightliesChange} checked={state.includeNightlies} /> <input className="mr-2 custom-control-input" id="nightlies" type="checkbox" onChange={handleNightliesChange} checked={state.includeNightlies} />
<label htmlFor="nightlies" data-id="compilerNightliesBuild" className="form-check-label custom-control-label">Include nightly builds</label> <label htmlFor="nightlies" data-id="compilerNightliesBuild" className="form-check-label custom-control-label">Include nightly builds</label>
</div> </div>
<div className="mb-2"> <div className="mt-2 remixui_compilerConfig custom-control custom-checkbox">
<label className="remixui_compilerLabel form-check-label" htmlFor="compilierLanguageSelector">Language</label> <input className="remixui_autocompile custom-control-input" type="checkbox" onChange={handleAutoCompile} data-id="compilerContainerAutoCompile" id="autoCompile" title="Auto compile" checked={state.autoCompile} />
<select onChange={(e) => handleLanguageChange(e.target.value)} value={state.language} className="custom-select" id="compilierLanguageSelector" title="Available since v0.5.7"> <label className="form-check-label custom-control-label" htmlFor="autoCompile">Auto compile</label>
<option data-id={state.language === 'Solidity' ? 'selected' : ''} value='Solidity'>Solidity</option>
<option data-id={state.language === 'Yul' ? 'selected' : ''} value='Yul'>Yul</option>
</select>
</div> </div>
<div className="mb-2"> <div className="mt-1 mb-2 remixui_compilerConfig custom-control custom-checkbox">
<label className="remixui_compilerLabel form-check-label" htmlFor="evmVersionSelector">EVM Version</label> <input className="remixui_autocompile custom-control-input" onChange={handleHideWarningsChange} id="hideWarningsBox" type="checkbox" title="Hide warnings" checked={state.hideWarnings} />
<select value={state.evmVersion} onChange={(e) => handleEvmVersionChange(e.target.value)} className="custom-select" id="evmVersionSelector"> <label className="form-check-label custom-control-label" htmlFor="hideWarningsBox">Hide warnings</label>
{compileTabLogic.evmVersions.map((version, index) => (<option key={index} data-id={state.evmVersion === version ? 'selected' : ''} value={version}>{version}</option>))}
</select>
</div>
<div className="mt-3">
<p className="mt-2 remixui_compilerLabel">Compiler Configuration</p>
<div className="mt-2 remixui_compilerConfig custom-control custom-checkbox">
<input className="remixui_autocompile custom-control-input" type="checkbox" onChange={handleAutoCompile} data-id="compilerContainerAutoCompile" id="autoCompile" title="Auto compile" checked={state.autoCompile} />
<label className="form-check-label custom-control-label" htmlFor="autoCompile">Auto compile</label>
</div>
<div className="mt-2 remixui_compilerConfig custom-control custom-checkbox">
<div className="justify-content-between align-items-center d-flex">
<input onChange={(e) => { handleOptimizeChange(e.target.checked) }} className="custom-control-input" id="optimize" type="checkbox" checked={state.optimize} />
<label className="form-check-label custom-control-label" htmlFor="optimize">Enable optimization</label>
<input
min="1"
className="custom-select ml-2 remixui_runs"
id="runs"
placeholder="200"
value={state.runs}
type="number"
title="Estimated number of times each opcode of the deployed code will be executed across the life-time of the contract."
onChange={(e) => onChangeRuns(e.target.value)}
disabled={!state.optimize}
/>
</div>
</div>
<div className="mt-2 remixui_compilerConfig custom-control custom-checkbox">
<input className="remixui_autocompile custom-control-input" onChange={handleHideWarningsChange} id="hideWarningsBox" type="checkbox" title="Hide warnings" checked={state.hideWarnings} />
<label className="form-check-label custom-control-label" htmlFor="hideWarningsBox">Hide warnings</label>
</div>
</div> </div>
{ {
isHardhatProject && isHardhatProject &&
@ -635,7 +700,81 @@ export const CompilerContainer = (props: CompilerContainerProps) => {
</a> </a>
</div> </div>
} }
</div>
<div className="d-flex px-4 remixui_compilerConfigSection justify-content-between" onClick={toggleConfigurations}>
<div className="d-flex">
<label className="mt-1 remixui_compilerConfigSection">Advanced Configurations</label>
</div>
<div> <div>
<span data-id='scConfigExpander' onClick={toggleConfigurations}>
<i className={!toggleExpander ? 'fas fa-angle-right' : 'fas fa-angle-down'} aria-hidden="true"></i>
</span>
</div>
</div>
<div className={`px-4 pb-4 border-bottom flex-column ${toggleExpander ? "d-flex" : "d-none"}`}>
<div className="d-flex pb-1 remixui_compilerConfig custom-control custom-radio">
<input className="custom-control-input" type="radio" name="configradio" value="manual" onChange={toggleConfigType} checked={!state.useFileConfiguration} id="scManualConfig" />
<label className="form-check-label custom-control-label" htmlFor="scManualConfig">Compiler configuration</label>
</div>
<div className={`flex-column 'd-flex'}`}>
<div className="mb-2 ml-4">
<label className="remixui_compilerLabel form-check-label" htmlFor="compilierLanguageSelector">Language</label>
<select onChange={(e) => handleLanguageChange(e.target.value)} disabled={state.useFileConfiguration} value={state.language} className="custom-select" id="compilierLanguageSelector" title="Available since v0.5.7">
<option data-id={state.language === 'Solidity' ? 'selected' : ''} value='Solidity'>Solidity</option>
<option data-id={state.language === 'Yul' ? 'selected' : ''} value='Yul'>Yul</option>
</select>
</div>
<div className="mb-2 ml-4">
<label className="remixui_compilerLabel form-check-label" htmlFor="evmVersionSelector">EVM Version</label>
<select value={state.evmVersion} onChange={(e) => handleEvmVersionChange(e.target.value)} disabled={state.useFileConfiguration} className="custom-select" id="evmVersionSelector">
{compileTabLogic.evmVersions.map((version, index) => (<option key={index} data-id={state.evmVersion === version ? 'selected' : ''} value={version}>{version}</option>))}
</select>
</div>
<div className="mt-1 mt-3 border-dark pb-3 ml-4 remixui_compilerConfig custom-control custom-checkbox">
<div className="justify-content-between align-items-center d-flex">
<input onChange={(e) => { handleOptimizeChange(e.target.checked) }} disabled={state.useFileConfiguration} className="custom-control-input" id="optimize" type="checkbox" checked={state.optimize} />
<label className="form-check-label custom-control-label" htmlFor="optimize">Enable optimization</label>
<input
min="1"
className="custom-select ml-2 remixui_runs"
id="runs"
placeholder="200"
value={state.runs}
type="number"
title="Estimated number of times each opcode of the deployed code will be executed across the life-time of the contract."
onChange={(e) => onChangeRuns(e.target.value)}
disabled={!state.optimize || state.useFileConfiguration}
/>
</div>
</div>
</div>
<div className="d-flex pb-1 remixui_compilerConfig custom-control custom-radio">
<input className="custom-control-input" type="radio" name="configradio" value="file" onChange={toggleConfigType} checked={state.useFileConfiguration} id="scFileConfig" />
<label className="form-check-label custom-control-label" htmlFor="scFileConfig">Use configuration file</label>
</div>
<div className={`pt-2 ml-4 ml-2 align-items-start justify-content-between d-flex`}>
{ (!showFilePathInput && state.useFileConfiguration) && <span
title="Click to open the config file."
onClick={openFile}
className="py-2 text-primary remixui_compilerConfigPath"
>{state.configFilePath}</span> }
{ (!showFilePathInput&& !state.useFileConfiguration) && <span className="py-2 text-secondary">{state.configFilePath}</span> }
<input
ref={configFilePathInput}
className={`py-0 my-0 form-control ${showFilePathInput ? "d-flex" : "d-none"}`}
placeholder={"Enter the new path"}
title="If the file you entered does not exist you will be able to create one in the next step."
disabled={!state.useFileConfiguration}
onKeyPress={event => {
if (event.key === 'Enter') {
handleConfigPathChange()
}
}}
/>
{ !showFilePathInput && <button disabled={!state.useFileConfiguration} className="btn-secondary" onClick={() => {setShowFilePathInput(true)}}>Change</button> }
</div>
</div>
<div className="px-4">
<button id="compileBtn" data-id="compilerContainerCompileBtn" className="btn btn-primary btn-block d-block w-100 text-break remixui_disabled mb-1 mt-3" onClick={compile} disabled={disableCompileButton}> <button id="compileBtn" data-id="compilerContainerCompileBtn" className="btn btn-primary btn-block d-block w-100 text-break remixui_disabled mb-1 mt-3" onClick={compile} disabled={disableCompileButton}>
<OverlayTrigger overlay={ <OverlayTrigger overlay={
<Tooltip id="overlay-tooltip-compile"> <Tooltip id="overlay-tooltip-compile">
@ -665,33 +804,32 @@ export const CompilerContainer = (props: CompilerContainerProps) => {
</OverlayTrigger> </OverlayTrigger>
</button> </button>
<OverlayTrigger overlay={ <OverlayTrigger overlay={
<Tooltip id="overlay-tooltip-compile-run-doc"> <Tooltip id="overlay-tooltip-compile-run-doc">
<div className="text-left p-2"> <div className="text-left p-2">
<div>Choose the script to execute right after compilation by adding the `dev-run-script` natspec tag, as in:</div> <div>Choose the script to execute right after compilation by adding the `dev-run-script` natspec tag, as in:</div>
<pre> <pre>
<code> <code>
/**<br /> /**<br />
* @title ContractName<br /> * @title ContractName<br />
* @dev ContractDescription<br /> * @dev ContractDescription<br />
* @custom:dev-run-script file_path<br /> * @custom:dev-run-script file_path<br />
*/<br /> */<br />
contract ContractName {'{}'}<br /> contract ContractName {'{}'}<br />
</code> </code>
</pre> </pre>
Click to know more Click to know more
</div> </div>
</Tooltip> </Tooltip>
}> }>
<a href="https://remix-ide.readthedocs.io/en/latest/running_js_scripts.html#compile-a-contract-and-run-a-script-on-the-fly" target="_blank" ><i className="pl-2 ml-2 mt-3 mb-1 fas fa-info text-dark"></i></a> <a href="https://remix-ide.readthedocs.io/en/latest/running_js_scripts.html#compile-a-contract-and-run-a-script-on-the-fly" target="_blank" ><i className="pl-2 ml-2 mt-3 mb-1 fas fa-info text-dark"></i></a>
</OverlayTrigger> </OverlayTrigger>
<CopyToClipboard tip="Copy tag to use in contract NatSpec" getContent={() => '@custom:dev-run-script file_path'} direction='top'> <CopyToClipboard tip="Copy tag to use in contract NatSpec" getContent={() => '@custom:dev-run-script file_path'} direction='top'>
<button className="btn remixui_copyButton ml-2 mt-3 mb-1 text-dark"> <button className="btn remixui_copyButton ml-2 mt-3 mb-1 text-dark">
<i className="remixui_copyIcon far fa-copy" aria-hidden="true"></i> <i className="remixui_copyIcon far fa-copy" aria-hidden="true"></i>
</button> </button>
</CopyToClipboard> </CopyToClipboard>
</div> </div>
</div> </div>
</header>
</article> </article>
</section> </section>
) )

@ -0,0 +1,18 @@
export const configFileContent = `
{
"language": "Solidity",
"settings": {
"optimizer": {
"enabled": true,
"runs": 200
},
"outputSelection": {
"*": {
"": ["ast"],
"*": ["abi", "metadata", "devdoc", "userdoc", "storageLayout", "evm.legacyAssembly", "evm.bytecode", "evm.deployedBytecode", "evm.methodIdentifiers", "evm.gasEstimates", "evm.assembly"]
}
},
"evmVersion": "byzantium"
}
}
`

@ -72,6 +72,15 @@
.remixui_compilerSection { .remixui_compilerSection {
padding: 12px 24px 16px; padding: 12px 24px 16px;
} }
.remixui_compilerConfigSection:hover {
cursor: pointer;
}
.remixui_compilerConfigSection {
font-size: 1rem;
}
.remixui_compilerConfigPath {
cursor: pointer;
}
.remixui_compilerLabel { .remixui_compilerLabel {
margin-bottom: 2px; margin-bottom: 2px;
font-size: 11px; font-size: 11px;

@ -18,6 +18,8 @@ export class CompileTabLogic {
public compilerImport public compilerImport
public event public event
public evmVersions: Array<string> public evmVersions: Array<string>
public useFileConfiguration: boolean
public configFilePath: string
constructor (public api: ICompilerApi, public contentImport) { constructor (public api: ICompilerApi, public contentImport) {
this.event = new EventEmitter() this.event = new EventEmitter()
@ -52,12 +54,21 @@ export class CompileTabLogic {
} }
} }
setOptimize (newOptimizeValue) { setOptimize (newOptimizeValue: boolean) {
this.optimize = newOptimizeValue this.optimize = newOptimizeValue
this.api.setCompilerParameters({ optimize: this.optimize }) this.api.setCompilerParameters({ optimize: this.optimize })
this.compiler.set('optimize', this.optimize) this.compiler.set('optimize', this.optimize)
} }
setUseFileConfiguration (useFileConfiguration: boolean) {
this.useFileConfiguration = useFileConfiguration
this.compiler.set('useFileConfiguration', useFileConfiguration)
}
setConfigFilePath (path) {
this.configFilePath = path
}
setRuns (runs) { setRuns (runs) {
this.runs = runs this.runs = runs
this.api.setCompilerParameters({ runs: this.runs }) this.api.setCompilerParameters({ runs: this.runs })
@ -95,6 +106,9 @@ export class CompileTabLogic {
const sources = { [target]: { content } } const sources = { [target]: { content } }
this.event.emit('removeAnnotations') this.event.emit('removeAnnotations')
this.event.emit('startingCompilation') this.event.emit('startingCompilation')
this.api.readFile(this.configFilePath).then( contentConfig => {
this.compiler.set('configFileContent', contentConfig)
})
// setTimeout fix the animation on chrome... (animation triggered by 'staringCompilation') // setTimeout fix the animation on chrome... (animation triggered by 'staringCompilation')
setTimeout(() => { this.compiler.compile(sources, target); resolve(true) }, 100) setTimeout(() => { this.compiler.compile(sources, target); resolve(true) }, 100)
}).catch((error) => { }).catch((error) => {

@ -13,6 +13,7 @@ export const SolidityCompiler = (props: SolidityCompilerProps) => {
const [state, setState] = useState({ const [state, setState] = useState({
isHardhatProject: false, isHardhatProject: false,
isTruffleProject: false, isTruffleProject: false,
workspaceName: '',
currentFile, currentFile,
loading: false, loading: false,
compileTabLogic: null, compileTabLogic: null,
@ -63,11 +64,11 @@ export const SolidityCompiler = (props: SolidityCompilerProps) => {
}) })
} }
api.onSetWorkspace = async (isLocalhost: boolean) => { api.onSetWorkspace = async (isLocalhost: boolean, workspaceName: string) => {
const isHardhat = isLocalhost && await compileTabLogic.isHardhatProject() const isHardhat = isLocalhost && await compileTabLogic.isHardhatProject()
const isTruffle = await compileTabLogic.isTruffleProject() const isTruffle = await compileTabLogic.isTruffleProject()
setState(prevState => { setState(prevState => {
return { ...prevState, currentFile, isHardhatProject: isHardhat, isTruffleProject: isTruffle } return { ...prevState, currentFile, isHardhatProject: isHardhat, workspaceName: workspaceName, isTruffleProject: isTruffle }
}) })
} }
@ -150,7 +151,18 @@ export const SolidityCompiler = (props: SolidityCompilerProps) => {
return ( return (
<> <>
<div id="compileTabView"> <div id="compileTabView">
<CompilerContainer api={api} isHardhatProject={state.isHardhatProject} isTruffleProject={state.isTruffleProject} compileTabLogic={compileTabLogic} tooltip={toast} modal={modal} compiledFileName={currentFile} updateCurrentVersion={updateCurrentVersion} configurationSettings={configurationSettings} /> <CompilerContainer
api={api}
isHardhatProject={state.isHardhatProject}
workspaceName={state.workspaceName}
isTruffleProject={state.isTruffleProject}
compileTabLogic={compileTabLogic}
tooltip={toast}
modal={modal}
compiledFileName={currentFile}
updateCurrentVersion={updateCurrentVersion}
configurationSettings={configurationSettings}
/>
{ contractsFile[currentFile] && contractsFile[currentFile].contractsDetails && <ContractSelection api={api} contractsDetails={contractsFile[currentFile].contractsDetails} contractList={contractsFile[currentFile].contractList} modal={modal} /> } { contractsFile[currentFile] && contractsFile[currentFile].contractsDetails && <ContractSelection api={api} contractsDetails={contractsFile[currentFile].contractsDetails} contractList={contractsFile[currentFile].contractList} modal={modal} /> }
{ compileErrors[currentFile] && { compileErrors[currentFile] &&
<div className="remixui_errorBlobs p-4" data-id="compiledErrors"> <div className="remixui_errorBlobs p-4" data-id="compiledErrors">

@ -11,6 +11,7 @@ export interface CompilerContainerProps {
compileTabLogic: CompileTabLogic, compileTabLogic: CompileTabLogic,
isHardhatProject: boolean, isHardhatProject: boolean,
isTruffleProject: boolean, isTruffleProject: boolean,
workspaceName: string,
tooltip: (message: string | JSX.Element) => void, tooltip: (message: string | JSX.Element) => void,
modal: (title: string, message: string | JSX.Element, okLabel: string, okFn: () => void, cancelLabel?: string, cancelFn?: () => void) => void, modal: (title: string, message: string | JSX.Element, okLabel: string, okFn: () => void, cancelLabel?: string, cancelFn?: () => void) => void,
compiledFileName: string, compiledFileName: string,
@ -45,4 +46,3 @@ export interface CompilationDetails {
export interface ContractsFile { export interface ContractsFile {
[currentFile: string]: CompilationDetails [currentFile: string]: CompilationDetails
} }

@ -4,7 +4,7 @@
"jsx": "react", "jsx": "react",
"allowJs": true, "allowJs": true,
"esModuleInterop": true, "esModuleInterop": true,
"allowSyntheticDefaultImports": true "allowSyntheticDefaultImports": true,
}, },
"files": [], "files": [],
"include": [], "include": [],

Loading…
Cancel
Save