Merge branch 'master' into remixd_terminal

pull/1342/head
David Zagi 3 years ago committed by GitHub
commit 77209336e3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 2
      apps/remix-ide-e2e/src/commands/getModalBody.ts
  2. 10
      apps/remix-ide-e2e/src/commands/verifyContracts.ts
  3. 31
      apps/remix-ide-e2e/src/tests/publishContract.test.ts
  4. 49
      apps/remix-ide-e2e/src/tests/url.spec.ts
  5. 7
      apps/remix-ide-e2e/src/tests/usingWebWorker.test.ts
  6. 3
      apps/remix-ide/src/app.js
  7. 15
      apps/remix-ide/src/app/files/dgitProvider.js
  8. 22
      apps/remix-ide/src/app/panels/file-panel.js
  9. 357
      apps/remix-ide/src/app/tabs/compile-tab.js
  10. 2
      apps/remix-ide/src/app/tabs/compileTab/compilerContainer.js
  11. 2
      apps/remix-ide/src/app/ui/copy-to-clipboard.js
  12. 8
      libs/remix-debug/src/code/codeManager.ts
  13. 4
      libs/remix-debug/src/debugger/VmDebugger.ts
  14. 34
      libs/remix-ui/clipboard/src/lib/copy-to-clipboard/copy-to-clipboard.tsx
  15. 36
      libs/remix-ui/debugger-ui/src/lib/vm-debugger/assembly-items.tsx
  16. 8
      libs/remix-ui/debugger-ui/src/reducers/assembly-items.ts
  17. 2
      libs/remix-ui/modal-dialog/src/lib/types/index.ts
  18. 4
      libs/remix-ui/publish-to-storage/.babelrc
  19. 18
      libs/remix-ui/publish-to-storage/.eslintrc
  20. 7
      libs/remix-ui/publish-to-storage/README.md
  21. 1
      libs/remix-ui/publish-to-storage/src/index.ts
  22. 0
      libs/remix-ui/publish-to-storage/src/lib/publish-to-storage.css
  23. 110
      libs/remix-ui/publish-to-storage/src/lib/publish-to-storage.tsx
  24. 109
      libs/remix-ui/publish-to-storage/src/lib/publishOnSwarm.tsx
  25. 117
      libs/remix-ui/publish-to-storage/src/lib/publishToIPFS.tsx
  26. 7
      libs/remix-ui/publish-to-storage/src/lib/types/index.ts
  27. 16
      libs/remix-ui/publish-to-storage/tsconfig.json
  28. 13
      libs/remix-ui/publish-to-storage/tsconfig.lib.json
  29. 4
      libs/remix-ui/renderer/.babelrc
  30. 19
      libs/remix-ui/renderer/.eslintrc
  31. 7
      libs/remix-ui/renderer/README.md
  32. 1
      libs/remix-ui/renderer/src/index.ts
  33. 47
      libs/remix-ui/renderer/src/lib/renderer.css
  34. 127
      libs/remix-ui/renderer/src/lib/renderer.tsx
  35. 16
      libs/remix-ui/renderer/tsconfig.json
  36. 13
      libs/remix-ui/renderer/tsconfig.lib.json
  37. 4
      libs/remix-ui/solidity-compiler/.babelrc
  38. 19
      libs/remix-ui/solidity-compiler/.eslintrc
  39. 7
      libs/remix-ui/solidity-compiler/README.md
  40. 2
      libs/remix-ui/solidity-compiler/src/index.ts
  41. 57
      libs/remix-ui/solidity-compiler/src/lib/actions/compiler.ts
  42. 582
      libs/remix-ui/solidity-compiler/src/lib/compiler-container.tsx
  43. 238
      libs/remix-ui/solidity-compiler/src/lib/contract-selection.tsx
  44. 234
      libs/remix-ui/solidity-compiler/src/lib/css/style.css
  45. 128
      libs/remix-ui/solidity-compiler/src/lib/logic/compileTabLogic.ts
  46. 51
      libs/remix-ui/solidity-compiler/src/lib/logic/compiler-abstract.ts
  47. 19
      libs/remix-ui/solidity-compiler/src/lib/logic/compiler-helpers.ts
  48. 46
      libs/remix-ui/solidity-compiler/src/lib/logic/compiler-utils.ts
  49. 119
      libs/remix-ui/solidity-compiler/src/lib/logic/contract-parser.ts
  50. 5
      libs/remix-ui/solidity-compiler/src/lib/logic/index.ts
  51. 63
      libs/remix-ui/solidity-compiler/src/lib/reducers/compiler.ts
  52. 115
      libs/remix-ui/solidity-compiler/src/lib/solidity-compiler.tsx
  53. 54
      libs/remix-ui/solidity-compiler/src/lib/types/index.ts
  54. 16
      libs/remix-ui/solidity-compiler/tsconfig.json
  55. 13
      libs/remix-ui/solidity-compiler/tsconfig.lib.json
  56. 9
      nx.json
  57. 19
      package-lock.json
  58. 6
      package.json
  59. 15
      tsconfig.json
  60. 54
      workspace.json

@ -3,7 +3,7 @@ import EventEmitter from 'events'
class GetModalBody extends EventEmitter { class GetModalBody extends EventEmitter {
command (this: NightwatchBrowser, callback: (value: string, cb: VoidFunction) => void) { command (this: NightwatchBrowser, callback: (value: string, cb: VoidFunction) => void) {
this.api.waitForElementPresent('.modal-body') this.api.waitForElementVisible('[data-id="modalDialogModalBody"]')
.getText('#modal-dialog', (result) => { .getText('#modal-dialog', (result) => {
console.log(result) console.log(result)
const value = typeof result.value === 'string' ? result.value : null const value = typeof result.value === 'string' ? result.value : null

@ -22,12 +22,12 @@ function verifyContracts (browser: NightwatchBrowser, compiledContractNames: str
if (opts.version) { if (opts.version) {
browser browser
.click('*[data-id="compilation-details"]') .click('*[data-id="compilation-details"]')
.waitForElementVisible('*[data-id="treeViewDivcompiler"]') .waitForElementVisible('*[data-id="treeViewDivtreeViewItemcompiler"]')
.pause(2000) .pause(2000)
.click('*[data-id="treeViewDivcompiler"]') .click('*[data-id="treeViewDivtreeViewItemcompiler"]')
.waitForElementVisible('*[data-id="treeViewLicompiler/version"]') .waitForElementVisible('*[data-id="treeViewLiversion"]')
.assert.containsText('*[data-id="treeViewLicompiler/version"]', `version:\n ${opts.version}`) .assert.containsText('*[data-id="treeViewLiversion"]', `${opts.version}`)
.modalFooterCancelClick() .click('[data-id="workspacesModalDialog-modal-footer-ok-react"]')
.perform(() => { .perform(() => {
done() done()
callback() callback()

@ -20,24 +20,32 @@ module.exports = {
.verifyContracts(['Ballot']) .verifyContracts(['Ballot'])
.click('#publishOnIpfs') .click('#publishOnIpfs')
.pause(8000) .pause(8000)
.getModalBody((value, done) => { .waitForElementVisible('[data-id="publishToStorageModalDialogModalBody-react"]', 60000)
if (value.indexOf('Metadata of "ballot" was published successfully.') === -1) browser.assert.fail('ipfs deploy failed', '', '') .getText('[data-id="publishToStorageModalDialogModalBody-react"]', (result) => {
if (value.indexOf('ipfs://') === -1) browser.assert.fail('ipfs deploy failed', '', '') const value = <string>(result.value)
done()
browser.perform((done) => {
if (value.indexOf('Metadata of "ballot" was published successfully.') === -1) browser.assert.fail('ipfs deploy failed')
done()
})
}) })
.modalFooterOKClick() .click('[data-id="publishToStorage-modal-footer-ok-react"]')
}, },
'Publish on Swarm': '' + function (browser: NightwatchBrowser) { 'Publish on Swarm': '' + function (browser: NightwatchBrowser) {
browser browser
.click('#publishOnSwarm') .click('#publishOnSwarm')
.pause(8000) .pause(8000)
.getModalBody((value, done) => { .getText('[data-id="publishToStorageModalDialogModalBody-react"]', (result) => {
if (value.indexOf('Metadata of "ballot" was successfully.') === -1) browser.assert.fail('swarm deploy failed', '', '') const value = <string>(result.value)
if (value.indexOf('bzz') === -1) browser.assert.fail('swarm deploy failed', '', '')
done() browser.perform((done) => {
if (value.indexOf('Metadata of "ballot" was published successfully.') === -1) browser.assert.fail('swarm deploy failed')
if (value.indexOf('bzz') === -1) browser.assert.fail('swarm deploy failed')
done()
})
}) })
.modalFooterOKClick() .click('[data-id="publishToStorage-modal-footer-ok-react"]')
}, },
'Should publish contract metadata to ipfs on deploy': function (browser: NightwatchBrowser) { 'Should publish contract metadata to ipfs on deploy': function (browser: NightwatchBrowser) {
@ -48,10 +56,11 @@ module.exports = {
.clickLaunchIcon('udapp') .clickLaunchIcon('udapp')
.waitForElementPresent('*[data-id="contractDropdownIpfsCheckbox"]') .waitForElementPresent('*[data-id="contractDropdownIpfsCheckbox"]')
.click('*[data-id="contractDropdownIpfsCheckbox"]') .click('*[data-id="contractDropdownIpfsCheckbox"]')
.waitForElementVisible('*[data-id="Deploy - transact (not payable)"]')
.click('*[data-id="Deploy - transact (not payable)"]') .click('*[data-id="Deploy - transact (not payable)"]')
.pause(8000) .pause(8000)
.getModalBody((value, done) => { .getModalBody((value, done) => {
if (value.indexOf('Metadata of "storage" was published successfully.') === -1) browser.assert.fail('ipfs deploy failed', '', '') if (value.indexOf('Metadata of "storage" was published successfully.') === -1) browser.assert.fail('ipfs deploy failed')
done() done()
}) })
.modalFooterOKClick() .modalFooterOKClick()

@ -17,7 +17,7 @@ module.exports = {
return sources return sources
}, },
'Should load the code from URL params': function (browser: NightwatchBrowser) { 'Should load the code from URL params (code param)': function (browser: NightwatchBrowser) {
browser browser
.pause(5000) .pause(5000)
.url('http://127.0.0.1:8080/#optimize=true&runs=300&evmVersion=istanbul&version=soljson-v0.7.4+commit.3f05b770.js&code=cHJhZ21hIHNvbGlkaXR5ID49MC42LjAgPDAuNy4wOwoKaW1wb3J0ICJodHRwczovL2dpdGh1Yi5jb20vT3BlblplcHBlbGluL29wZW56ZXBwZWxpbi1jb250cmFjdHMvYmxvYi9tYXN0ZXIvY29udHJhY3RzL2FjY2Vzcy9Pd25hYmxlLnNvbCI7Cgpjb250cmFjdCBHZXRQYWlkIGlzIE93bmFibGUgewogIGZ1bmN0aW9uIHdpdGhkcmF3KCkgZXh0ZXJuYWwgb25seU93bmVyIHsKICB9Cn0') .url('http://127.0.0.1:8080/#optimize=true&runs=300&evmVersion=istanbul&version=soljson-v0.7.4+commit.3f05b770.js&code=cHJhZ21hIHNvbGlkaXR5ID49MC42LjAgPDAuNy4wOwoKaW1wb3J0ICJodHRwczovL2dpdGh1Yi5jb20vT3BlblplcHBlbGluL29wZW56ZXBwZWxpbi1jb250cmFjdHMvYmxvYi9tYXN0ZXIvY29udHJhY3RzL2FjY2Vzcy9Pd25hYmxlLnNvbCI7Cgpjb250cmFjdCBHZXRQYWlkIGlzIE93bmFibGUgewogIGZ1bmN0aW9uIHdpdGhkcmF3KCkgZXh0ZXJuYWwgb25seU93bmVyIHsKICB9Cn0')
@ -31,6 +31,43 @@ module.exports = {
}) })
}, },
'Should load the code from URL params (url param)': function (browser: NightwatchBrowser) {
browser
.pause(5000)
.url('http://127.0.0.1:8080/#optimize=true&runs=300&evmVersion=istanbul&version=soljson-v0.7.4+commit.3f05b770.js&url=https://github.com/ethereum/remix-project/blob/master/apps/remix-ide/contracts/app/solidity/mode.sol')
.refresh() // we do one reload for making sure we already have the default workspace
.pause(5000)
.currentWorkspaceIs('code-sample')
.getEditorValue((content) => {
browser.assert.ok(content.indexOf(
'proposals.length = _numProposals;') !== -1,
'url has not been loaded')
})
},
'Should load the code from URL & code params': function (browser: NightwatchBrowser) {
browser
.pause(5000)
.url('http://127.0.0.1:8080/#optimize=true&runs=300&evmVersion=istanbul&version=soljson-v0.7.4+commit.3f05b770.js&url=https://github.com/ethereum/remix-project/blob/master/apps/remix-ide/contracts/app/solidity/mode.sol&code=cHJhZ21hIHNvbGlkaXR5ID49MC42LjAgPDAuNy4wOwoKaW1wb3J0ICJodHRwczovL2dpdGh1Yi5jb20vT3BlblplcHBlbGluL29wZW56ZXBwZWxpbi1jb250cmFjdHMvYmxvYi9tYXN0ZXIvY29udHJhY3RzL2FjY2Vzcy9Pd25hYmxlLnNvbCI7Cgpjb250cmFjdCBHZXRQYWlkIGlzIE93bmFibGUgewogIGZ1bmN0aW9uIHdpdGhkcmF3KCkgZXh0ZXJuYWwgb25seU93bmVyIHsKICB9Cn0')
.refresh() // we do one reload for making sure we already have the default workspace
.pause(5000)
.currentWorkspaceIs('code-sample')
.getEditorValue((content) => {
browser.assert.ok(content.indexOf(
'proposals.length = _numProposals;') !== -1,
'code has not been loaded')
})
.openFile('contract-76747f6e19.sol')
.openFile('ethereum')
.openFile('ethereum/remix-project')
.openFile('ethereum/remix-project/apps')
.openFile('ethereum/remix-project/apps/remix-ide')
.openFile('ethereum/remix-project/apps/remix-ide/contracts')
.openFile('ethereum/remix-project/apps/remix-ide/contracts/app')
.openFile('ethereum/remix-project/apps/remix-ide/contracts/app/solidity')
.openFile('ethereum/remix-project/apps/remix-ide/contracts/app/solidity/mode.sol')
},
'Should load using URL compiler params': function (browser: NightwatchBrowser) { 'Should load using URL compiler params': function (browser: NightwatchBrowser) {
browser browser
.pause(5000) .pause(5000)
@ -38,22 +75,22 @@ module.exports = {
.refresh() .refresh()
.pause(5000) .pause(5000)
.clickLaunchIcon('solidity') .clickLaunchIcon('solidity')
.assert.containsText('#versionSelector option[selected="selected"]', '0.7.4+commit.3f05b770') .assert.containsText('#versionSelector option[data-id="selected"]', '0.7.4+commit.3f05b770')
.assert.containsText('#evmVersionSelector option[selected="selected"]', 'istanbul') .assert.containsText('#evmVersionSelector option[data-id="selected"]', 'istanbul')
.verify.elementPresent('#optimize:checked') .verify.elementPresent('#optimize:checked')
.verify.attributeEquals('#runs', 'value', '300') .verify.attributeEquals('#runs', 'value', '300')
}, },
'Should load using compiler from link passed in remix URL': function (browser: NightwatchBrowser) { 'Should load using compiler from link passed in remix URL': function (browser: NightwatchBrowser) {
browser browser
.url('http://127.0.0.1:8080/#version=https://solidity-blog.s3.eu-central-1.amazonaws.com/data/08preview/soljson.js') .url('http://127.0.0.1:8080/#version=https://solidity-blog.s3.eu-central-1.amazonaws.com/data/08preview/soljson.js&optimize=false')
.refresh() .refresh()
.pause(5000) .pause(5000)
.clickLaunchIcon('solidity') .clickLaunchIcon('solidity')
.pause(5000) .pause(5000)
.assert.containsText('#versionSelector option[selected="selected"]', 'custom') .assert.containsText('#versionSelector option[data-id="selected"]', 'custom')
// default values // default values
.assert.containsText('#evmVersionSelector option[selected="selected"]', 'default') .assert.containsText('#evmVersionSelector option[data-id="selected"]', 'default')
.verify.elementPresent('#optimize') .verify.elementPresent('#optimize')
.assert.elementNotPresent('#optimize:checked') .assert.elementNotPresent('#optimize:checked')
.verify.elementPresent('#runs:disabled') .verify.elementPresent('#runs:disabled')

@ -32,11 +32,8 @@ module.exports = {
.clickLaunchIcon('filePanel') .clickLaunchIcon('filePanel')
.addFile('basic.sol', sources[0]['basic.sol']) .addFile('basic.sol', sources[0]['basic.sol'])
.clickLaunchIcon('solidity') .clickLaunchIcon('solidity')
.execute(function () { .waitForElementVisible('[data-id="compilerNightliesBuild"]')
const elem = document.getElementById('nightlies') as HTMLInputElement .click('[data-id="compilerNightliesBuild"]')
elem.checked = true
})
.noWorkerErrorFor('soljson-v0.3.4+commit.7dab8902.js') .noWorkerErrorFor('soljson-v0.3.4+commit.7dab8902.js')
.noWorkerErrorFor('soljson-v0.6.5+commit.f956cc89.js') .noWorkerErrorFor('soljson-v0.6.5+commit.f956cc89.js')
.noWorkerErrorFor('soljson-v0.6.8-nightly.2020.5.14+commit.a6d0067b.js') .noWorkerErrorFor('soljson-v0.6.8-nightly.2020.5.14+commit.a6d0067b.js')

@ -460,7 +460,8 @@ Please make a backup of your contracts and start using http://remix.ethereum.org
await appManager.activatePlugin(['mainPanel', 'menuicons', 'tabs']) await appManager.activatePlugin(['mainPanel', 'menuicons', 'tabs'])
await appManager.activatePlugin(['sidePanel']) // activating host plugin separately await appManager.activatePlugin(['sidePanel']) // activating host plugin separately
await appManager.activatePlugin(['home']) await appManager.activatePlugin(['home'])
await appManager.activatePlugin(['hiddenPanel', 'pluginManager', 'filePanel', 'settings', 'contextualListener', 'terminal', 'fetchAndCompile', 'contentImport']) await appManager.activatePlugin(['settings'])
await appManager.activatePlugin(['hiddenPanel', 'pluginManager', 'filePanel', 'contextualListener', 'terminal', 'fetchAndCompile', 'contentImport'])
const queryParams = new QueryParams() const queryParams = new QueryParams()
const params = queryParams.get() const params = queryParams.get()

@ -206,7 +206,20 @@ class DGitProvider extends Plugin {
const commits = await this.log({ ref: 'HEAD' }) const commits = await this.log({ ref: 'HEAD' })
ob = { ob = {
ref: commits[0].oid, ref: commits[0].oid,
message: commits[0].commit.message message: commits[0].commit.message,
commits: JSON.stringify(commits.map((commit) => {
return {
oid: commit.oid,
commit: {
parent: commit.commit?.parent,
tree: commit.commit?.tree,
message: commit.commit?.message,
committer: {
timestamp: commit.commit?.committer?.timestamp
}
}
}
}))
} }
} catch (e) { } catch (e) {
ob = { ob = {

@ -165,16 +165,26 @@ module.exports = class Filepanel extends ViewPlugin {
} }
if (loadedFromGist) return if (loadedFromGist) return
if (params.code) { if (params.code || params.url) {
try { try {
await this.processCreateWorkspace('code-sample') await this.processCreateWorkspace('code-sample')
this._deps.fileProviders.workspace.setWorkspace('code-sample') this._deps.fileProviders.workspace.setWorkspace('code-sample')
var hash = bufferToHex(keccakFromString(params.code)) let path = ''
const fileName = 'contract-' + hash.replace('0x', '').substring(0, 10) + '.sol' let content = ''
const path = fileName if (params.code) {
await this._deps.fileProviders.workspace.set(path, atob(params.code)) var hash = bufferToHex(keccakFromString(params.code))
path = 'contract-' + hash.replace('0x', '').substring(0, 10) + '.sol'
content = atob(params.code)
await this._deps.fileProviders.workspace.set(path, content)
}
if (params.url) {
const data = await this.call('contentImport', 'resolve', params.url)
path = data.cleanUrl
content = data.content
await this._deps.fileProviders.workspace.set(path, content)
}
this.initialWorkspace = 'code-sample' this.initialWorkspace = 'code-sample'
await this._deps.fileManager.openFile(fileName) await this._deps.fileManager.openFile(path)
} catch (e) { } catch (e) {
console.error(e) console.error(e)
} }

@ -1,28 +1,18 @@
/* global */ /* global */
import React from 'react' // eslint-disable-line
import ReactDOM from 'react-dom'
import { SolidityCompiler, CompileTab as CompileTabLogic, parseContracts } from '@remix-ui/solidity-compiler' // eslint-disable-line
import { compile } from '@remix-project/remix-solidity'
import { ViewPlugin } from '@remixproject/engine-web' import { ViewPlugin } from '@remixproject/engine-web'
import * as packageJson from '../../../../../package.json' import * as packageJson from '../../../../../package.json'
import publishToStorage from '../../publishToStorage'
import { compile } from '@remix-project/remix-solidity'
const EventEmitter = require('events') const EventEmitter = require('events')
const $ = require('jquery') const $ = require('jquery')
const yo = require('yo-yo') const yo = require('yo-yo')
const copy = require('copy-text-to-clipboard')
var QueryParams = require('../../lib/query-params') var QueryParams = require('../../lib/query-params')
const TreeView = require('../ui/TreeView')
const modalDialog = require('../ui/modaldialog')
const copyToClipboard = require('../ui/copy-to-clipboard')
const modalDialogCustom = require('../ui/modal-dialog-custom')
const parseContracts = require('./compileTab/contractParser')
const addTooltip = require('../ui/tooltip') const addTooltip = require('../ui/tooltip')
const Renderer = require('../ui/renderer')
const globalRegistry = require('../../global/registry') const globalRegistry = require('../../global/registry')
var css = require('./styles/compile-tab-styles')
const CompileTabLogic = require('./compileTab/compileTab.js')
const CompilerContainer = require('./compileTab/compilerContainer.js')
const profile = { const profile = {
name: 'solidity', name: 'solidity',
displayName: 'Solidity compiler', displayName: 'Solidity compiler',
@ -57,11 +47,9 @@ class CompileTab extends ViewPlugin {
// dependencies // dependencies
this.editor = editor this.editor = editor
this.config = config this.config = config
this.renderer = new Renderer(this)
this.fileManager = fileManager this.fileManager = fileManager
this.contractsDetails = {}
this.data = { this.data = {
contractsDetails: {},
eventHandlers: {}, eventHandlers: {},
loading: false loading: false
} }
@ -71,30 +59,32 @@ class CompileTab extends ViewPlugin {
this.editor, this.editor,
this.config, this.config,
this.fileProvider, this.fileProvider,
this.contentImport this.contentImport,
this.setCompileErrors.bind(this)
) )
}
onActivationInternal () {
this.compiler = this.compileTabLogic.compiler this.compiler = this.compileTabLogic.compiler
this.compileTabLogic.init() this.compileTabLogic.init()
this.contractMap = {}
this.compilerContainer = new CompilerContainer( this.isHardHatProject = false
this.compileTabLogic, this.compileErrors = {}
this.editor, this.compiledFileName = ''
this.config, this.selectedVersion = ''
this.queryParams this.configurationSettings = null
)
this.el = document.createElement('div')
this.el.setAttribute('id', 'compileTabView')
} }
resetResults () { resetResults () {
if (this._view.errorContainer) { this.currentFile = ''
this._view.errorContainer.innerHTML = '' this.contractsDetails = {}
}
this.compilerContainer.currentFile = ''
this.data.contractsDetails = {}
yo.update(this._view.contractSelection, this.contractSelection())
this.emit('statusChanged', { key: 'none' }) this.emit('statusChanged', { key: 'none' })
this.renderComponent()
}
setCompileErrors (data) {
this.compileErrors = data
this.renderComponent()
} }
/************ /************
@ -102,13 +92,6 @@ class CompileTab extends ViewPlugin {
*/ */
listenToEvents () { listenToEvents () {
this.on('filePanel', 'setWorkspace', (workspace) => {
this.compileTabLogic.isHardhatProject().then((result) => {
if (result && workspace.isLocalhost) this.compilerContainer.hardhatCompilation.style.display = 'flex'
else this.compilerContainer.hardhatCompilation.style.display = 'none'
})
})
this.data.eventHandlers.onContentChanged = () => { this.data.eventHandlers.onContentChanged = () => {
this.emit('statusChanged', { key: 'edited', title: 'the content has changed, needs recompilation', type: 'info' }) this.emit('statusChanged', { key: 'edited', title: 'the content has changed, needs recompilation', type: 'info' })
} }
@ -127,9 +110,6 @@ class CompileTab extends ViewPlugin {
this.compiler.event.register('compilerLoaded', this.data.eventHandlers.onCompilerLoaded) this.compiler.event.register('compilerLoaded', this.data.eventHandlers.onCompilerLoaded)
this.data.eventHandlers.onStartingCompilation = () => { this.data.eventHandlers.onStartingCompilation = () => {
if (this._view.errorContainer) {
this._view.errorContainer.innerHTML = ''
}
this.emit('statusChanged', { key: 'loading', title: 'compiling...', type: 'info' }) this.emit('statusChanged', { key: 'loading', title: 'compiling...', type: 'info' })
} }
@ -137,23 +117,32 @@ class CompileTab extends ViewPlugin {
this.call('editor', 'clearAnnotations') this.call('editor', 'clearAnnotations')
} }
this.on('filePanel', 'setWorkspace', () => this.resetResults()) this.on('filePanel', 'setWorkspace', (workspace) => {
this.compileTabLogic.isHardhatProject().then((result) => {
if (result && workspace.isLocalhost) this.isHardHatProject = true
else this.isHardHatProject = false
this.renderComponent()
})
this.resetResults()
})
this.compileTabLogic.event.on('startingCompilation', this.data.eventHandlers.onStartingCompilation) this.compileTabLogic.event.on('startingCompilation', this.data.eventHandlers.onStartingCompilation)
this.compileTabLogic.event.on('removeAnnotations', this.data.eventHandlers.onRemoveAnnotations) this.compileTabLogic.event.on('removeAnnotations', this.data.eventHandlers.onRemoveAnnotations)
this.data.eventHandlers.onCurrentFileChanged = (name) => { this.data.eventHandlers.onCurrentFileChanged = (name) => {
this.compilerContainer.currentFile = name this.currentFile = name
this.renderComponent()
} }
this.fileManager.events.on('currentFileChanged', this.data.eventHandlers.onCurrentFileChanged) this.fileManager.events.on('currentFileChanged', this.data.eventHandlers.onCurrentFileChanged)
this.data.eventHandlers.onNoFileSelected = () => { this.data.eventHandlers.onNoFileSelected = () => {
this.compilerContainer.currentFile = '' this.currentFile = ''
this.renderComponent()
} }
this.fileManager.events.on('noFileSelected', this.data.eventHandlers.onNoFileSelected) this.fileManager.events.on('noFileSelected', this.data.eventHandlers.onNoFileSelected)
this.data.eventHandlers.onCompilationFinished = (success, data, source) => { this.data.eventHandlers.onCompilationFinished = (success, data, source) => {
this._view.errorContainer.appendChild(yo`<span data-id="compilationFinishedWith_${this.getCurrentVersion()}"></span>`) this.setCompileErrors(data)
if (success) { if (success) {
// forwarding the event to the appManager infra // forwarding the event to the appManager infra
this.emit('compilationFinished', source.target, source, 'soljson', data) this.emit('compilationFinished', source.target, source, 'soljson', data)
@ -165,9 +154,9 @@ class CompileTab extends ViewPlugin {
}) })
} else this.emit('statusChanged', { key: 'succeed', title: 'compilation successful', type: 'success' }) } else this.emit('statusChanged', { key: 'succeed', title: 'compilation successful', type: 'success' })
// Store the contracts // Store the contracts
this.data.contractsDetails = {} this.contractsDetails = {}
this.compiler.visitContracts((contract) => { this.compiler.visitContracts((contract) => {
this.data.contractsDetails[contract.name] = parseContracts( this.contractsDetails[contract.name] = parseContracts(
contract.name, contract.name,
contract.object, contract.object,
this.compiler.getSource(contract.file) this.compiler.getSource(contract.file)
@ -178,37 +167,9 @@ class CompileTab extends ViewPlugin {
this.emit('statusChanged', { key: count, title: `compilation failed with ${count} error${count.length > 1 ? 's' : ''}`, type: 'error' }) this.emit('statusChanged', { key: count, title: `compilation failed with ${count} error${count.length > 1 ? 's' : ''}`, type: 'error' })
} }
// Update contract Selection // Update contract Selection
const contractMap = {} this.contractMap = {}
if (success) this.compiler.visitContracts((contract) => { contractMap[contract.name] = contract }) if (success) this.compiler.visitContracts((contract) => { this.contractMap[contract.name] = contract })
const contractSelection = this.contractSelection(contractMap) this.renderComponent()
yo.update(this._view.contractSelection, contractSelection)
if (data.error) {
this.renderer.error(
data.error.formattedMessage || data.error,
this._view.errorContainer,
{ type: data.error.severity || 'error', errorType: data.error.type }
)
if (data.error.mode === 'panic') {
return modalDialogCustom.alert(yo`
<div><i class="fas fa-exclamation-circle ${css.panicError}" aria-hidden="true"></i>
The compiler returned with the following internal error: <br> <b>${data.error.formattedMessage}.<br>
The compiler might be in a non-sane state, please be careful and do not use further compilation data to deploy to mainnet.
It is heavily recommended to use another browser not affected by this issue (Firefox is known to not be affected).</b><br>
Please join <a href="https://gitter.im/ethereum/remix" target="blank" >remix gitter channel</a> for more information.</div>`)
}
}
if (data.errors && data.errors.length) {
data.errors.forEach((err) => {
if (this.config.get('hideWarnings')) {
if (err.severity !== 'warning') {
this.renderer.error(err.formattedMessage, this._view.errorContainer, { type: err.severity, errorType: err.type })
}
} else {
this.renderer.error(err.formattedMessage, this._view.errorContainer, { type: err.severity, errorType: err.type })
}
})
}
} }
this.compiler.event.register('compilationFinished', this.data.eventHandlers.onCompilationFinished) this.compiler.event.register('compilationFinished', this.data.eventHandlers.onCompilationFinished)
@ -226,11 +187,19 @@ class CompileTab extends ViewPlugin {
// ctrl+s or command+s // ctrl+s or command+s
if ((e.metaKey || e.ctrlKey) && e.keyCode === 83) { if ((e.metaKey || e.ctrlKey) && e.keyCode === 83) {
e.preventDefault() e.preventDefault()
this.compileTabLogic.runCompiler(this.compilerContainer.hhCompilation) this.compileTabLogic.runCompiler(this.hhCompilation)
} }
}) })
} }
setHardHatCompilation (value) {
this.hhCompilation = value
}
setSelectedVersion (version) {
this.selectedVersion = version
}
getCompilationResult () { getCompilationResult () {
return this.compileTabLogic.compiler.state.lastCompilationResult return this.compileTabLogic.compiler.state.lastCompilationResult
} }
@ -254,20 +223,15 @@ class CompileTab extends ViewPlugin {
* @param {object} settings {evmVersion, optimize, runs, version, language} * @param {object} settings {evmVersion, optimize, runs, version, language}
*/ */
async compileWithParameters (compilationTargets, settings) { async compileWithParameters (compilationTargets, settings) {
settings.version = settings.version || this.compilerContainer.data.selectedVersion settings.version = settings.version || this.selectedVersion
const res = await compile(compilationTargets, settings) const res = await compile(compilationTargets, settings)
return res return res
} }
// This function is used for passing the compiler remix-tests
getCurrentVersion () {
return this.compilerContainer.data.selectedVersion
}
// This function is used for passing the compiler configuration to 'remix-tests' // This function is used for passing the compiler configuration to 'remix-tests'
getCurrentCompilerConfig () { getCurrentCompilerConfig () {
return { return {
currentVersion: this.compilerContainer.data.selectedVersion, currentVersion: this.selectedVersion,
evmVersion: this.compileTabLogic.evmVersion, evmVersion: this.compileTabLogic.evmVersion,
optimize: this.compileTabLogic.optimize, optimize: this.compileTabLogic.optimize,
runs: this.compileTabLogic.runs runs: this.compileTabLogic.runs
@ -280,96 +244,10 @@ class CompileTab extends ViewPlugin {
* @param {object} settings {evmVersion, optimize, runs, version, language} * @param {object} settings {evmVersion, optimize, runs, version, language}
*/ */
setCompilerConfig (settings) { setCompilerConfig (settings) {
return new Promise((resolve, reject) => { this.configurationSettings = settings
addTooltip(yo`<div><b>${this.currentRequest.from}</b> is updating the <b>Solidity compiler configuration</b>.<pre class="text-left">${JSON.stringify(settings, null, '\t')}</pre></div>`) this.renderComponent()
this.compilerContainer.setConfiguration(settings) // @todo(#2875) should use loading compiler return value to check whether the compiler is loaded instead of "setInterval"
// @todo(#2875) should use loading compiler return value to check whether the compiler is loaded instead of "setInterval" addTooltip(yo`<div><b>${this.currentRequest.from}</b> is updating the <b>Solidity compiler configuration</b>.<pre class="text-left">${JSON.stringify(settings, null, '\t')}</pre></div>`)
let timeout = 0
const id = setInterval(() => {
timeout++
console.log(this.data.loading)
if (!this.data.loading || timeout > 10) {
resolve()
clearInterval(id)
}
}, 200)
})
}
/*********
* SUB-COMPONENTS
*/
/**
* Section to select the compiled contract
* @param {string[]} contractList Names of the compiled contracts
*/
contractSelection (contractMap) {
// Return the file name of a path: ex "browser/ballot.sol" -> "ballot.sol"
const getFileName = (path) => {
const part = path.split('/')
return part[part.length - 1]
}
const contractList = contractMap ? Object.keys(contractMap).map((key) => ({
name: key,
file: getFileName(contractMap[key].file)
})) : []
const selectEl = yo`
<select
onchange="${e => this.selectContract(e.target.value)}"
data-id="compiledContracts" id="compiledContracts" class="custom-select"
>
${contractList.map(({ name, file }) => yo`<option value="${name}">${name} (${file})</option>`)}
</select>
`
// define swarm logo
const result = contractList.length
? yo`<section class="${css.compilerSection} pt-3">
<!-- Select Compiler Version -->
<div class="mb-3">
<label class="${css.compilerLabel} form-check-label" for="compiledContracts">Contract</label>
${selectEl}
</div>
<article class="mt-2 pb-0">
<button id="publishOnSwarm" class="btn btn-secondary btn-block" title="Publish on Swarm" onclick="${() => { publishToStorage('swarm', this.fileProvider, this.fileManager, this.data.contractsDetails[this.selectedContract]) }}">
<span>Publish on Swarm</span>
<img id="swarmLogo" class="${css.storageLogo} ml-2" src="assets/img/swarm.webp">
</button>
<button id="publishOnIpfs" class="btn btn-secondary btn-block" title="Publish on Ipfs" onclick="${() => { publishToStorage('ipfs', this.fileProvider, this.fileManager, this.data.contractsDetails[this.selectedContract]) }}">
<span>Publish on Ipfs</span>
<img id="ipfsLogo" class="${css.storageLogo} ml-2" src="assets/img/ipfs.webp">
</button>
<button data-id="compilation-details" class="btn btn-secondary btn-block" title="Display Contract Details" onclick="${() => { this.details() }}">
Compilation Details
</button>
<!-- Copy to Clipboard -->
<div class="${css.contractHelperButtons}">
<div class="input-group">
<div class="btn-group" role="group" aria-label="Copy to Clipboard">
<button class="btn ${css.copyButton}" title="Copy ABI to clipboard" onclick="${() => { this.copyABI() }}">
<i class="${css.copyIcon} far fa-copy" aria-hidden="true"></i>
<span>ABI</span>
</button>
<button class="btn ${css.copyButton}" title="Copy Bytecode to clipboard" onclick="${() => { this.copyBytecode() }}">
<i class="${css.copyIcon} far fa-copy" aria-hidden="true"></i>
<span>Bytecode</span>
</button>
</div>
</div>
</div>
</div>
</section>`
: yo`<section class="${css.container} clearfix"><article class="px-2 mt-2 pb-0 d-flex">
<span class="mt-2 mx-3 w-100 alert alert-warning" role="alert">No Contract Compiled Yet</span>
</article></section>`
if (contractList.length) {
this.selectedContract = selectEl.value
} else {
delete this.selectedContract
}
return result
} }
// TODO : Add success alert when compilation succeed // TODO : Add success alert when compilation succeed
@ -390,119 +268,15 @@ class CompileTab extends ViewPlugin {
this.selectedContract = contractName this.selectedContract = contractName
} }
details () { render () {
const help = { this.renderComponent()
Assembly: 'Assembly opcodes describing the contract including corresponding solidity source code', return this.el
Opcodes: 'Assembly opcodes describing the contract',
'Runtime Bytecode': 'Bytecode storing the state and being executed during normal contract call',
bytecode: 'Bytecode being executed during contract creation',
functionHashes: 'List of declared function and their corresponding hash',
gasEstimates: 'Gas estimation for each function call',
metadata: 'Contains all informations related to the compilation',
metadataHash: 'Hash representing all metadata information',
abi: 'ABI: describing all the functions (input/output params, scope, ...)',
name: 'Name of the compiled contract',
swarmLocation: 'Swarm url where all metadata information can be found (contract needs to be published first)',
web3Deploy: 'Copy/paste this code to any JavaScript/Web3 console to deploy this contract'
}
if (!this.selectedContract) throw new Error('No contract compiled yet')
const contractProperties = this.data.contractsDetails[this.selectedContract]
const log = yo`<div class="${css.detailsJSON}"></div>`
Object.keys(contractProperties).map(propertyName => {
const copyDetails = yo`<span class="${css.copyDetails}">${copyToClipboard(() => contractProperties[propertyName])}</span>`
const questionMark = yo`<span class="${css.questionMark}"><i title="${help[propertyName]}" class="fas fa-question-circle" aria-hidden="true"></i></span>`
log.appendChild(yo`<div class=${css.log}>
<div class="${css.key}">${propertyName} ${copyDetails} ${questionMark}</div>
${this.insertValue(contractProperties, propertyName)}
</div>`)
})
modalDialog(this.selectedContract, log, { label: '' }, { label: 'Close' })
}
insertValue (details, propertyName) {
var node
if (propertyName === 'web3Deploy' || propertyName === 'name' || propertyName === 'Assembly') {
node = yo`<pre>${details[propertyName]}</pre>`
} else if (propertyName === 'abi' || propertyName === 'metadata') {
const treeView = new TreeView({
extractData: function (item, parent, key) {
var ret = {}
if (item instanceof Array) {
ret.children = item.map((item, index) => ({ key: index, value: item }))
ret.self = ''
} else if (item instanceof Object) {
ret.children = Object.keys(item).map((key) => ({ key: key, value: item[key] }))
ret.self = ''
} else {
ret.self = item
ret.children = []
}
return ret
}
})
if (details[propertyName] !== '') {
try {
node = yo`
<div>
${treeView.render(typeof details[propertyName] === 'object' ? details[propertyName] : JSON.parse(details[propertyName]))}
</div>` // catch in case the parsing fails.
} catch (e) {
node = yo`<div>Unable to display "${propertyName}": ${e.message}</div>`
}
} else {
node = yo`<div> - </div>`
}
} else {
node = yo`<div>${JSON.stringify(details[propertyName], null, 4)}</div>`
}
return yo`<pre class="${css.value}">${node || ''}</pre>`
}
getContractProperty (property) {
if (!this.selectedContract) throw new Error('No contract compiled yet')
const contractProperties = this.data.contractsDetails[this.selectedContract]
return contractProperties[property] || null
}
copyContractProperty (property) {
let content = this.getContractProperty(property)
if (!content) {
addTooltip('No content available for ' + property)
return
}
try {
if (typeof content !== 'string') {
content = JSON.stringify(content, null, '\t')
}
} catch (e) {}
copy(content)
addTooltip('Copied value to clipboard')
}
copyABI () {
this.copyContractProperty('abi')
}
copyBytecode () {
this.copyContractProperty('bytecode')
} }
render () { renderComponent () {
if (this._view.el) return this._view.el ReactDOM.render(
this.onActivationInternal() <SolidityCompiler plugin={this}/>
this._view.errorContainer = yo`<div class="${css.errorBlobs} p-4" data-id="compiledErrors" ></div>` , this.el)
this._view.contractSelection = this.contractSelection()
this._view.compilerContainer = this.compilerContainer.render()
this.compilerContainer.activate()
this._view.el = yo`
<div id="compileTabView">
${this._view.compilerContainer}
${this._view.contractSelection}
${this._view.errorContainer}
</div>`
return this._view.el
} }
onActivation () { onActivation () {
@ -526,7 +300,8 @@ class CompileTab extends ViewPlugin {
} }
onDeactivation () { onDeactivation () {
this.compilerContainer.deactivate() this.editor.event.unregister('contentChanged')
this.editor.event.unregister('sessionSwitched')
this.editor.event.unregister('contentChanged', this.data.eventHandlers.onContentChanged) this.editor.event.unregister('contentChanged', this.data.eventHandlers.onContentChanged)
this.compiler.event.unregister('loadingCompiler', this.data.eventHandlers.onLoadingCompiler) this.compiler.event.unregister('loadingCompiler', this.data.eventHandlers.onLoadingCompiler)
this.compiler.event.unregister('compilerLoaded', this.data.eventHandlers.onCompilerLoaded) this.compiler.event.unregister('compilerLoaded', this.data.eventHandlers.onCompilerLoaded)

@ -112,7 +112,7 @@ class CompilerContainer {
this._view.compileIcon.setAttribute('title', 'idle') this._view.compileIcon.setAttribute('title', 'idle')
this._view.compileIcon.classList.remove(`${css.spinningIcon}`) this._view.compileIcon.classList.remove(`${css.spinningIcon}`)
this._view.compileIcon.classList.remove(`${css.bouncingIcon}`) this._view.compileIcon.classList.remove(`${css.bouncingIcon}`)
_paq.push(['trackEvent', 'compiler', `compiled_with_v_${this._retrieveVersion()}`]) _paq.push(['trackEvent', 'compiler', 'compiled_with_version', this._retrieveVersion()])
}) })
} }

@ -1,6 +1,6 @@
var yo = require('yo-yo') var yo = require('yo-yo')
// -------------- copyToClipboard ---------------------- // -------------- copyToClipboard ----------------------
const copy = require('copy-text-to-clipboard') const copy = require('copy-to-clipboard')
var addTooltip = require('./tooltip') var addTooltip = require('./tooltip')
// -------------- styling ---------------------- // -------------- styling ----------------------
var csjs = require('csjs-inject') var csjs = require('csjs-inject')

@ -147,13 +147,17 @@ export class CodeManager {
private async retrieveIndexAndTrigger (codeMananger, address, step, code) { private async retrieveIndexAndTrigger (codeMananger, address, step, code) {
let result let result
let next const next = []
const returnInstructionIndexes = [] const returnInstructionIndexes = []
const outOfGasInstructionIndexes = [] const outOfGasInstructionIndexes = []
try { try {
result = codeMananger.getInstructionIndex(address, step) result = codeMananger.getInstructionIndex(address, step)
next = codeMananger.getInstructionIndex(address, step + 1) for (let i = 1; i < 6; i++) {
if (this.traceManager.inRange(step + i)) {
next.push(codeMananger.getInstructionIndex(address, step + i))
}
}
let values = this.traceManager.getAllStopIndexes() let values = this.traceManager.getAllStopIndexes()
if (values) { if (values) {

@ -59,8 +59,8 @@ export class VmDebuggerLogic {
} }
listenToCodeManagerEvents () { listenToCodeManagerEvents () {
this._codeManager.event.register('changed', (code, address, index, nextIndex, returnInstructionIndexes, outOfGasInstructionIndexes) => { this._codeManager.event.register('changed', (code, address, index, nextIndexes, returnInstructionIndexes, outOfGasInstructionIndexes) => {
this.event.trigger('codeManagerChanged', [code, address, index, nextIndex, returnInstructionIndexes, outOfGasInstructionIndexes]) this.event.trigger('codeManagerChanged', [code, address, index, nextIndexes, returnInstructionIndexes, outOfGasInstructionIndexes])
}) })
} }

@ -1,27 +1,37 @@
import React, { useState } from 'react' import React, { useState } from 'react'
import copy from 'copy-text-to-clipboard' import copy from 'copy-to-clipboard'
import { OverlayTrigger, Tooltip } from 'react-bootstrap' import { OverlayTrigger, Tooltip } from 'react-bootstrap'
import { Placement } from 'react-bootstrap/esm/Overlay'
import './copy-to-clipboard.css' import './copy-to-clipboard.css'
export const CopyToClipboard = ({ content, tip='Copy', icon='fa-copy', ...otherProps }) => { interface ICopyToClipboard {
content: any,
tip?: string,
icon?: string,
direction?: Placement,
className?: string,
title?: string,
children?: JSX.Element
}
export const CopyToClipboard = (props: ICopyToClipboard) => {
let { content, tip = 'Copy', icon = 'fa-copy', direction = 'right', children, ...otherProps } = props
const [message, setMessage] = useState(tip) const [message, setMessage] = useState(tip)
const handleClick = (e) => {
const handleClick = (event) => {
if (content && content !== '') { // module `copy` keeps last copied thing in the memory, so don't show tooltip if nothing is copied, because nothing was added to memory if (content && content !== '') { // module `copy` keeps last copied thing in the memory, so don't show tooltip if nothing is copied, because nothing was added to memory
try { try {
if (typeof content !== 'string') { if (typeof content !== 'string') {
content = JSON.stringify(content, null, '\t') content = JSON.stringify(content, null, '\t')
} }
copy(content)
setMessage('Copied')
} catch (e) { } catch (e) {
console.error(e) console.error(e)
} }
copy(content)
setMessage('Copied')
} else { } else {
setMessage('Cannot copy empty content!') setMessage('Cannot copy empty content!')
} }
event.preventDefault() e.preventDefault()
return false return false
} }
@ -31,14 +41,16 @@ export const CopyToClipboard = ({ content, tip='Copy', icon='fa-copy', ...otherP
return ( return (
<a href='#' onClick={handleClick} onMouseLeave={reset}> <a href='#' onClick={handleClick} onMouseLeave={reset}>
<OverlayTrigger placement="right" overlay={ <OverlayTrigger placement={direction} overlay={
<Tooltip id="overlay-tooltip"> <Tooltip id="overlay-tooltip">
{ message } { message }
</Tooltip> </Tooltip>
}> }>
<i className={`far ${icon} ml-1 p-2`} aria-hidden="true" {
{...otherProps} children || (<i className={`far ${icon} ml-1 p-2`} aria-hidden="true"
></i> {...otherProps}
></i>)
}
</OverlayTrigger> </OverlayTrigger>
</a> </a>
) )

@ -6,15 +6,15 @@ export const AssemblyItems = ({ registerEvent }) => {
const [assemblyItems, dispatch] = useReducer(reducer, initialState) const [assemblyItems, dispatch] = useReducer(reducer, initialState)
const [absoluteSelectedIndex, setAbsoluteSelectedIndex] = useState(0) const [absoluteSelectedIndex, setAbsoluteSelectedIndex] = useState(0)
const [selectedItem, setSelectedItem] = useState(0) const [selectedItem, setSelectedItem] = useState(0)
const [nextSelectedItem, setNextSelectedItem] = useState(1) const [nextSelectedItems, setNextSelectedItems] = useState([1])
const [returnInstructionIndexes, setReturnInstructionIndexes] = useState([]) const [returnInstructionIndexes, setReturnInstructionIndexes] = useState([])
const [outOfGasInstructionIndexes, setOutOfGasInstructionIndexes] = useState([]) const [outOfGasInstructionIndexes, setOutOfGasInstructionIndexes] = useState([])
const refs = useRef({}) const refs = useRef({})
const asmItemsRef = useRef(null) const asmItemsRef = useRef(null)
useEffect(() => { useEffect(() => {
registerEvent && registerEvent('codeManagerChanged', (code, address, index, nextIndex, returnInstructionIndexes, outOfGasInstructionIndexes) => { registerEvent && registerEvent('codeManagerChanged', (code, address, index, nextIndexes, returnInstructionIndexes, outOfGasInstructionIndexes) => {
dispatch({ type: 'FETCH_OPCODES_SUCCESS', payload: { code, address, index, nextIndex, returnInstructionIndexes, outOfGasInstructionIndexes } }) dispatch({ type: 'FETCH_OPCODES_SUCCESS', payload: { code, address, index, nextIndexes, returnInstructionIndexes, outOfGasInstructionIndexes } })
}) })
}, []) }, [])
@ -22,7 +22,7 @@ export const AssemblyItems = ({ registerEvent }) => {
if (absoluteSelectedIndex !== assemblyItems.index) { if (absoluteSelectedIndex !== assemblyItems.index) {
clearItems() clearItems()
indexChanged(assemblyItems.index) indexChanged(assemblyItems.index)
nextIndexChanged(assemblyItems.nextIndex) nextIndexesChanged(assemblyItems.nextIndexes)
returnIndexes(assemblyItems.returnInstructionIndexes) returnIndexes(assemblyItems.returnInstructionIndexes)
outOfGasIndexes(assemblyItems.outOfGasInstructionIndexes) outOfGasIndexes(assemblyItems.outOfGasInstructionIndexes)
} }
@ -40,7 +40,11 @@ export const AssemblyItems = ({ registerEvent }) => {
const clearItems = () => { const clearItems = () => {
clearItem(refs.current[selectedItem] ? refs.current[selectedItem] : null) clearItem(refs.current[selectedItem] ? refs.current[selectedItem] : null)
clearItem(refs.current[nextSelectedItem] ? refs.current[nextSelectedItem] : null) if (nextSelectedItems) {
nextSelectedItems.map((index) => {
clearItem(refs.current[index] ? refs.current[index] : null)
})
}
returnInstructionIndexes.map((index) => { returnInstructionIndexes.map((index) => {
if (index < 0) return if (index < 0) return
@ -70,18 +74,20 @@ export const AssemblyItems = ({ registerEvent }) => {
setAbsoluteSelectedIndex(assemblyItems.opCodes.index) setAbsoluteSelectedIndex(assemblyItems.opCodes.index)
} }
const nextIndexChanged = (index: number) => { const nextIndexesChanged = (indexes: Array<number>) => {
if (index < 0) return indexes.map((index) => {
if (index < 0) return
const codeView = asmItemsRef.current const codeView = asmItemsRef.current
const currentItem = codeView.children[index] const currentItem = codeView.children[index]
if (currentItem) { if (currentItem) {
currentItem.style.setProperty('border-color', 'var(--secondary)') currentItem.style.setProperty('color', 'var(--primary)')
currentItem.style.setProperty('border-style', 'dotted') currentItem.style.setProperty('font-weight', 'bold')
currentItem.setAttribute('selected', 'selected') currentItem.setAttribute('selected', 'selected')
} }
setNextSelectedItem(index) })
setNextSelectedItems(indexes)
} }
const returnIndexes = (indexes) => { const returnIndexes = (indexes) => {

@ -13,7 +13,7 @@ export const initialState = {
}, },
display: [], display: [],
index: 0, index: 0,
nextIndex: -1, nextIndexes: [-1],
returnInstructionIndexes: [], returnInstructionIndexes: [],
outOfGasInstructionIndexes: [], outOfGasInstructionIndexes: [],
top: 0, top: 0,
@ -30,7 +30,7 @@ const reducedOpcode = (opCodes, payload) => {
const top = bottom + length const top = bottom + length
return { return {
index: opCodes.index - bottom, index: opCodes.index - bottom,
nextIndex: opCodes.nextIndex - bottom, nextIndexes: opCodes.nextIndexes.map(index => index - bottom),
display: opCodes.code.slice(bottom, top), display: opCodes.code.slice(bottom, top),
returnInstructionIndexes: payload.returnInstructionIndexes.map((index) => index.instructionIndex - bottom), returnInstructionIndexes: payload.returnInstructionIndexes.map((index) => index.instructionIndex - bottom),
outOfGasInstructionIndexes: payload.outOfGasInstructionIndexes.map((index) => index.instructionIndex - bottom) outOfGasInstructionIndexes: payload.outOfGasInstructionIndexes.map((index) => index.instructionIndex - bottom)
@ -49,7 +49,7 @@ export const reducer = (state = initialState, action: Action) => {
} }
case 'FETCH_OPCODES_SUCCESS': { case 'FETCH_OPCODES_SUCCESS': {
const opCodes = action.payload.address === state.opCodes.address ? { const opCodes = action.payload.address === state.opCodes.address ? {
...state.opCodes, index: action.payload.index, nextIndex: action.payload.nextIndex ...state.opCodes, index: action.payload.index, nextIndexes: action.payload.nextIndexes
} : deepEqual(action.payload.code, state.opCodes.code) ? state.opCodes : action.payload } : deepEqual(action.payload.code, state.opCodes.code) ? state.opCodes : action.payload
const reduced = reducedOpcode(opCodes, action.payload) const reduced = reducedOpcode(opCodes, action.payload)
@ -57,7 +57,7 @@ export const reducer = (state = initialState, action: Action) => {
opCodes, opCodes,
display: reduced.display, display: reduced.display,
index: reduced.index, index: reduced.index,
nextIndex: reduced.nextIndex, nextIndexes: reduced.nextIndexes,
isRequesting: false, isRequesting: false,
isSuccessful: true, isSuccessful: true,
hasError: null, hasError: null,

@ -1,7 +1,7 @@
export interface ModalDialogProps { export interface ModalDialogProps {
id?: string id?: string
title?: string, title?: string,
message?: string, message?: string | JSX.Element,
okLabel?: string, okLabel?: string,
okFn?: () => void, okFn?: () => void,
cancelLabel?: string, cancelLabel?: string,

@ -0,0 +1,4 @@
{
"presets": ["@nrwl/react/babel"],
"plugins": []
}

@ -0,0 +1,18 @@
{
"env": {
"browser": true,
"es6": true
},
"extends": "../../../.eslintrc",
"globals": {
"Atomics": "readonly",
"SharedArrayBuffer": "readonly"
},
"parserOptions": {
"ecmaVersion": 11,
"sourceType": "module"
},
"rules": {
"standard/no-callback-literal": "off"
}
}

@ -0,0 +1,7 @@
# remix-ui-publish-to-storage
This library was generated with [Nx](https://nx.dev).
## Running unit tests
Run `nx test remix-ui-publish-to-storage` to execute the unit tests via [Jest](https://jestjs.io).

@ -0,0 +1 @@
export * from './lib/publish-to-storage'

@ -0,0 +1,110 @@
import React, { useEffect, useState } from 'react' // eslint-disable-line
import { ModalDialog } from '@remix-ui/modal-dialog' // eslint-disable-line
import { RemixUiPublishToStorageProps } from './types' // eslint-disable-line
import { publishToIPFS } from './publishToIPFS'
import { publishToSwarm } from './publishOnSwarm'
export const PublishToStorage = (props: RemixUiPublishToStorageProps) => {
const { storage, fileProvider, fileManager, contract, resetStorage } = props
const [state, setState] = useState({
modal: {
title: '',
message: null,
hide: true,
okLabel: '',
okFn: null,
cancelLabel: '',
cancelFn: null
}
})
useEffect(() => {
const storageService = async () => {
if ((contract.metadata === undefined || contract.metadata.length === 0)) {
modal('Publish To Storage', 'This contract may be abstract, may not implement an abstract parent\'s methods completely or not invoke an inherited contract\'s constructor correctly.')
} else {
if (storage === 'swarm') {
try {
const result = await publishToSwarm(contract, fileManager)
modal(`Published ${contract.name}'s Metadata`, publishMessage(result.uploaded))
// triggered each time there's a new verified publish (means hash correspond)
fileProvider.addExternal('swarm/' + result.item.hash, result.item.content)
} catch (err) {
let parseError = err
try {
parseError = JSON.stringify(err)
} catch (e) {}
modal('Swarm Publish Failed', publishMessageFailed(storage, parseError))
}
} else {
try {
const result = await publishToIPFS(contract, fileManager)
modal(`Published ${contract.name}'s Metadata`, publishMessage(result.uploaded))
// triggered each time there's a new verified publish (means hash correspond)
fileProvider.addExternal('ipfs/' + result.item.hash, result.item.content)
} catch (err) {
modal('IPFS Publish Failed', publishMessageFailed(storage, err))
}
}
}
}
if (storage) {
storageService()
}
}, [storage])
const publishMessage = (uploaded) => (
<span> Metadata of "{contract.name.toLowerCase()}" was published successfully. <br />
<pre>
<div>
{ uploaded.map((value, index) => <div key={index}><b>{ value.filename }</b> : <pre>{ value.output.url }</pre></div>) }
</div>
</pre>
</span>
)
const publishMessageFailed = (storage, err) => (
<span>Failed to publish metadata file to { storage }, please check the { storage } gateways is available. <br />
{err}
</span>
)
const handleHideModal = () => {
setState(prevState => {
return { ...prevState, modal: { ...prevState.modal, hide: true, message: null } }
})
resetStorage()
}
const modal = async (title: string, message: string | JSX.Element) => {
await setState(prevState => {
return {
...prevState,
modal: {
...prevState.modal,
hide: false,
message,
title
}
}
})
}
return (
<ModalDialog
id='publishToStorage'
title={ state.modal.title }
message={ state.modal.message }
hide={ state.modal.hide }
okLabel='OK'
okFn={() => {}}
handleHide={ handleHideModal }>
{ (typeof state.modal.message !== 'string') && state.modal.message }
</ModalDialog>
)
}
export default PublishToStorage

@ -0,0 +1,109 @@
import swarm from 'swarmgw'
const swarmgw = swarm()
export const publishToSwarm = async (contract, fileManager) => {
// gather list of files to publish
const sources = []
let metadata
const item = { content: null, hash: null }
const uploaded = []
try {
metadata = JSON.parse(contract.metadata)
} catch (e) {
throw new Error(e)
}
if (metadata === undefined) {
throw new Error('No metadata')
}
await Promise.all(Object.keys(metadata.sources).map(fileName => {
// find hash
let hash = null
try {
// we try extract the hash defined in the metadata.json
// in order to check if the hash that we get after publishing is the same as the one located in metadata.json
// if it's not the same, we throw "hash mismatch between solidity bytecode and uploaded content"
// if we don't find the hash in the metadata.json, the check is not done.
//
// TODO: refactor this with publishOnIpfs
if (metadata.sources[fileName].urls) {
metadata.sources[fileName].urls.forEach(url => {
if (url.includes('bzz')) hash = url.match('(bzzr|bzz-raw)://(.+)')[1]
})
}
} catch (e) {
throw new Error('Error while extracting the hash from metadata.json')
}
fileManager.fileProviderOf(fileName).get(fileName, (error, content) => {
if (error) {
console.log(error)
} else {
sources.push({
content: content,
hash: hash,
filename: fileName
})
}
})
}))
// publish the list of sources in order, fail if any failed
await Promise.all(sources.map(async (item) => {
try {
const result = await swarmVerifiedPublish(item.content, item.hash)
try {
item.hash = result.url.match('bzz-raw://(.+)')[1]
} catch (e) {
item.hash = '<Metadata inconsistency> - ' + item.fileName
}
item.output = result
uploaded.push(item)
// TODO this is a fix cause Solidity metadata does not contain the right swarm hash (poc 0.3)
metadata.sources[item.filename].urls[0] = result.url
} catch (error) {
throw new Error(error)
}
}))
const metadataContent = JSON.stringify(metadata)
try {
const result = await swarmVerifiedPublish(metadataContent, '')
try {
contract.metadataHash = result.url.match('bzz-raw://(.+)')[1]
} catch (e) {
contract.metadataHash = '<Metadata inconsistency> - metadata.json'
}
item.content = metadataContent
item.hash = contract.metadataHash
uploaded.push({
content: contract.metadata,
hash: contract.metadataHash,
filename: 'metadata.json',
output: result
})
} catch (error) {
throw new Error(error)
}
return { uploaded, item }
}
const swarmVerifiedPublish = async (content, expectedHash): Promise<Record<string, any>> => {
return new Promise((resolve, reject) => {
swarmgw.put(content, function (err, ret) {
if (err) {
reject(err)
} else if (expectedHash && ret !== expectedHash) {
resolve({ message: 'hash mismatch between solidity bytecode and uploaded content.', url: 'bzz-raw://' + ret, hash: ret })
} else {
resolve({ message: 'ok', url: 'bzz-raw://' + ret, hash: ret })
}
})
})
}

@ -0,0 +1,117 @@
import IpfsClient from 'ipfs-mini'
const ipfsNodes = [
new IpfsClient({ host: 'ipfs.komputing.org', port: 443, protocol: 'https' }),
new IpfsClient({ host: 'ipfs.infura.io', port: 5001, protocol: 'https' }),
new IpfsClient({ host: '127.0.0.1', port: 5001, protocol: 'http' })
]
export const publishToIPFS = async (contract, fileManager) => {
// gather list of files to publish
const sources = []
let metadata
const item = { content: null, hash: null }
const uploaded = []
try {
metadata = JSON.parse(contract.metadata)
} catch (e) {
throw new Error(e)
}
if (metadata === undefined) {
throw new Error('No metadata')
}
await Promise.all(Object.keys(metadata.sources).map(fileName => {
// find hash
let hash = null
try {
// we try extract the hash defined in the metadata.json
// in order to check if the hash that we get after publishing is the same as the one located in metadata.json
// if it's not the same, we throw "hash mismatch between solidity bytecode and uploaded content"
// if we don't find the hash in the metadata.json, the check is not done.
//
// TODO: refactor this with publishOnSwarm
if (metadata.sources[fileName].urls) {
metadata.sources[fileName].urls.forEach(url => {
if (url.includes('ipfs')) hash = url.match('dweb:/ipfs/(.+)')[1]
})
}
} catch (e) {
throw new Error('Error while extracting the hash from metadata.json')
}
fileManager.fileProviderOf(fileName).get(fileName, (error, content) => {
if (error) {
console.log(error)
} else {
sources.push({
content: content,
hash: hash,
filename: fileName
})
}
})
}))
// publish the list of sources in order, fail if any failed
await Promise.all(sources.map(async (item) => {
try {
const result = await ipfsVerifiedPublish(item.content, item.hash)
try {
item.hash = result.url.match('dweb:/ipfs/(.+)')[1]
} catch (e) {
item.hash = '<Metadata inconsistency> - ' + item.fileName
}
item.output = result
uploaded.push(item)
} catch (error) {
throw new Error(error)
}
}))
const metadataContent = JSON.stringify(metadata)
try {
const result = await ipfsVerifiedPublish(metadataContent, '')
try {
contract.metadataHash = result.url.match('dweb:/ipfs/(.+)')[1]
} catch (e) {
contract.metadataHash = '<Metadata inconsistency> - metadata.json'
}
item.content = metadataContent
item.hash = contract.metadataHash
uploaded.push({
content: contract.metadata,
hash: contract.metadataHash,
filename: 'metadata.json',
output: result
})
} catch (error) {
throw new Error(error)
}
return { uploaded, item }
}
const ipfsVerifiedPublish = async (content, expectedHash) => {
try {
const results = await severalGatewaysPush(content)
if (expectedHash && results !== expectedHash) {
return { message: 'hash mismatch between solidity bytecode and uploaded content.', url: 'dweb:/ipfs/' + results, hash: results }
} else {
return { message: 'ok', url: 'dweb:/ipfs/' + results, hash: results }
}
} catch (error) {
throw new Error(error)
}
}
const severalGatewaysPush = (content) => {
const invert = p => new Promise((resolve, reject) => p.then(reject).catch(resolve)) // Invert res and rej
const promises = ipfsNodes.map((node) => invert(node.add(content)))
return invert(Promise.all(promises))
}

@ -0,0 +1,7 @@
export interface RemixUiPublishToStorageProps {
storage: string,
fileProvider: any,
fileManager: any,
contract: any,
resetStorage: () => void
}

@ -0,0 +1,16 @@
{
"extends": "../../../tsconfig.json",
"compilerOptions": {
"jsx": "react",
"allowJs": true,
"esModuleInterop": true,
"allowSyntheticDefaultImports": true
},
"files": [],
"include": [],
"references": [
{
"path": "./tsconfig.lib.json"
}
]
}

@ -0,0 +1,13 @@
{
"extends": "./tsconfig.json",
"compilerOptions": {
"outDir": "../../../dist/out-tsc",
"types": ["node"]
},
"files": [
"../../../node_modules/@nrwl/react/typings/cssmodule.d.ts",
"../../../node_modules/@nrwl/react/typings/image.d.ts"
],
"exclude": ["**/*.spec.ts", "**/*.spec.tsx"],
"include": ["**/*.js", "**/*.jsx", "**/*.ts", "**/*.tsx"]
}

@ -0,0 +1,4 @@
{
"presets": ["@nrwl/react/babel"],
"plugins": []
}

@ -0,0 +1,19 @@
{
"env": {
"browser": true,
"es6": true
},
"extends": "../../../.eslintrc",
"globals": {
"Atomics": "readonly",
"SharedArrayBuffer": "readonly"
},
"parserOptions": {
"ecmaVersion": 11,
"sourceType": "module"
},
"rules": {
"no-unused-vars": "off",
"@typescript-eslint/no-unused-vars": "error"
}
}

@ -0,0 +1,7 @@
# remix-ui-renderer
This library was generated with [Nx](https://nx.dev).
## Running unit tests
Run `nx test remix-ui-renderer` to execute the unit tests via [Jest](https://jestjs.io).

@ -0,0 +1 @@
export * from './lib/renderer'

@ -0,0 +1,47 @@
.remixui_sol.success,
.remixui_sol.error,
.remixui_sol.warning {
white-space: pre-line;
word-wrap: break-word;
cursor: pointer;
position: relative;
margin: 0.5em 0 1em 0;
border-radius: 5px;
line-height: 20px;
padding: 8px 15px;
}
.remixui_sol.success pre,
.remixui_sol.error pre,
.remixui_sol.warning pre {
white-space: pre-line;
overflow-y: hidden;
background-color: transparent;
margin: 0;
font-size: 12px;
border: 0 none;
padding: 0;
border-radius: 0;
}
.remixui_sol.success .close,
.remixui_sol.error .close,
.remixui_sol.warning .close {
white-space: pre-line;
font-weight: bold;
position: absolute;
color: hsl(0, 0%, 0%); /* black in style-guide.js */
top: 0;
right: 0;
padding: 0.5em;
}
.remixui_sol.error {
}
.remixui_sol.warning {
}
.remixui_sol.success {
/* background-color: // styles.rightPanel.message_Success_BackgroundColor; */
}

@ -0,0 +1,127 @@
import React, { useEffect, useState } from 'react' //eslint-disable-line
import './renderer.css'
interface RendererProps {
message: any;
opt?: any,
plugin: any,
editor: any,
config: any,
fileManager: any
}
export const Renderer = ({ message, opt = {}, editor, config, fileManager, plugin }: RendererProps) => {
const [messageText, setMessageText] = useState(null)
const [editorOptions, setEditorOptions] = useState({
useSpan: false,
type: '',
errFile: ''
})
const [classList] = useState(opt.type === 'error' ? 'alert alert-danger' : 'alert alert-warning')
const [close, setClose] = useState(false)
useEffect(() => {
if (!message) return
let text
if (typeof message === 'string') {
text = message
} else if (message.innerText) {
text = message.innerText
}
// ^ e.g:
// browser/gm.sol: Warning: Source file does not specify required compiler version! Consider adding "pragma solidity ^0.6.12
// https://github.com/OpenZeppelin/openzeppelin-contracts/blob/release-v3.2.0/contracts/introspection/IERC1820Registry.sol:3:1: ParserError: Source file requires different compiler version (current compiler is 0.7.4+commit.3f05b770.Emscripten.clang) - note that nightly builds are considered to be strictly less than the released version
let positionDetails = getPositionDetails(text)
const options = opt
if (!positionDetails.errFile || (opt.errorType && opt.errorType === positionDetails.errFile)) {
// Updated error reported includes '-->' before file details
const errorDetails = text.split('-->')
// errorDetails[1] will have file details
if (errorDetails.length > 1) positionDetails = getPositionDetails(errorDetails[1])
}
options.errLine = positionDetails.errLine
options.errCol = positionDetails.errCol
options.errFile = positionDetails.errFile.trim()
if (!opt.noAnnotations && opt.errFile) {
addAnnotation(opt.errFile, {
row: opt.errLine,
column: opt.errCol,
text: text,
type: opt.type
})
}
setMessageText(text)
setEditorOptions(options)
setClose(false)
}, [message])
const getPositionDetails = (msg: any) => {
const result = { } as Record<string, number | string>
// To handle some compiler warning without location like SPDX license warning etc
if (!msg.includes(':')) return { errLine: -1, errCol: -1, errFile: msg }
// extract line / column
let pos = msg.match(/^(.*?):([0-9]*?):([0-9]*?)?/)
result.errLine = pos ? parseInt(pos[2]) - 1 : -1
result.errCol = pos ? parseInt(pos[3]) : -1
// extract file
pos = msg.match(/^(https:.*?|http:.*?|.*?):/)
result.errFile = pos ? pos[1] : ''
return result
}
const addAnnotation = (file, error) => {
if (file === config.get('currentFile')) {
plugin.call('editor', 'addAnnotation', error, file)
}
}
const handleErrorClick = (opt) => {
if (opt.click) {
opt.click(message)
} else if (opt.errFile !== undefined && opt.errLine !== undefined && opt.errCol !== undefined) {
_errorClick(opt.errFile, opt.errLine, opt.errCol)
}
}
const handleClose = () => {
setClose(true)
}
const _errorClick = (errFile, errLine, errCol) => {
if (errFile !== config.get('currentFile')) {
// TODO: refactor with this._components.contextView.jumpTo
const provider = fileManager.fileProviderOf(errFile)
if (provider) {
provider.exists(errFile).then(() => {
fileManager.open(errFile)
editor.gotoLine(errLine, errCol)
}).catch(error => {
if (error) return console.log(error)
})
}
} else {
editor.gotoLine(errLine, errCol)
}
}
return (
<>
{
messageText && !close && (
<div className={`sol ${editorOptions.type} ${classList}`} data-id={editorOptions.errFile} onClick={() => handleErrorClick(editorOptions)}>
{ editorOptions.useSpan ? <span> { messageText } </span> : <pre><span>{ messageText }</span></pre> }
<div className="close" data-id="renderer" onClick={handleClose}>
<i className="fas fa-times"></i>
</div>
</div>)
}
</>
)
}

@ -0,0 +1,16 @@
{
"extends": "../../../tsconfig.json",
"compilerOptions": {
"jsx": "react",
"allowJs": true,
"esModuleInterop": true,
"allowSyntheticDefaultImports": true
},
"files": [],
"include": [],
"references": [
{
"path": "./tsconfig.lib.json"
}
]
}

@ -0,0 +1,13 @@
{
"extends": "./tsconfig.json",
"compilerOptions": {
"outDir": "../../../dist/out-tsc",
"types": ["node"]
},
"files": [
"../../../node_modules/@nrwl/react/typings/cssmodule.d.ts",
"../../../node_modules/@nrwl/react/typings/image.d.ts"
],
"exclude": ["**/*.spec.ts", "**/*.spec.tsx"],
"include": ["**/*.js", "**/*.jsx", "**/*.ts", "**/*.tsx"]
}

@ -0,0 +1,4 @@
{
"presets": ["@nrwl/react/babel"],
"plugins": []
}

@ -0,0 +1,19 @@
{
"env": {
"browser": true,
"es6": true
},
"extends": "../../../.eslintrc",
"globals": {
"Atomics": "readonly",
"SharedArrayBuffer": "readonly"
},
"parserOptions": {
"ecmaVersion": 11,
"sourceType": "module"
},
"rules": {
"no-unused-vars": "off",
"@typescript-eslint/no-unused-vars": "error"
}
}

@ -0,0 +1,7 @@
# remix-ui-solidity-compiler
This library was generated with [Nx](https://nx.dev).
## Running unit tests
Run `nx test remix-ui-solidity-compiler` to execute the unit tests via [Jest](https://jestjs.io).

@ -0,0 +1,2 @@
export * from './lib/solidity-compiler'
export * from './lib/logic'

@ -0,0 +1,57 @@
import React from 'react'
export const setEditorMode = (mode: string) => {
return {
type: 'SET_EDITOR_MODE',
payload: mode
}
}
export const resetEditorMode = () => (dispatch: React.Dispatch<any>) => {
dispatch({
type: 'RESET_EDITOR_MODE'
})
}
export const setCompilerMode = (mode: string, ...args) => {
return {
type: 'SET_COMPILER_MODE',
payload: { mode, args }
}
}
export const resetCompilerMode = () => (dispatch: React.Dispatch<any>) => {
dispatch({
type: 'RESET_COMPILER_MODE'
})
}
export const listenToEvents = (editor, compileTabLogic) => (dispatch: React.Dispatch<any>) => {
editor.event.register('sessionSwitched', () => {
dispatch(setEditorMode('sessionSwitched'))
})
compileTabLogic.event.on('startingCompilation', () => {
dispatch(setCompilerMode('startingCompilation'))
})
compileTabLogic.compiler.event.register('compilationDuration', (speed) => {
dispatch(setCompilerMode('compilationDuration', speed))
})
editor.event.register('contentChanged', () => {
dispatch(setEditorMode('contentChanged'))
})
compileTabLogic.compiler.event.register('loadingCompiler', () => {
dispatch(setCompilerMode('compilationDuration'))
})
compileTabLogic.compiler.event.register('compilerLoaded', () => {
dispatch(setCompilerMode('compilerLoaded'))
})
compileTabLogic.compiler.event.register('compilationFinished', (success, data, source) => {
dispatch(setCompilerMode('compilationFinished', success, data, source))
})
}

@ -0,0 +1,582 @@
import React, { useEffect, useState, useRef, useReducer } from 'react' // eslint-disable-line
import semver from 'semver'
import { CompilerContainerProps, ConfigurationSettings } from './types'
import * as helper from '../../../../../apps/remix-ide/src/lib/helper'
import { canUseWorker, baseURLBin, baseURLWasm, urlFromVersion, pathToURL, promisedMiniXhr } from '@remix-project/remix-solidity' // @ts-ignore
import { compilerReducer, compilerInitialState } from './reducers/compiler'
import { resetEditorMode, listenToEvents } from './actions/compiler'
import './css/style.css'
export const CompilerContainer = (props: CompilerContainerProps) => {
const { editor, config, queryParams, compileTabLogic, tooltip, modal, compiledFileName, setHardHatCompilation, updateCurrentVersion, isHardHatProject, configurationSettings } = props // eslint-disable-line
const [state, setState] = useState({
hideWarnings: false,
autoCompile: false,
optimise: false,
compileTimeout: null,
timeout: 300,
allversions: [],
customVersions: [],
selectedVersion: null,
defaultVersion: 'soljson-v0.8.4+commit.c7e474f2.js', // this default version is defined: in makeMockCompiler (for browser test)
selectedLanguage: '',
runs: '',
compiledFileName: '',
includeNightlies: false,
language: '',
evmVersion: ''
})
const compileIcon = useRef(null)
const warningIcon = useRef(null)
const promptMessageInput = useRef(null)
const [hhCompilation, sethhCompilation] = useState(false)
const [compilerContainer, dispatch] = useReducer(compilerReducer, compilerInitialState)
useEffect(() => {
fetchAllVersion((allversions, selectedVersion, isURL) => {
setState(prevState => {
return { ...prevState, allversions }
})
if (isURL) _updateVersionSelector(state.defaultVersion, selectedVersion)
else {
setState(prevState => {
return { ...prevState, selectedVersion }
})
updateCurrentVersion(selectedVersion)
_updateVersionSelector(selectedVersion)
}
})
const currentFileName = config.get('currentFile')
currentFile(currentFileName)
listenToEvents(editor, compileTabLogic)(dispatch)
}, [])
useEffect(() => {
if (compileTabLogic && compileTabLogic.compiler) {
setState(prevState => {
const params = queryParams.get()
const optimize = params.optimize === 'false' ? false : params.optimize === 'true' ? true : null
const runs = params.runs
const evmVersion = params.evmVersion
return {
...prevState,
hideWarnings: config.get('hideWarnings') || false,
autoCompile: config.get('autoCompile') || false,
includeNightlies: config.get('includeNightlies') || false,
optimise: (optimize !== null) && (optimize !== undefined) ? optimize : config.get('optimise') || false,
runs: (runs !== null) && (runs !== 'null') && (runs !== undefined) && (runs !== 'undefined') ? runs : 200,
evmVersion: (evmVersion !== null) && (evmVersion !== 'null') && (evmVersion !== undefined) && (evmVersion !== 'undefined') ? evmVersion : 'default'
}
})
}
}, [compileTabLogic])
useEffect(() => {
setState(prevState => {
return { ...prevState, compiledFileName }
})
}, [compiledFileName])
useEffect(() => {
if (compilerContainer.compiler.mode) {
switch (compilerContainer.compiler.mode) {
case 'startingCompilation':
startingCompilation()
break
case 'compilationDuration':
compilationDuration(compilerContainer.compiler.args[0])
break
case 'loadingCompiler':
loadingCompiler()
break
case 'compilerLoaded':
compilerLoaded()
break
case 'compilationFinished':
compilationFinished()
break
}
}
}, [compilerContainer.compiler.mode])
useEffect(() => {
if (compilerContainer.editor.mode) {
switch (compilerContainer.editor.mode) {
case 'sessionSwitched':
sessionSwitched()
resetEditorMode()(dispatch)
break
case 'contentChanged':
contentChanged()
resetEditorMode()(dispatch)
break
}
}
}, [compilerContainer.editor.mode])
useEffect(() => {
if (configurationSettings) {
setConfiguration(configurationSettings)
}
}, [configurationSettings])
// fetching both normal and wasm builds and creating a [version, baseUrl] map
const fetchAllVersion = async (callback) => {
let selectedVersion, allVersionsWasm, isURL
let allVersions = [{ path: 'builtin', longVersion: 'latest local version - 0.7.4' }]
// fetch normal builds
const binRes: any = await promisedMiniXhr(`${baseURLBin}/list.json`)
// fetch wasm builds
const wasmRes: any = await promisedMiniXhr(`${baseURLWasm}/list.json`)
if (binRes.event.type === 'error' && wasmRes.event.type === 'error') {
selectedVersion = 'builtin'
return callback(allVersions, selectedVersion)
}
try {
const versions = JSON.parse(binRes.json).builds.slice().reverse()
allVersions = [...allVersions, ...versions]
selectedVersion = state.defaultVersion
if (queryParams.get().version) selectedVersion = queryParams.get().version
// Check if version is a URL and corresponding filename starts with 'soljson'
if (selectedVersion.startsWith('https://')) {
const urlArr = selectedVersion.split('/')
if (urlArr[urlArr.length - 1].startsWith('soljson')) isURL = true
}
if (wasmRes.event.type !== 'error') {
allVersionsWasm = JSON.parse(wasmRes.json).builds.slice().reverse()
}
} catch (e) {
tooltip('Cannot load compiler version list. It might have been blocked by an advertisement blocker. Please try deactivating any of them from this page and reload. Error: ' + e)
}
// replace in allVersions those compiler builds which exist in allVersionsWasm with new once
if (allVersionsWasm && allVersions) {
allVersions.forEach((compiler, index) => {
const wasmIndex = allVersionsWasm.findIndex(wasmCompiler => { return wasmCompiler.longVersion === compiler.longVersion })
if (wasmIndex !== -1) {
allVersions[index] = allVersionsWasm[wasmIndex]
pathToURL[compiler.path] = baseURLWasm
} else {
pathToURL[compiler.path] = baseURLBin
}
})
}
callback(allVersions, selectedVersion, isURL)
}
/**
* Update the compilation button with the name of the current file
*/
const currentFile = (name = '') => {
if (name && name !== '') {
_setCompilerVersionFromPragma(name)
}
const compiledFileName = name.split('/').pop()
setState(prevState => {
return { ...prevState, compiledFileName }
})
}
// Load solc compiler version according to pragma in contract file
const _setCompilerVersionFromPragma = (filename: string) => {
if (!state.allversions) return
compileTabLogic.fileManager.readFile(filename).then(data => {
const pragmaArr = data.match(/(pragma solidity (.+?);)/g)
if (pragmaArr && pragmaArr.length === 1) {
const pragmaStr = pragmaArr[0].replace('pragma solidity', '').trim()
const pragma = pragmaStr.substring(0, pragmaStr.length - 1)
const releasedVersions = state.allversions.filter(obj => !obj.prerelease).map(obj => obj.version)
const allVersions = state.allversions.map(obj => _retrieveVersion(obj.version))
const currentCompilerName = _retrieveVersion(state.selectedVersion)
// contains only numbers part, for example '0.4.22'
const pureVersion = _retrieveVersion()
// is nightly build newer than the last release
const isNewestNightly = currentCompilerName.includes('nightly') && semver.gt(pureVersion, releasedVersions[0])
// checking if the selected version is in the pragma range
const isInRange = semver.satisfies(pureVersion, pragma)
// checking if the selected version is from official compilers list(excluding custom versions) and in range or greater
const isOfficial = allVersions.includes(currentCompilerName)
if (isOfficial && (!isInRange && !isNewestNightly)) {
const compilerToLoad = semver.maxSatisfying(releasedVersions, pragma)
const compilerPath = state.allversions.filter(obj => !obj.prerelease && obj.version === compilerToLoad)[0].path
if (state.selectedVersion !== compilerPath) {
setState((prevState) => {
return { ...prevState, selectedVersion: compilerPath }
})
_updateVersionSelector(compilerPath)
}
}
}
})
}
const isSolFileSelected = (currentFile = '') => {
if (!currentFile) currentFile = config.get('currentFile')
if (!currentFile) return false
const extention = currentFile.substr(currentFile.length - 3, currentFile.length)
return extention.toLowerCase() === 'sol' || extention.toLowerCase() === 'yul'
}
const sessionSwitched = () => {
if (!compileIcon.current) return
scheduleCompilation()
}
const startingCompilation = () => {
if (!compileIcon.current) return
compileIcon.current.setAttribute('title', 'compiling...')
compileIcon.current.classList.remove('remixui_bouncingIcon')
compileIcon.current.classList.add('remixui_spinningIcon')
}
const compilationDuration = (speed: number) => {
if (!warningIcon.current) return
if (speed > 1000) {
const msg = `Last compilation took ${speed}ms. We suggest to turn off autocompilation.`
warningIcon.current.setAttribute('title', msg)
warningIcon.current.style.visibility = 'visible'
} else {
warningIcon.current.style.visibility = 'hidden'
}
}
const contentChanged = () => {
if (!compileIcon.current) return
scheduleCompilation()
compileIcon.current.classList.add('remixui_bouncingIcon') // @TODO: compileView tab
}
const loadingCompiler = () => {
if (!compileIcon.current) return
compileIcon.current.setAttribute('title', 'compiler is loading, please wait a few moments.')
compileIcon.current.classList.add('remixui_spinningIcon')
warningIcon.current.style.visibility = 'hidden'
_updateLanguageSelector()
}
const compilerLoaded = () => {
if (!compileIcon.current) return
compileIcon.current.setAttribute('title', '')
compileIcon.current.classList.remove('remixui_spinningIcon')
if (state.autoCompile) compile()
}
const compilationFinished = () => {
if (!compileIcon.current) return
compileIcon.current.setAttribute('title', 'idle')
compileIcon.current.classList.remove('remixui_spinningIcon')
compileIcon.current.classList.remove('remixui_bouncingIcon')
}
const scheduleCompilation = () => {
if (!state.autoCompile) return
if (state.compileTimeout) window.clearTimeout(state.compileTimeout)
const compileTimeout = window.setTimeout(() => {
state.autoCompile && compile()
}, state.timeout)
setState(prevState => {
return { ...prevState, compileTimeout }
})
}
const compile = () => {
const currentFile = config.get('currentFile')
if (!isSolFileSelected()) return
_setCompilerVersionFromPragma(currentFile)
compileTabLogic.runCompiler()
}
const _retrieveVersion = (version?) => {
if (!version) version = state.selectedVersion
if (version === 'builtin') version = state.defaultVersion
return semver.coerce(version) ? semver.coerce(version).version : ''
}
const _updateVersionSelector = (version, customUrl = '') => {
// update selectedversion of previous one got filtered out
let selectedVersion = version
if (!selectedVersion || !_shouldBeAdded(selectedVersion)) {
selectedVersion = state.defaultVersion
setState(prevState => {
return { ...prevState, selectedVersion }
})
}
updateCurrentVersion(selectedVersion)
queryParams.update({ version: selectedVersion })
let url
if (customUrl !== '') {
selectedVersion = customUrl
setState(prevState => {
return { ...prevState, selectedVersion, customVersions: [...state.customVersions, selectedVersion] }
})
updateCurrentVersion(selectedVersion)
url = customUrl
queryParams.update({ version: selectedVersion })
} else if (selectedVersion === 'builtin') {
let location: string | Location = window.document.location
let path = location.pathname
if (!path.startsWith('/')) path = '/' + path
location = `${location.protocol}//${location.host}${path}assets/js`
if (location.endsWith('index.html')) location = location.substring(0, location.length - 10)
if (!location.endsWith('/')) location += '/'
url = location + 'soljson.js'
} else {
if (selectedVersion.indexOf('soljson') !== 0 || helper.checkSpecialChars(selectedVersion)) {
return console.log('loading ' + selectedVersion + ' not allowed')
}
url = `${urlFromVersion(selectedVersion)}`
}
// Workers cannot load js on "file:"-URLs and we get a
// "Uncaught RangeError: Maximum call stack size exceeded" error on Chromium,
// resort to non-worker version in that case.
if (selectedVersion !== 'builtin' && canUseWorker(selectedVersion)) {
compileTabLogic.compiler.loadVersion(true, url)
} else {
compileTabLogic.compiler.loadVersion(false, url)
}
}
const _shouldBeAdded = (version) => {
return !version.includes('nightly') ||
(version.includes('nightly') && state.includeNightlies)
}
const promptCompiler = () => {
// custom url https://solidity-blog.s3.eu-central-1.amazonaws.com/data/08preview/soljson.js
modal('Add a custom compiler', promptMessage('URL'), 'OK', addCustomCompiler, 'Cancel', () => {})
}
const promptMessage = (message) => {
return (
<>
<span>{ message }</span>
<input type="text" data-id="modalDialogCustomPromptCompiler" className="form-control" ref={promptMessageInput} />
</>
)
}
const addCustomCompiler = () => {
const url = promptMessageInput.current.value
setState(prevState => {
return { ...prevState, selectedVersion: url }
})
_updateVersionSelector(state.defaultVersion, url)
}
const handleLoadVersion = (value) => {
setState(prevState => {
return { ...prevState, selectedVersion: value }
})
updateCurrentVersion(value)
_updateVersionSelector(value)
_updateLanguageSelector()
}
const _updateLanguageSelector = () => {
// This is the first version when Yul is available
if (!semver.valid(_retrieveVersion()) || semver.lt(_retrieveVersion(), 'v0.5.7+commit.6da8b019.js')) {
handleLanguageChange('Solidity')
compileTabLogic.setLanguage('Solidity')
}
}
const handleAutoCompile = (e) => {
const checked = e.target.checked
config.set('autoCompile', checked)
setState(prevState => {
return { ...prevState, autoCompile: checked }
})
}
const handleOptimizeChange = (value) => {
const checked = !!value
config.set('optimise', checked)
compileTabLogic.setOptimize(checked)
if (compileTabLogic.optimize) {
compileTabLogic.setRuns(parseInt(state.runs))
} else {
compileTabLogic.setRuns(200)
}
state.autoCompile && compile()
setState(prevState => {
return { ...prevState, optimise: checked }
})
}
const onChangeRuns = (value) => {
const runs = value
compileTabLogic.setRuns(parseInt(runs))
state.autoCompile && compile()
setState(prevState => {
return { ...prevState, runs }
})
}
const handleHideWarningsChange = (e) => {
const checked = e.target.checked
config.set('hideWarnings', checked)
state.autoCompile && compile()
setState(prevState => {
return { ...prevState, hideWarnings: checked }
})
}
const handleNightliesChange = (e) => {
const checked = e.target.checked
config.set('includeNightlies', checked)
setState(prevState => {
return { ...prevState, includeNightlies: checked }
})
}
const handleLanguageChange = (value) => {
compileTabLogic.setLanguage(value)
state.autoCompile && compile()
setState(prevState => {
return { ...prevState, language: value }
})
}
const handleEvmVersionChange = (value) => {
if (!value) return
let v = value
if (v === 'default') {
v = null
}
compileTabLogic.setEvmVersion(v)
state.autoCompile && compile()
setState(prevState => {
return { ...prevState, evmVersion: value }
})
}
const updatehhCompilation = (event) => {
const checked = event.target.checked
sethhCompilation(checked)
setHardHatCompilation(checked)
}
/*
The following functions map with the above event handlers.
They are an external API for modifying the compiler configuration.
*/
const setConfiguration = (settings: ConfigurationSettings) => {
handleLoadVersion(`soljson-v${settings.version}.js`)
handleEvmVersionChange(settings.evmVersion)
handleLanguageChange(settings.language)
handleOptimizeChange(settings.optimize)
onChangeRuns(settings.runs)
}
return (
<section>
<article>
<header className='remixui_compilerSection border-bottom'>
<div className="mb-2">
<label className="remixui_compilerLabel form-check-label" htmlFor="versionSelector">
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>
</label>
<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 === 'builtin' ? 'selected' : ''}>builtin</option> }
{ state.customVersions.map((url, i) => <option key={i} data-id={state.selectedVersion === url ? 'selected' : ''} value={url}>custom</option>)}
{ state.allversions.map((build, i) => {
return _shouldBeAdded(build.longVersion)
? <option key={i} value={build.path} data-id={state.selectedVersion === build.path ? 'selected' : ''}>{build.longVersion}</option>
: null
})
}
</select>
</div>
<div className="mb-2 remixui_nightlyBuilds custom-control custom-checkbox">
<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>
</div>
<div className="mb-2">
<label className="remixui_compilerLabel form-check-label" htmlFor="compilierLanguageSelector">Language</label>
<select onChange={(e) => handleLanguageChange(e.target.value)} value={state.language} className="custom-select" id="compilierLanguageSelector" title="Available since v0.5.7">
<option value='Solidity'>Solidity</option>
<option value='Yul'>Yul</option>
</select>
</div>
<div className="mb-2">
<label className="remixui_compilerLabel form-check-label" htmlFor="evmVersionSelector">EVM Version</label>
<select value={state.evmVersion} onChange={(e) => handleEvmVersionChange(e.target.value)} className="custom-select" id="evmVersionSelector">
<option data-id={state.evmVersion === 'default' ? 'selected' : ''} value="default">compiler default</option>
<option data-id={state.evmVersion === 'muirGlacier' ? 'selected' : ''} value="muirGlacier">muirGlacier</option>
<option data-id={state.evmVersion === 'istanbul' ? 'selected' : ''} value="istanbul">istanbul</option>
<option data-id={state.evmVersion === 'petersburg' ? 'selected' : ''} value="petersburg">petersburg</option>
<option data-id={state.evmVersion === 'constantinople' ? 'selected' : ''} value="constantinople">constantinople</option>
<option data-id={state.evmVersion === 'byzantium' ? 'selected' : ''} value="byzantium">byzantium</option>
<option data-id={state.evmVersion === 'spuriousDragon' ? 'selected' : ''} value="spuriousDragon">spuriousDragon</option>
<option data-id={state.evmVersion === 'tangerineWhistle' ? 'selected' : ''} value="tangerineWhistle">tangerineWhistle</option>
<option data-id={state.evmVersion === 'homestead' ? 'selected' : ''} value="homestead">homestead</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.optimise} />
<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.optimise}
/>
</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>
{
isHardHatProject && <div className="mt-2 remixui_compilerConfig custom-control custom-checkbox">
<input className="remixui_autocompile custom-control-input" onChange={updatehhCompilation} id="enableHardhat" type="checkbox" title="Enable Hardhat Compilation" checked={hhCompilation} />
<label className="form-check-label custom-control-label" htmlFor="enableHardhat">Enable Hardhat Compilation</label>
</div>
}
<button id="compileBtn" data-id="compilerContainerCompileBtn" className="btn btn-primary btn-block remixui_disabled mt-3" title="Compile" onClick={compile} disabled={!state.compiledFileName || (state.compiledFileName && !isSolFileSelected(state.compiledFileName))}>
<span>
<i ref={warningIcon} title="Compilation Slow" style={{ visibility: 'hidden' }} className="remixui_warnCompilationSlow fas fa-exclamation-triangle" aria-hidden="true"></i>
{ warningIcon.current && warningIcon.current.style.visibility === 'hidden' && <i ref={compileIcon} className="fas fa-sync remixui_icon" aria-hidden="true"></i> }
Compile { state.compiledFileName || '<no file selected>' }
</span>
</button>
</header>
</article>
</section>
)
}
export default CompilerContainer

@ -0,0 +1,238 @@
import React, { useState, useEffect } from 'react' // eslint-disable-line
import { ContractSelectionProps } from './types'
import { PublishToStorage } from '@remix-ui/publish-to-storage' // eslint-disable-line
import { TreeView, TreeViewItem } from '@remix-ui/tree-view' // eslint-disable-line
import { CopyToClipboard } from '@remix-ui/clipboard' // eslint-disable-line
import './css/style.css'
export const ContractSelection = (props: ContractSelectionProps) => {
const { contractMap, fileProvider, fileManager, contractsDetails, modal } = props
const [contractList, setContractList] = useState([])
const [selectedContract, setSelectedContract] = useState('')
const [storage, setStorage] = useState(null)
useEffect(() => {
const contractList = contractMap ? Object.keys(contractMap).map((key) => ({
name: key,
file: getFileName(contractMap[key].file)
})) : []
setContractList(contractList)
if (contractList.length) setSelectedContract(contractList[0].name)
}, [contractMap, contractsDetails])
const resetStorage = () => {
setStorage('')
}
// Return the file name of a path: ex "browser/ballot.sol" -> "ballot.sol"
const getFileName = (path) => {
const part = path.split('/')
return part[part.length - 1]
}
const handleContractChange = (contractName: string) => {
setSelectedContract(contractName)
}
const handlePublishToStorage = (type) => {
setStorage(type)
}
const copyABI = () => {
return copyContractProperty('abi')
}
const copyContractProperty = (property) => {
let content = getContractProperty(property)
if (!content) {
return
}
try {
if (typeof content !== 'string') {
content = JSON.stringify(content, null, '\t')
}
} catch (e) {}
return content
}
const getContractProperty = (property) => {
if (!selectedContract) throw new Error('No contract compiled yet')
const contractProperties = contractsDetails[selectedContract]
if (contractProperties && contractProperties[property]) return contractProperties[property]
return null
}
const renderData = (item, key: string | number, keyPath: string) => {
const data = extractData(item)
const children = (data.children || []).map((child) => renderData(child.value, child.key, keyPath + '/' + child.key))
if (children && children.length > 0) {
return (
<TreeViewItem id={`treeViewItem${key}`} key={keyPath} label={
<div className="d-flex mt-2 flex-row remixui_label_item">
<label className="small font-weight-bold pr-1 remixui_label_key">{ key }:</label>
<label className="m-0 remixui_label_value">{ typeof data.self === 'boolean' ? `${data.self}` : data.self }</label>
</div>
}>
<TreeView id={`treeView${key}`} key={keyPath}>
{children}
</TreeView>
</TreeViewItem>
)
} else {
return <TreeViewItem id={key.toString()} key={keyPath} label={
<div className="d-flex mt-2 flex-row remixui_label_item">
<label className="small font-weight-bold pr-1 remixui_label_key">{ key }:</label>
<label className="m-0 remixui_label_value">{ typeof data.self === 'boolean' ? `${data.self}` : data.self }</label>
</div>
} />
}
}
const extractData = (item) => {
const ret = { children: null, self: null }
if (item instanceof Array) {
ret.children = item.map((item, index) => ({ key: index, value: item }))
ret.self = ''
} else if (item instanceof Object) {
ret.children = Object.keys(item).map((key) => ({ key: key, value: item[key] }))
ret.self = ''
} else {
ret.self = item
ret.children = []
}
return ret
}
const insertValue = (details, propertyName) => {
let node
if (propertyName === 'web3Deploy' || propertyName === 'name' || propertyName === 'Assembly') {
node = <pre>{ details[propertyName] }</pre>
} else if (propertyName === 'abi' || propertyName === 'metadata') {
if (details[propertyName] !== '') {
try {
node = <div>
{ (typeof details[propertyName] === 'object')
? <TreeView id="treeView">
{
Object.keys(details[propertyName]).map((innerkey) => renderData(details[propertyName][innerkey], innerkey, innerkey))
}
</TreeView> : <TreeView id="treeView">
{
Object.keys(JSON.parse(details[propertyName])).map((innerkey) => renderData(JSON.parse(details[propertyName])[innerkey], innerkey, innerkey))
}
</TreeView>
}
</div> // catch in case the parsing fails.
} catch (e) {
node = <div>Unable to display "${propertyName}": ${e.message}</div>
}
} else {
node = <div> - </div>
}
} else {
node = <div>{JSON.stringify(details[propertyName], null, 4)}</div>
}
return <pre className="remixui_value">{node || ''}</pre>
}
const details = () => {
if (!selectedContract) throw new Error('No contract compiled yet')
const help = {
Assembly: 'Assembly opcodes describing the contract including corresponding solidity source code',
Opcodes: 'Assembly opcodes describing the contract',
'Runtime Bytecode': 'Bytecode storing the state and being executed during normal contract call',
bytecode: 'Bytecode being executed during contract creation',
functionHashes: 'List of declared function and their corresponding hash',
gasEstimates: 'Gas estimation for each function call',
metadata: 'Contains all informations related to the compilation',
metadataHash: 'Hash representing all metadata information',
abi: 'ABI: describing all the functions (input/output params, scope, ...)',
name: 'Name of the compiled contract',
swarmLocation: 'Swarm url where all metadata information can be found (contract needs to be published first)',
web3Deploy: 'Copy/paste this code to any JavaScript/Web3 console to deploy this contract'
}
const contractProperties = contractsDetails[selectedContract] || {}
const log = <div className="remixui_detailsJSON">
{
Object.keys(contractProperties).map((propertyName, index) => {
const copyDetails = <span className="remixui_copyDetails"><CopyToClipboard content={contractProperties[propertyName]} direction='top' /></span>
const questionMark = <span className="remixui_questionMark"><i title={ help[propertyName] } className="fas fa-question-circle" aria-hidden="true"></i></span>
return (<div className="remixui_log" key={index}>
<div className="remixui_key">{ propertyName } { copyDetails } { questionMark }</div>
{ insertValue(contractProperties, propertyName) }
</div>)
})
}
</div>
modal(selectedContract, log, 'Close', null)
}
const copyBytecode = () => {
return copyContractProperty('bytecode')
}
return (
// define swarm logo
<>
{ contractList.length
? <section className="remixui_compilerSection pt-3">
{/* Select Compiler Version */}
<div className="mb-3">
<label className="remixui_compilerLabel form-check-label" htmlFor="compiledContracts">Contract</label>
<select onChange={(e) => handleContractChange(e.target.value)} value={selectedContract} data-id="compiledContracts" id="compiledContracts" className="custom-select">
{ contractList.map(({ name, file }, index) => <option value={name} key={index}>{name} ({file})</option>)}
</select>
</div>
<article className="mt-2 pb-0">
<button id="publishOnSwarm" className="btn btn-secondary btn-block" title="Publish on Swarm" onClick={() => { handlePublishToStorage('swarm') }}>
<span>Publish on Swarm</span>
<img id="swarmLogo" className="remixui_storageLogo ml-2" src="assets/img/swarm.webp" />
</button>
<button id="publishOnIpfs" className="btn btn-secondary btn-block" title="Publish on Ipfs" onClick={() => { handlePublishToStorage('ipfs') }}>
<span>Publish on Ipfs</span>
<img id="ipfsLogo" className="remixui_storageLogo ml-2" src="assets/img/ipfs.webp" />
</button>
<button data-id="compilation-details" className="btn btn-secondary btn-block" title="Display Contract Details" onClick={() => { details() }}>
Compilation Details
</button>
{/* Copy to Clipboard */}
<div className="remixui_contractHelperButtons">
<div className="input-group">
<div className="btn-group" role="group" aria-label="Copy to Clipboard">
<CopyToClipboard title="Copy ABI to clipboard" content={copyABI()} direction='top'>
<button className="btn remixui_copyButton" title="Copy ABI to clipboard">
<i className="remixui_copyIcon far fa-copy" aria-hidden="true"></i>
<span>ABI</span>
</button>
</CopyToClipboard>
<CopyToClipboard title="Copy ABI to clipboard" content={copyBytecode()} direction='top'>
<button className="btn remixui_copyButton" title="Copy Bytecode to clipboard">
<i className="remixui_copyIcon far fa-copy" aria-hidden="true"></i>
<span>Bytecode</span>
</button>
</CopyToClipboard>
</div>
</div>
</div>
</article>
</section> : <section className="remixui_container clearfix"><article className="px-2 mt-2 pb-0 d-flex w-100">
<span className="mt-2 mx-3 w-100 alert alert-warning" role="alert">No Contract Compiled Yet</span>
</article></section>
}
<PublishToStorage storage={storage} fileManager={fileManager} fileProvider={fileProvider} contract={contractsDetails[selectedContract]} resetStorage={resetStorage} />
</>
)
}
export default ContractSelection

@ -0,0 +1,234 @@
.remixui_title {
font-size: 1.1em;
font-weight: bold;
margin-bottom: 1em;
}
.remixui_panicError {
color: red;
font-size: 20px;
}
.remixui_crow {
display: flex;
overflow: auto;
clear: both;
padding: .2em;
}
.remixui_checkboxText {
font-weight: normal;
}
.remixui_crow label {
cursor:pointer;
}
.remixui_crowNoFlex {
overflow: auto;
clear: both;
}
.remixui_info {
padding: 10px;
word-break: break-word;
}
.remixui_contract {
display: block;
margin: 3% 0;
}
.remixui_nightlyBuilds {
display: flex;
flex-direction: row;
align-items: center;
}
.remixui_autocompileContainer {
display: flex;
align-items: center;
}
.remixui_runs {
width: 40%;
}
.remixui_hideWarningsContainer {
display: flex;
align-items: center;
}
.remixui_autocompile {}
.remixui_autocompileTitle {
font-weight: bold;
margin: 1% 0;
}
.remixui_autocompileText {
margin: 1% 0;
font-size: 12px;
overflow: hidden;
word-break: normal;
line-height: initial;
}
.remixui_warnCompilationSlow {
margin-left: 1%;
}
.remixui_compilerConfig {
display: flex;
align-items: center;
}
.remixui_compilerConfig label {
margin: 0;
}
.remixui_compilerSection {
padding: 12px 24px 16px;
}
.remixui_compilerLabel {
margin-bottom: 2px;
font-size: 11px;
line-height: 12px;
text-transform: uppercase;
}
.remixui_copyButton {
padding: 6px;
font-weight: bold;
font-size: 11px;
line-height: 15px;
}
.remixui_name {
display: flex;
}
.remixui_size {
display: flex;
}
.remixui_checkboxes {
display: flex;
width: 100%;
justify-content: space-between;
flex-wrap: wrap;
}
.remixui_compileButton {
width: 100%;
margin: 15px 0 10px 0;
font-size: 12px;
}
.remixui_container {
margin: 0;
margin-bottom: 2%;
}
.remixui_optimizeContainer {
display: flex;
}
.remixui_noContractAlert {
display: flex;
justify-content: center;
align-items: center;
}
.remixui_contractHelperButtons {
margin-top: 6px;
display: flex;
align-items: center;
justify-content: space-between;
float: right;
}
.remixui_copyToClipboard {
font-size: 1rem;
}
.remixui_copyIcon {
margin-right: 5px;
}
.remixui_log {
display: flex;
flex-direction: column;
margin-bottom: 5%;
overflow: visible;
}
.remixui_key {
margin-right: 5px;
text-transform: uppercase;
width: 100%;
}
.remixui_value {
display: flex;
width: 100%;
margin-top: 1.5%;
}
.remixui_questionMark {
margin-left: 2%;
cursor: pointer;
}
.remixui_questionMark:hover {
}
.remixui_detailsJSON {
padding: 8px 0;
border: none;
}
.remixui_icon {
margin-right: 0.3em;
}
.remixui_errorBlobs {
padding-left: 5px;
padding-right: 5px;
word-break: break-word;
}
.remixui_storageLogo {
width: 20px;
height: 20px;
}
.remixui_spinningIcon {
display: inline-block;
position: relative;
animation: spin 2s infinite linear;
-moz-animation: spin 2s infinite linear;
-o-animation: spin 2s infinite linear;
-webkit-animation: spin 2s infinite linear;
}
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
@-webkit-keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
@-moz-keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
@-o-keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
@-ms-keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
.remixui_bouncingIcon {
display: inline-block;
position: relative;
-moz-animation: bounce 2s infinite linear;
-o-animation: bounce 2s infinite linear;
-webkit-animation: bounce 2s infinite linear;
animation: bounce 2s infinite linear;
}
@-webkit-keyframes bounce {
0% { top: 0; }
50% { top: -0.2em; }
70% { top: -0.3em; }
100% { top: 0; }
}
@-moz-keyframes bounce {
0% { top: 0; }
50% { top: -0.2em; }
70% { top: -0.3em; }
100% { top: 0; }
}
@-o-keyframes bounce {
0% { top: 0; }
50% { top: -0.2em; }
70% { top: -0.3em; }
100% { top: 0; }
}
@-ms-keyframes bounce {
0% { top: 0; }
50% { top: -0.2em; }
70% { top: -0.3em; }
100% { top: 0; }
}
@keyframes bounce {
0% { top: 0; }
50% { top: -0.2em; }
70% { top: -0.3em; }
100% { top: 0; }
}

@ -0,0 +1,128 @@
import { Plugin } from '@remixproject/engine'
const packageJson = require('../../../../../../package.json')
const Compiler = require('@remix-project/remix-solidity').Compiler
const EventEmitter = require('events')
const profile = {
name: 'solidity-logic',
displayName: 'Solidity compiler logic',
description: 'Compile solidity contracts - Logic',
version: packageJson.version
}
export class CompileTab extends Plugin {
public compiler
public optimize
public runs
public evmVersion: string
public compilerImport
public event
constructor (public queryParams, public fileManager, public editor, public config, public fileProvider, public contentImport) {
super(profile)
this.event = new EventEmitter()
this.compiler = new Compiler((url, cb) => this.call('contentImport', 'resolveAndSave', url).then((result) => cb(null, result)).catch((error) => cb(error.message)))
}
init () {
this.optimize = this.queryParams.get().optimize
this.optimize = this.optimize === 'true'
this.queryParams.update({ optimize: this.optimize })
this.compiler.set('optimize', this.optimize)
this.runs = this.queryParams.get().runs
this.runs = this.runs && this.runs !== 'undefined' ? this.runs : 200
this.queryParams.update({ runs: this.runs })
this.compiler.set('runs', this.runs)
this.evmVersion = this.queryParams.get().evmVersion
if (this.evmVersion === 'undefined' || this.evmVersion === 'null' || !this.evmVersion) {
this.evmVersion = null
}
this.queryParams.update({ evmVersion: this.evmVersion })
this.compiler.set('evmVersion', this.evmVersion)
}
setOptimize (newOptimizeValue) {
this.optimize = newOptimizeValue
this.queryParams.update({ optimize: this.optimize })
this.compiler.set('optimize', this.optimize)
}
setRuns (runs) {
this.runs = runs
this.queryParams.update({ runs: this.runs })
this.compiler.set('runs', this.runs)
}
setEvmVersion (newEvmVersion) {
this.evmVersion = newEvmVersion
this.queryParams.update({ evmVersion: this.evmVersion })
this.compiler.set('evmVersion', this.evmVersion)
}
/**
* Set the compiler to using Solidity or Yul (default to Solidity)
* @params lang {'Solidity' | 'Yul'} ...
*/
setLanguage (lang) {
this.compiler.set('language', lang)
}
/**
* Compile a specific file of the file manager
* @param {string} target the path to the file to compile
*/
compileFile (target) {
if (!target) throw new Error('No target provided for compiliation')
const provider = this.fileManager.fileProviderOf(target)
if (!provider) throw new Error(`cannot compile ${target}. Does not belong to any explorer`)
return new Promise((resolve, reject) => {
provider.get(target, (error, content) => {
if (error) return reject(error)
const sources = { [target]: { content } }
this.event.emit('startingCompilation')
// setTimeout fix the animation on chrome... (animation triggered by 'staringCompilation')
setTimeout(() => { this.compiler.compile(sources, target); resolve(true) }, 100)
})
})
}
async isHardhatProject () {
if (this.fileManager.mode === 'localhost') {
return await this.fileManager.exists('hardhat.config.js')
} else return false
}
runCompiler (hhCompilation) {
try {
if (this.fileManager.mode === 'localhost' && hhCompilation) {
const { currentVersion, optimize, runs } = this.compiler.state
if (currentVersion) {
const fileContent = `module.exports = {
solidity: '${currentVersion.substring(0, currentVersion.indexOf('+commit'))}',
settings: {
optimizer: {
enabled: ${optimize},
runs: ${runs}
}
}
}
`
const configFilePath = 'remix-compiler.config.js'
this.fileManager.setFileContent(configFilePath, fileContent)
this.call('hardhat', 'compile', configFilePath).then((result) => {
this.call('terminal', 'log', { type: 'info', value: result })
}).catch((error) => {
this.call('terminal', 'log', { type: 'error', value: error })
})
}
}
this.fileManager.saveCurrentFile()
this.event.emit('removeAnnotations')
var currentFile = this.config.get('currentFile')
return this.compileFile(currentFile)
} catch (err) {
console.error(err)
}
}
}

@ -0,0 +1,51 @@
'use strict'
import * as remixLib from '@remix-project/remix-lib'
const txHelper = remixLib.execution.txHelper
export class CompilerAbstract {
public languageversion: string
public data: Record<string, any>
public source: Record<string, any>
constructor (languageversion, data, source) {
this.languageversion = languageversion
this.data = data
this.source = source // source code
}
getContracts () {
return this.data.contracts
}
getContract (name) {
return txHelper.getContract(name, this.data.contracts)
}
visitContracts (calllback) {
return txHelper.visitContracts(this.data.contracts, calllback)
}
getData () {
return this.data
}
getAsts () {
return this.data.sources // ast
}
getSourceName (fileIndex) {
if (this.data && this.data.sources) {
return Object.keys(this.data.sources)[fileIndex]
} else if (Object.keys(this.source.sources).length === 1) {
// if we don't have ast, we return the only one filename present.
const sourcesArray = Object.keys(this.source.sources)
return sourcesArray[0]
}
return null
}
getSourceCode () {
return this.source
}
}

@ -0,0 +1,19 @@
'use strict'
import { canUseWorker, urlFromVersion } from './compiler-utils'
import { Compiler } from '@remix-project/remix-solidity'
import { CompilerAbstract } from './compiler-abstract'
export const compile = async (compilationTargets, settings, contentResolverCallback) => {
return new Promise((resolve) => {
const compiler = new Compiler(contentResolverCallback)
compiler.set('evmVersion', settings.evmVersion)
compiler.set('optimize', settings.optimize)
compiler.set('language', settings.language)
compiler.set('runs', settings.runs)
compiler.loadVersion(canUseWorker(settings.version), urlFromVersion(settings.version))
compiler.event.register('compilationFinished', (success, compilationData, source) => {
resolve(new CompilerAbstract(settings.version, compilationData, source))
})
compiler.event.register('compilerLoaded', () => compiler.compile(compilationTargets, ''))
})
}

@ -0,0 +1,46 @@
const semver = require('semver')
const minixhr = require('minixhr')
/* global Worker */
export const baseURLBin = 'https://binaries.soliditylang.org/bin'
export const baseURLWasm = 'https://binaries.soliditylang.org/wasm'
export const pathToURL = {}
/**
* Retrieves the URL of the given compiler version
* @param version is the version of compiler with or without 'soljson-v' prefix and .js postfix
*/
export function urlFromVersion (version) {
if (!version.startsWith('soljson-v')) version = 'soljson-v' + version
if (!version.endsWith('.js')) version = version + '.js'
return `${pathToURL[version]}/${version}`
}
/**
* Checks if the worker can be used to load a compiler.
* checks a compiler whitelist, browser support and OS.
*/
export function canUseWorker (selectedVersion) {
const version = semver.coerce(selectedVersion)
const isNightly = selectedVersion.includes('nightly')
return browserSupportWorker() && (
// All compiler versions (including nightlies) after 0.6.3 are wasm compiled
semver.gt(version, '0.6.3') ||
// Only releases are wasm compiled starting with 0.3.6
(semver.gte(version, '0.3.6') && !isNightly)
)
}
function browserSupportWorker () {
return document.location.protocol !== 'file:' && Worker !== undefined
}
// returns a promise for minixhr
export function promisedMiniXhr (url) {
return new Promise((resolve) => {
minixhr(url, (json, event) => {
resolve({ json, event })
})
})
}

@ -0,0 +1,119 @@
'use strict'
import * as solcTranslate from 'solc/translate'
import * as remixLib from '@remix-project/remix-lib'
const txHelper = remixLib.execution.txHelper
export function parseContracts (contractName, contract, source) {
const detail: Record<string, any> = {}
detail.name = contractName
detail.metadata = contract.metadata
if (contract.evm.bytecode.object) {
detail.bytecode = contract.evm.bytecode.object
}
detail.abi = contract.abi
if (contract.evm.bytecode.object) {
detail.bytecode = contract.evm.bytecode
detail.web3Deploy = gethDeploy(contractName.toLowerCase(), contract.abi, contract.evm.bytecode.object)
detail.metadataHash = retrieveMetadataHash(contract.evm.bytecode.object)
if (detail.metadataHash) {
detail.swarmLocation = 'bzzr://' + detail.metadataHash
}
}
detail.functionHashes = {}
for (const fun in contract.evm.methodIdentifiers) {
detail.functionHashes[contract.evm.methodIdentifiers[fun]] = fun
}
detail.gasEstimates = formatGasEstimates(contract.evm.gasEstimates)
detail.devdoc = contract.devdoc
detail.userdoc = contract.userdoc
if (contract.evm.deployedBytecode && contract.evm.deployedBytecode.object.length > 0) {
detail['Runtime Bytecode'] = contract.evm.deployedBytecode
}
if (source && contract.assembly !== null) {
detail.Assembly = solcTranslate.prettyPrintLegacyAssemblyJSON(contract.evm.legacyAssembly, source.content)
}
return detail
}
const retrieveMetadataHash = function (bytecode) {
var match = /a165627a7a72305820([0-9a-f]{64})0029$/.exec(bytecode)
if (!match) {
match = /a265627a7a72305820([0-9a-f]{64})6c6578706572696d656e74616cf50037$/.exec(bytecode)
}
if (match) {
return match[1]
}
}
const gethDeploy = function (contractName, jsonInterface, bytecode) {
let code = ''
const funABI = txHelper.getConstructorInterface(jsonInterface)
funABI.inputs.forEach(function (inp) {
code += 'var ' + inp.name + ' = /* var of type ' + inp.type + ' here */ ;\n'
})
contractName = contractName.replace(/[:./]/g, '_')
code += 'var ' + contractName + 'Contract = new web3.eth.Contract(' + JSON.stringify(jsonInterface).replace('\n', '') + ');' +
'\nvar ' + contractName + ' = ' + contractName + 'Contract.deploy({' +
"\n data: '0x" + bytecode + "', " +
'\n arguments: ['
funABI.inputs.forEach(function (inp) {
code += '\n ' + inp.name + ','
})
code += '\n ]' +
'\n}).send({' +
'\n from: web3.eth.accounts[0], ' +
"\n gas: '4700000'" +
'\n }, function (e, contract){' +
'\n console.log(e, contract);' +
"\n if (typeof contract.address !== 'undefined') {" +
"\n console.log('Contract mined! address: ' + contract.address + ' transactionHash: ' + contract.transactionHash);" +
'\n }' +
'\n })'
return code
}
const formatGasEstimates = function (data) {
if (!data) return {}
if (data.creation === undefined && data.external === undefined && data.internal === undefined) return {}
const gasToText = function (g) {
return g === null ? 'unknown' : g
}
const ret: Record<string, any> = {}
let fun
if ('creation' in data) {
ret.Creation = data.creation
}
if ('external' in data) {
ret.External = {}
for (fun in data.external) {
ret.External[fun] = gasToText(data.external[fun])
}
}
if ('internal' in data) {
ret.Internal = {}
for (fun in data.internal) {
ret.Internal[fun] = gasToText(data.internal[fun])
}
}
return ret
}

@ -0,0 +1,5 @@
export * from './compileTabLogic'
export * from './compiler-abstract'
export * from './compiler-helpers'
export * from './compiler-utils'
export * from './contract-parser'

@ -0,0 +1,63 @@
interface Action {
type: string;
payload: Record<string, any>;
}
export const compilerInitialState = {
compiler: {
mode: '',
args: null
},
editor: {
mode: ''
}
}
export const compilerReducer = (state = compilerInitialState, action: Action) => {
switch (action.type) {
case 'SET_COMPILER_MODE': {
return {
...state,
compiler: {
...state.compiler,
mode: action.payload.mode,
args: action.payload.args || null
}
}
}
case 'RESET_COMPILER_MODE': {
return {
...state,
compiler: {
...state.compiler,
mode: '',
args: null
}
}
}
case 'SET_EDITOR_MODE': {
return {
...state,
editor: {
...state.editor,
mode: action.payload
}
}
}
case 'RESET_EDITOR_MODE': {
return {
...state,
editor: {
...state.editor,
mode: ''
}
}
}
default:
throw new Error()
}
}

@ -0,0 +1,115 @@
import React, { useState } from 'react' // eslint-disable-line
import { SolidityCompilerProps } from './types'
import { CompilerContainer } from './compiler-container' // eslint-disable-line
import { ContractSelection } from './contract-selection' // eslint-disable-line
import { Toaster } from '@remix-ui/toaster' // eslint-disable-line
import { ModalDialog } from '@remix-ui/modal-dialog' // eslint-disable-line
import { Renderer } from '@remix-ui/renderer' // eslint-disable-line
import './css/style.css'
export const SolidityCompiler = (props: SolidityCompilerProps) => {
const { plugin, plugin: { editor, config, queryParams, compileTabLogic, currentFile, fileProvider, fileManager, contractsDetails, contractMap, compileErrors, isHardHatProject, setHardHatCompilation, configurationSettings } } = props
const [state, setState] = useState({
contractsDetails: {},
eventHandlers: {},
loading: false,
compileTabLogic: null,
compiler: null,
toasterMsg: '',
modal: {
hide: true,
title: '',
message: null,
okLabel: '',
okFn: () => {},
cancelLabel: '',
cancelFn: () => {},
handleHide: null
}
})
const [currentVersion, setCurrentVersion] = useState('')
const toast = (message: string) => {
setState(prevState => {
return { ...prevState, toasterMsg: message }
})
}
const updateCurrentVersion = (value) => {
setCurrentVersion(value)
plugin.setSelectedVersion(value)
}
const modal = async (title: string, message: string | JSX.Element, okLabel: string, okFn: () => void, cancelLabel?: string, cancelFn?: () => void) => {
await setState(prevState => {
return {
...prevState,
modal: {
...prevState.modal,
hide: false,
message,
title,
okLabel,
okFn,
cancelLabel,
cancelFn
}
}
})
}
const handleHideModal = () => {
setState(prevState => {
return { ...prevState, modal: { ...state.modal, hide: true, message: null } }
})
}
const panicMessage = (message: string) => (
<div>
<i className="fas fa-exclamation-circle remixui_panicError" aria-hidden="true"></i>
The compiler returned with the following internal error: <br /> <b>{message}.<br />
The compiler might be in a non-sane state, please be careful and do not use further compilation data to deploy to mainnet.
It is heavily recommended to use another browser not affected by this issue (Firefox is known to not be affected).</b><br />
Please join <a href="https://gitter.im/ethereum/remix" target="blank" >remix gitter channel</a> for more information.
</div>
)
return (
<>
<div id="compileTabView">
<CompilerContainer editor={editor} config={config} queryParams={queryParams} compileTabLogic={compileTabLogic} tooltip={toast} modal={modal} compiledFileName={currentFile} setHardHatCompilation={setHardHatCompilation.bind(plugin)} updateCurrentVersion={updateCurrentVersion} isHardHatProject={isHardHatProject} configurationSettings={configurationSettings} />
<ContractSelection contractMap={contractMap} fileProvider={fileProvider} fileManager={fileManager} contractsDetails={contractsDetails} modal={modal} />
<div className="remixui_errorBlobs p-4" data-id="compiledErrors">
<span data-id={`compilationFinishedWith_${currentVersion}`}></span>
{ compileErrors.error && <Renderer message={compileErrors.error.formattedMessage || compileErrors.error} plugin={plugin} opt={{ type: compileErrors.error.severity || 'error', errorType: compileErrors.error.type }} config={config} editor={editor} fileManager={fileManager} /> }
{ compileErrors.error && (compileErrors.error.mode === 'panic') && modal('Error', panicMessage(compileErrors.error.formattedMessage), 'Close', null) }
{ compileErrors.errors && compileErrors.errors.length && compileErrors.errors.map((err, index) => {
if (config.get('hideWarnings')) {
if (err.severity !== 'warning') {
return <Renderer key={index} message={err.formattedMessage} plugin={plugin} opt={{ type: err.severity, errorType: err.type }} config={config} editor={editor} fileManager={fileManager} />
}
} else {
return <Renderer key={index} message={err.formattedMessage} plugin={plugin} opt={{ type: err.severity, errorType: err.type }} config={config} editor={editor} fileManager={fileManager} />
}
}) }
</div>
</div>
<Toaster message={state.toasterMsg} />
<ModalDialog
id='workspacesModalDialog'
title={ state.modal.title }
message={ state.modal.message }
hide={ state.modal.hide }
okLabel={ state.modal.okLabel }
okFn={ state.modal.okFn }
cancelLabel={ state.modal.cancelLabel }
cancelFn={ state.modal.cancelFn }
handleHide={ handleHideModal }>
{ (typeof state.modal.message !== 'string') && state.modal.message }
</ModalDialog>
</>
)
}
export default SolidityCompiler

@ -0,0 +1,54 @@
export interface SolidityCompilerProps {
plugin: {
contractMap: {
file: string
} | Record<string, any>
compileErrors: any,
isHardHatProject: boolean,
queryParams: any,
compileTabLogic: any,
currentFile: string,
contractsDetails: Record<string, any>,
editor: any,
config: any,
fileProvider: any,
fileManager: any,
contentImport: any,
call: (...args) => void
on: (...args) => void,
setHardHatCompilation: (value: boolean) => void,
setSelectedVersion: (value: string) => void,
configurationSettings: ConfigurationSettings
},
}
export interface CompilerContainerProps {
editor: any,
config: any,
queryParams: any,
compileTabLogic: any,
tooltip: (message: string | JSX.Element) => void,
modal: (title: string, message: string | JSX.Element, okLabel: string, okFn: () => void, cancelLabel?: string, cancelFn?: () => void) => void,
compiledFileName: string,
setHardHatCompilation: (value: boolean) => void,
updateCurrentVersion: any,
isHardHatProject: boolean,
configurationSettings: ConfigurationSettings
}
export interface ContractSelectionProps {
contractMap: {
file: string
} | Record<string, any>,
fileManager: any,
fileProvider: any,
modal: (title: string, message: string | JSX.Element, okLabel: string, okFn: () => void, cancelLabel?: string, cancelFn?: () => void) => void,
contractsDetails: Record<string, any>
}
export interface ConfigurationSettings {
version: string,
evmVersion: string,
language: string,
optimize: boolean,
runs: string
}

@ -0,0 +1,16 @@
{
"extends": "../../../tsconfig.json",
"compilerOptions": {
"jsx": "react",
"allowJs": true,
"esModuleInterop": true,
"allowSyntheticDefaultImports": true
},
"files": [],
"include": [],
"references": [
{
"path": "./tsconfig.lib.json"
}
]
}

@ -0,0 +1,13 @@
{
"extends": "./tsconfig.json",
"compilerOptions": {
"outDir": "../../../dist/out-tsc",
"types": ["node"]
},
"files": [
"../../../node_modules/@nrwl/react/typings/cssmodule.d.ts",
"../../../node_modules/@nrwl/react/typings/image.d.ts"
],
"exclude": ["**/*.spec.ts", "**/*.spec.tsx"],
"include": ["**/*.js", "**/*.jsx", "**/*.ts", "**/*.tsx"]
}

@ -109,6 +109,15 @@
}, },
"remix-core-plugin": { "remix-core-plugin": {
"tags": [] "tags": []
},
"remix-ui-solidity-compiler": {
"tags": []
},
"remix-ui-publish-to-storage": {
"tags": []
},
"remix-ui-renderer": {
"tags": []
} }
} }
} }

19
package-lock.json generated

@ -13250,11 +13250,14 @@
"is-plain-object": "^2.0.1" "is-plain-object": "^2.0.1"
} }
}, },
"copy-text-to-clipboard": { "copy-to-clipboard": {
"version": "1.0.4", "version": "3.3.1",
"resolved": "https://registry.npmjs.org/copy-text-to-clipboard/-/copy-text-to-clipboard-1.0.4.tgz", "resolved": "https://registry.npmjs.org/copy-to-clipboard/-/copy-to-clipboard-3.3.1.tgz",
"integrity": "sha512-4hDE+0bgqm4G/nXnt91CP3rc0vOptaePPU5WfVZuhv2AYNJogdLHR4pF1XPgXDAGY4QCzj9pD7zKATa+50sQPg==", "integrity": "sha512-i13qo6kIHTTpCm8/Wup+0b1mVWETvu2kIMzKoK8FpkLkFxlt0znUAHcMzox+T8sPlqtZXq3CulEjQHsYiGFJUw==",
"dev": true "dev": true,
"requires": {
"toggle-selection": "^1.0.6"
}
}, },
"copy-webpack-plugin": { "copy-webpack-plugin": {
"version": "5.1.1", "version": "5.1.1",
@ -37193,6 +37196,12 @@
"through2": "^2.0.3" "through2": "^2.0.3"
} }
}, },
"toggle-selection": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/toggle-selection/-/toggle-selection-1.0.6.tgz",
"integrity": "sha1-bkWxJj8gF/oKzH2J14sVuL932jI=",
"dev": true
},
"toidentifier": { "toidentifier": {
"version": "1.0.0", "version": "1.0.0",
"resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz", "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz",

@ -41,8 +41,8 @@
"workspace-schematic": "nx workspace-schematic", "workspace-schematic": "nx workspace-schematic",
"dep-graph": "nx dep-graph", "dep-graph": "nx dep-graph",
"help": "nx help", "help": "nx help",
"lint:libs": "nx run-many --target=lint --projects=remix-analyzer,remix-astwalker,remix-debug,remix-lib,remix-simulator,remix-solidity,remix-tests,remix-url-resolver,remixd,remix-ui-tree-view,remix-ui-modal-dialog,remix-ui-toaster,remix-ui-file-explorer,remix-ui-debugger-ui,remix-ui-workspace,remix-ui-static-analyser,remix-ui-checkbox,remix-ui-settings,remix-core-plugin", "lint:libs": "nx run-many --target=lint --projects=remix-analyzer,remix-astwalker,remix-debug,remix-lib,remix-simulator,remix-solidity,remix-tests,remix-url-resolver,remixd,remix-ui-tree-view,remix-ui-modal-dialog,remix-ui-toaster,remix-ui-file-explorer,remix-ui-debugger-ui,remix-ui-workspace,remix-ui-static-analyser,remix-ui-checkbox,remix-ui-settings,remix-core-plugin,remix-ui-renderer,remix-ui-publish-to-storage,remix-ui-solidity-compiler",
"build:libs": "nx run-many --target=build --parallel=false --with-deps=true --projects=remix-analyzer,remix-astwalker,remix-debug,remix-lib,remix-simulator,remix-solidity,remix-tests,remix-url-resolver,remixd,remix-core-plugin", "build:libs": "nx run-many --target=build --parallel=false --with-deps=true --projects=remix-analyzer,remix-astwalker,remix-debug,remix-lib,remix-simulator,remix-solidity,remix-tests,remix-url-resolver,remixd",
"test:libs": "nx run-many --target=test --projects=remix-analyzer,remix-astwalker,remix-debug,remix-lib,remix-simulator,remix-solidity,remix-tests,remix-url-resolver,remixd", "test:libs": "nx run-many --target=test --projects=remix-analyzer,remix-astwalker,remix-debug,remix-lib,remix-simulator,remix-solidity,remix-tests,remix-url-resolver,remixd",
"publish:libs": "npm run build:libs && lerna publish --skip-git && npm run bumpVersion:libs", "publish:libs": "npm run build:libs && lerna publish --skip-git && npm run bumpVersion:libs",
"build:e2e": "tsc -p apps/remix-ide-e2e/tsconfig.e2e.json", "build:e2e": "tsc -p apps/remix-ide-e2e/tsconfig.e2e.json",
@ -234,7 +234,7 @@
"browserify": "^16.2.3", "browserify": "^16.2.3",
"browserify-reload": "^1.0.3", "browserify-reload": "^1.0.3",
"component-type": "^1.2.1", "component-type": "^1.2.1",
"copy-text-to-clipboard": "^1.0.4", "copy-to-clipboard": "^3.3.1",
"csjs-inject": "^1.0.1", "csjs-inject": "^1.0.1",
"csslint": "^1.0.2", "csslint": "^1.0.2",
"cypress": "^4.1.0", "cypress": "^4.1.0",

@ -20,14 +20,10 @@
"@remix-project/remix-astwalker": ["dist/libs/remix-astwalker/index.js"], "@remix-project/remix-astwalker": ["dist/libs/remix-astwalker/index.js"],
"@remix-project/remix-debug": ["dist/libs/remix-debug/src/index.js"], "@remix-project/remix-debug": ["dist/libs/remix-debug/src/index.js"],
"@remix-project/remix-lib": ["dist/libs/remix-lib/src/index.js"], "@remix-project/remix-lib": ["dist/libs/remix-lib/src/index.js"],
"@remix-project/remix-simulator": [ "@remix-project/remix-simulator": ["dist/libs/remix-simulator/src/index.js"],
"dist/libs/remix-simulator/src/index.js"
],
"@remix-project/remix-solidity": ["dist/libs/remix-solidity/index.js"], "@remix-project/remix-solidity": ["dist/libs/remix-solidity/index.js"],
"@remix-project/remix-tests": ["dist/libs/remix-tests/src/index.js"], "@remix-project/remix-tests": ["dist/libs/remix-tests/src/index.js"],
"@remix-project/remix-url-resolver": [ "@remix-project/remix-url-resolver": ["dist/libs/remix-url-resolver/index.js"],
"dist/libs/remix-url-resolver/index.js"
],
"@remixproject/debugger-plugin": ["apps/debugger/src/index.ts"], "@remixproject/debugger-plugin": ["apps/debugger/src/index.ts"],
"@remix-project/remixd": ["dist/libs/remixd/index.js"], "@remix-project/remixd": ["dist/libs/remixd/index.js"],
"@remix-ui/tree-view": ["libs/remix-ui/tree-view/src/index.ts"], "@remix-ui/tree-view": ["libs/remix-ui/tree-view/src/index.ts"],
@ -43,9 +39,10 @@
"@remix-ui/checkbox": ["libs/remix-ui/checkbox/src/index.ts"], "@remix-ui/checkbox": ["libs/remix-ui/checkbox/src/index.ts"],
"@remix-ui/terminal": ["libs/remix-ui/terminal/src/index.ts"], "@remix-ui/terminal": ["libs/remix-ui/terminal/src/index.ts"],
"@remix-ui/settings": ["libs/remix-ui/settings/src/index.ts"], "@remix-ui/settings": ["libs/remix-ui/settings/src/index.ts"],
"@remix-project/core-plugin": [ "@remix-project/core-plugin": ["libs/remix-core-plugin/src/index.ts"],
"libs/remix-core-plugin/src/index.ts" "@remix-ui/solidity-compiler": ["libs/remix-ui/solidity-compiler/src/index.ts"],
] "@remix-ui/publish-to-storage": ["libs/remix-ui/publish-to-storage/src/index.ts"],
"@remix-ui/renderer": ["libs/remix-ui/renderer/src/index.ts"]
} }
}, },
"exclude": ["node_modules", "tmp"] "exclude": ["node_modules", "tmp"]

@ -841,6 +841,60 @@
} }
} }
} }
},
"remix-ui-solidity-compiler": {
"root": "libs/remix-ui/solidity-compiler",
"sourceRoot": "libs/remix-ui/solidity-compiler/src",
"projectType": "library",
"schematics": {},
"architect": {
"lint": {
"builder": "@nrwl/linter:lint",
"options": {
"linter": "eslint",
"tsConfig": ["libs/remix-ui/solidity-compiler/tsconfig.lib.json"],
"exclude": [
"**/node_modules/**",
"!libs/remix-ui/solidity-compiler/**/*"
]
}
}
}
},
"remix-ui-publish-to-storage": {
"root": "libs/remix-ui/publish-to-storage",
"sourceRoot": "libs/remix-ui/publish-to-storage/src",
"projectType": "library",
"schematics": {},
"architect": {
"lint": {
"builder": "@nrwl/linter:lint",
"options": {
"linter": "eslint",
"tsConfig": ["libs/remix-ui/publish-to-storage/tsconfig.lib.json"],
"exclude": [
"**/node_modules/**",
"!libs/remix-ui/publish-to-storage/**/*"
]
}
}
}
},
"remix-ui-renderer": {
"root": "libs/remix-ui/renderer",
"sourceRoot": "libs/remix-ui/renderer/src",
"projectType": "library",
"schematics": {},
"architect": {
"lint": {
"builder": "@nrwl/linter:lint",
"options": {
"linter": "eslint",
"tsConfig": ["libs/remix-ui/renderer/tsconfig.lib.json"],
"exclude": ["**/node_modules/**", "!libs/remix-ui/renderer/**/*"]
}
}
}
} }
}, },
"cli": { "cli": {

Loading…
Cancel
Save