Merge branch 'remixd_terminal' of https://github.com/ethereum/remix-project into remixd_terminal

pull/5370/head
davidzagi93@gmail.com 4 years ago
commit 7733594514
  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. 4
      apps/remix-ide/ci/publishIpfs
  7. 19
      apps/remix-ide/src/app.js
  8. 169
      apps/remix-ide/src/app/compiler/compiler-imports.js
  9. 21
      apps/remix-ide/src/app/compiler/compiler-input.js
  10. 148
      apps/remix-ide/src/app/files/compiler-metadata.js
  11. 19
      apps/remix-ide/src/app/files/dgitProvider.js
  12. 28
      apps/remix-ide/src/app/files/fileManager.js
  13. 4
      apps/remix-ide/src/app/files/fileProvider.js
  14. 22
      apps/remix-ide/src/app/panels/file-panel.js
  15. 357
      apps/remix-ide/src/app/tabs/compile-tab.js
  16. 4
      apps/remix-ide/src/app/tabs/compileTab/compilerContainer.js
  17. 2
      apps/remix-ide/src/app/tabs/runTab/model/dropdownlogic.js
  18. 6
      apps/remix-ide/src/app/tabs/settings-tab.js
  19. 3
      apps/remix-ide/src/app/tabs/test-tab.js
  20. 2
      apps/remix-ide/src/app/ui/copy-to-clipboard.js
  21. 4
      apps/remix-ide/src/app/ui/landing-page/landing-page.js
  22. 4
      apps/remix-ide/src/lib/cmdInterpreterAPI.js
  23. 2
      apps/remix-ide/src/lib/publishOnIpfs.js
  24. 1
      libs/remix-core-plugin/.eslintrc
  25. 3
      libs/remix-core-plugin/README.md
  26. 12
      libs/remix-core-plugin/package.json
  27. 5
      libs/remix-core-plugin/src/index.ts
  28. 11
      libs/remix-core-plugin/src/lib/compiler-artefacts.ts
  29. 175
      libs/remix-core-plugin/src/lib/compiler-content-imports.ts
  30. 28
      libs/remix-core-plugin/src/lib/compiler-fetch-and-compile.ts
  31. 145
      libs/remix-core-plugin/src/lib/compiler-metadata.ts
  32. 77
      libs/remix-core-plugin/src/lib/offset-line-to-column-converter.ts
  33. 10
      libs/remix-core-plugin/tsconfig.json
  34. 12
      libs/remix-core-plugin/tsconfig.lib.json
  35. 8
      libs/remix-debug/src/code/codeManager.ts
  36. 4
      libs/remix-debug/src/debugger/VmDebugger.ts
  37. 2
      libs/remix-debug/src/eventManager.ts
  38. 2
      libs/remix-debug/src/solidity-decoder/types/util.ts
  39. 2
      libs/remix-debug/src/trace/traceManager.ts
  40. 2
      libs/remix-lib/src/eventManager.ts
  41. 17
      libs/remix-lib/src/init.ts
  42. 48
      libs/remix-solidity/src/compiler/compiler-abstract.ts
  43. 4
      libs/remix-solidity/src/compiler/compiler-helpers.ts
  44. 0
      libs/remix-solidity/src/compiler/compiler-utils.ts
  45. 3
      libs/remix-solidity/src/index.ts
  46. 2
      libs/remix-solidity/src/lib/eventManager.ts
  47. 34
      libs/remix-ui/clipboard/src/lib/copy-to-clipboard/copy-to-clipboard.tsx
  48. 36
      libs/remix-ui/debugger-ui/src/lib/vm-debugger/assembly-items.tsx
  49. 8
      libs/remix-ui/debugger-ui/src/reducers/assembly-items.ts
  50. 2
      libs/remix-ui/modal-dialog/src/lib/types/index.ts
  51. 4
      libs/remix-ui/publish-to-storage/.babelrc
  52. 18
      libs/remix-ui/publish-to-storage/.eslintrc
  53. 7
      libs/remix-ui/publish-to-storage/README.md
  54. 1
      libs/remix-ui/publish-to-storage/src/index.ts
  55. 0
      libs/remix-ui/publish-to-storage/src/lib/publish-to-storage.css
  56. 110
      libs/remix-ui/publish-to-storage/src/lib/publish-to-storage.tsx
  57. 109
      libs/remix-ui/publish-to-storage/src/lib/publishOnSwarm.tsx
  58. 117
      libs/remix-ui/publish-to-storage/src/lib/publishToIPFS.tsx
  59. 7
      libs/remix-ui/publish-to-storage/src/lib/types/index.ts
  60. 16
      libs/remix-ui/publish-to-storage/tsconfig.json
  61. 13
      libs/remix-ui/publish-to-storage/tsconfig.lib.json
  62. 4
      libs/remix-ui/renderer/.babelrc
  63. 19
      libs/remix-ui/renderer/.eslintrc
  64. 7
      libs/remix-ui/renderer/README.md
  65. 1
      libs/remix-ui/renderer/src/index.ts
  66. 47
      libs/remix-ui/renderer/src/lib/renderer.css
  67. 127
      libs/remix-ui/renderer/src/lib/renderer.tsx
  68. 16
      libs/remix-ui/renderer/tsconfig.json
  69. 13
      libs/remix-ui/renderer/tsconfig.lib.json
  70. 4
      libs/remix-ui/solidity-compiler/.babelrc
  71. 19
      libs/remix-ui/solidity-compiler/.eslintrc
  72. 7
      libs/remix-ui/solidity-compiler/README.md
  73. 2
      libs/remix-ui/solidity-compiler/src/index.ts
  74. 57
      libs/remix-ui/solidity-compiler/src/lib/actions/compiler.ts
  75. 582
      libs/remix-ui/solidity-compiler/src/lib/compiler-container.tsx
  76. 238
      libs/remix-ui/solidity-compiler/src/lib/contract-selection.tsx
  77. 234
      libs/remix-ui/solidity-compiler/src/lib/css/style.css
  78. 128
      libs/remix-ui/solidity-compiler/src/lib/logic/compileTabLogic.ts
  79. 11
      libs/remix-ui/solidity-compiler/src/lib/logic/compiler-abstract.ts
  80. 19
      libs/remix-ui/solidity-compiler/src/lib/logic/compiler-helpers.ts
  81. 46
      libs/remix-ui/solidity-compiler/src/lib/logic/compiler-utils.ts
  82. 119
      libs/remix-ui/solidity-compiler/src/lib/logic/contract-parser.ts
  83. 5
      libs/remix-ui/solidity-compiler/src/lib/logic/index.ts
  84. 63
      libs/remix-ui/solidity-compiler/src/lib/reducers/compiler.ts
  85. 115
      libs/remix-ui/solidity-compiler/src/lib/solidity-compiler.tsx
  86. 54
      libs/remix-ui/solidity-compiler/src/lib/types/index.ts
  87. 16
      libs/remix-ui/solidity-compiler/tsconfig.json
  88. 13
      libs/remix-ui/solidity-compiler/tsconfig.lib.json
  89. 6
      libs/remix-url-resolver/src/resolve.ts
  90. 3
      libs/remixd/src/origins.json
  91. 14
      nx.json
  92. 31
      package-lock.json
  93. 4
      package.json
  94. 16
      tsconfig.json
  95. 84
      workspace.json

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

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

@ -20,24 +20,32 @@ module.exports = {
.verifyContracts(['Ballot'])
.click('#publishOnIpfs')
.pause(8000)
.getModalBody((value, done) => {
if (value.indexOf('Metadata of "ballot" was published successfully.') === -1) browser.assert.fail('ipfs deploy failed', '', '')
if (value.indexOf('ipfs://') === -1) browser.assert.fail('ipfs deploy failed', '', '')
done()
.waitForElementVisible('[data-id="publishToStorageModalDialogModalBody-react"]', 60000)
.getText('[data-id="publishToStorageModalDialogModalBody-react"]', (result) => {
const value = <string>(result.value)
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) {
browser
.click('#publishOnSwarm')
.pause(8000)
.getModalBody((value, done) => {
if (value.indexOf('Metadata of "ballot" was successfully.') === -1) browser.assert.fail('swarm deploy failed', '', '')
if (value.indexOf('bzz') === -1) browser.assert.fail('swarm deploy failed', '', '')
done()
.getText('[data-id="publishToStorageModalDialogModalBody-react"]', (result) => {
const value = <string>(result.value)
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) {
@ -48,10 +56,11 @@ module.exports = {
.clickLaunchIcon('udapp')
.waitForElementPresent('*[data-id="contractDropdownIpfsCheckbox"]')
.click('*[data-id="contractDropdownIpfsCheckbox"]')
.waitForElementVisible('*[data-id="Deploy - transact (not payable)"]')
.click('*[data-id="Deploy - transact (not payable)"]')
.pause(8000)
.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()
})
.modalFooterOKClick()

@ -17,7 +17,7 @@ module.exports = {
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
.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')
@ -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) {
browser
.pause(5000)
@ -38,22 +75,22 @@ module.exports = {
.refresh()
.pause(5000)
.clickLaunchIcon('solidity')
.assert.containsText('#versionSelector option[selected="selected"]', '0.7.4+commit.3f05b770')
.assert.containsText('#evmVersionSelector option[selected="selected"]', 'istanbul')
.assert.containsText('#versionSelector option[data-id="selected"]', '0.7.4+commit.3f05b770')
.assert.containsText('#evmVersionSelector option[data-id="selected"]', 'istanbul')
.verify.elementPresent('#optimize:checked')
.verify.attributeEquals('#runs', 'value', '300')
},
'Should load using compiler from link passed in remix URL': function (browser: NightwatchBrowser) {
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()
.pause(5000)
.clickLaunchIcon('solidity')
.pause(5000)
.assert.containsText('#versionSelector option[selected="selected"]', 'custom')
.assert.containsText('#versionSelector option[data-id="selected"]', 'custom')
// default values
.assert.containsText('#evmVersionSelector option[selected="selected"]', 'default')
.assert.containsText('#evmVersionSelector option[data-id="selected"]', 'default')
.verify.elementPresent('#optimize')
.assert.elementNotPresent('#optimize:checked')
.verify.elementPresent('#runs:disabled')

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

@ -6,13 +6,13 @@ console.log('current folder', process.cwd())
const folder = process.cwd() + '/temp_publish_docker';
(async () => {
const host = 'ipfs.komputing.org' // ethdev berlin ipfs node
const host = 'ipfs.remixproject.org'
const ipfs = IpfsHttpClient({ host, port: 443, protocol: 'https' })
try {
let result = await ipfs.add(globSource(folder, { recursive: true}), { pin: false })
const hash = result.cid.toString()
console.log('ipfs://' + hash)
console.log('https://ipfsgw.komputing.org/ipfs/' + hash)
console.log('https://ipfs.remixproject.org/ipfs/' + hash)
console.log('https://gateway.ipfs.io/ipfs/' + hash)
} catch (e) {
console.log(e)

@ -16,7 +16,8 @@ import { HiddenPanel } from './app/components/hidden-panel'
import { VerticalIcons } from './app/components/vertical-icons'
import { LandingPage } from './app/ui/landing-page/landing-page'
import { MainPanel } from './app/components/main-panel'
import FetchAndCompile from './app/compiler/compiler-sourceVerifier-fetchAndCompile'
import { OffsetToLineColumnConverter, CompilerMetadata, CompilerArtefacts, FetchAndCompile, CompilerImports } from '@remix-project/core-plugin'
import migrateFileSystem from './migrateFileSystem'
@ -25,7 +26,7 @@ const csjs = require('csjs-inject')
const yo = require('yo-yo')
const remixLib = require('@remix-project/remix-lib')
const registry = require('./global/registry')
const { OffsetToLineColumnConverter } = require('./lib/offsetToLineColumnConverter')
const QueryParams = require('./lib/query-params')
const Storage = remixLib.Storage
const RemixDProvider = require('./app/files/remixDProvider')
@ -38,13 +39,10 @@ const FileProvider = require('./app/files/fileProvider')
const DGitProvider = require('./app/files/dgitProvider')
const WorkspaceFileProvider = require('./app/files/workspaceFileProvider')
const toolTip = require('./app/ui/tooltip')
const CompilerMetadata = require('./app/files/compiler-metadata')
const CompilerImport = require('./app/compiler/compiler-imports')
const Blockchain = require('./blockchain/blockchain.js')
const PluginManagerComponent = require('./app/components/plugin-manager-component')
const CompilersArtefacts = require('./app/compiler/compiler-artefacts')
const CompileTab = require('./app/tabs/compile-tab')
const SettingsTab = require('./app/tabs/settings-tab')
@ -262,14 +260,14 @@ Please make a backup of your contracts and start using http://remix.ethereum.org
const dGitProvider = new DGitProvider()
// ----------------- import content service ------------------------
const contentImport = new CompilerImport(fileManager)
const contentImport = new CompilerImports()
const blockchain = new Blockchain(registry.get('config').api)
// ----------------- compilation metadata generation service ---------
const compilerMetadataGenerator = new CompilerMetadata(blockchain, fileManager, registry.get('config').api)
const compilerMetadataGenerator = new CompilerMetadata()
// ----------------- compilation result service (can keep track of compilation results) ----------------------------
const compilersArtefacts = new CompilersArtefacts() // store all the compilation results (key represent a compiler name)
const compilersArtefacts = new CompilerArtefacts() // store all the compilation results (key represent a compiler name)
registry.put({ api: compilersArtefacts, name: 'compilersartefacts' })
// service which fetch contract artifacts from sourve-verify, put artifacts in remix and compile it
@ -459,11 +457,12 @@ Please make a backup of your contracts and start using http://remix.ethereum.org
console.log('couldn\'t register iframe plugins', e.message)
}
await appManager.activatePlugin(['contentImport', 'theme', 'editor', 'fileManager', 'compilerMetadata', 'compilerArtefacts', 'network', 'web3Provider', 'offsetToLineColumnConverter'])
await appManager.activatePlugin(['theme', 'editor', 'fileManager', 'compilerMetadata', 'compilerArtefacts', 'network', 'web3Provider', 'offsetToLineColumnConverter'])
await appManager.activatePlugin(['mainPanel', 'menuicons', 'tabs'])
await appManager.activatePlugin(['sidePanel']) // activating host plugin separately
await appManager.activatePlugin(['home'])
await appManager.activatePlugin(['hiddenPanel', 'pluginManager', 'filePanel', 'settings', 'contextualListener', 'terminal', 'fetchAndCompile'])
await appManager.activatePlugin(['settings'])
await appManager.activatePlugin(['hiddenPanel', 'pluginManager', 'filePanel', 'contextualListener', 'terminal', 'fetchAndCompile', 'contentImport'])
const queryParams = new QueryParams()
const params = queryParams.get()

@ -1,169 +0,0 @@
'use strict'
import { Plugin } from '@remixproject/engine'
import * as packageJson from '../../../../../package.json'
import { RemixURLResolver } from '@remix-project/remix-url-resolver'
const remixTests = require('@remix-project/remix-tests')
const globalRegistry = require('../../global/registry')
const addTooltip = require('../ui/tooltip')
const async = require('async')
const profile = {
name: 'contentImport',
displayName: 'content import',
version: packageJson.version,
methods: ['resolve', 'resolveAndSave', 'isExternalUrl']
}
module.exports = class CompilerImports extends Plugin {
constructor (fileManager) {
super(profile)
this.fileManager = fileManager
// const token = await this.call('settings', 'getGithubAccessToken')
const token = globalRegistry.get('config').api.get('settings/gist-access-token') // TODO replace with the plugin call above https://github.com/ethereum/remix-ide/issues/2288
const protocol = window.location.protocol
this.urlResolver = new RemixURLResolver(token, protocol)
this.previouslyHandled = {} // cache import so we don't make the request at each compilation.
}
isRelativeImport (url) {
return /^([^/]+)/.exec(url)
}
isExternalUrl (url) {
const handlers = this.urlResolver.getHandlers()
return handlers.some(handler => handler.match(url))
}
/**
* resolve the content of @arg url. This only resolves external URLs.
*
* @param {String} url - external URL of the content. can be basically anything like raw HTTP, ipfs URL, github address etc...
* @returns {Promise} - { content, cleanUrl, type, url }
*/
resolve (url) {
return new Promise((resolve, reject) => {
this.import(url, null, (error, content, cleanUrl, type, url) => {
if (error) return reject(error)
resolve({ content, cleanUrl, type, url })
})
})
}
async import (url, force, loadingCb, cb) {
if (typeof force !== 'boolean') {
const temp = loadingCb
loadingCb = force
cb = temp
force = false
}
if (!loadingCb) loadingCb = () => {}
if (!cb) cb = () => {}
var self = this
if (force) delete this.previouslyHandled[url]
var imported = this.previouslyHandled[url]
if (imported) {
return cb(null, imported.content, imported.cleanUrl, imported.type, url)
}
let resolved
try {
resolved = await this.urlResolver.resolve(url)
const { content, cleanUrl, type } = resolved
self.previouslyHandled[url] = {
content,
cleanUrl,
type
}
cb(null, content, cleanUrl, type, url)
} catch (e) {
return cb(new Error('not found ' + url))
}
}
importExternal (url, targetPath, cb) {
this.import(url,
// TODO: move to an event that is generated, the UI shouldn't be here
(loadingMsg) => { addTooltip(loadingMsg) },
(error, content, cleanUrl, type, url) => {
if (error) return cb(error)
if (this.fileManager) {
const provider = this.fileManager.currentFileProvider()
const path = targetPath || type + '/' + cleanUrl
if (provider) provider.addExternal('.deps/' + path, content, url)
}
cb(null, content)
})
}
/**
* import the content of @arg url.
* first look in the browser localstorage (browser explorer) or locahost explorer. if the url start with `browser/*` or `localhost/*`
* then check if the @arg url is located in the localhost, in the node_modules or installed_contracts folder
* then check if the @arg url match any external url
*
* @param {String} url - URL of the content. can be basically anything like file located in the browser explorer, in the localhost explorer, raw HTTP, github address etc...
* @param {String} targetPath - (optional) internal path where the content should be saved to
* @returns {Promise} - string content
*/
resolveAndSave (url, targetPath) {
return new Promise((resolve, reject) => {
if (url.indexOf('remix_tests.sol') !== -1) resolve(remixTests.assertLibCode)
if (!this.fileManager) {
// fallback to just resolving the file, it won't be saved in file manager
return this.importExternal(url, targetPath, (error, content) => {
if (error) return reject(error)
resolve(content)
})
}
var provider = this.fileManager.fileProviderOf(url)
if (provider) {
if (provider.type === 'localhost' && !provider.isConnected()) {
return reject(new Error(`file provider ${provider.type} not available while trying to resolve ${url}`))
}
provider.exists(url).then(exist => {
/*
if the path is absolute and the file does not exist, we can stop here
Doesn't make sense to try to resolve "localhost/node_modules/localhost/node_modules/<path>" and we'll end in an infinite loop.
*/
if (!exist && url.startsWith('browser/')) return reject(new Error(`not found ${url}`))
if (!exist && url.startsWith('localhost/')) return reject(new Error(`not found ${url}`))
if (exist) {
return provider.get(url, (error, content) => {
if (error) return reject(error)
resolve(content)
})
}
// try to resolve localhost modules (aka truffle imports) - e.g from the node_modules folder
const localhostProvider = this.fileManager.getProvider('localhost')
if (localhostProvider.isConnected()) {
var splitted = /([^/]+)\/(.*)$/g.exec(url)
return async.tryEach([
(cb) => { this.resolveAndSave('localhost/installed_contracts/' + url).then((result) => cb(null, result)).catch((error) => cb(error.message)) },
(cb) => { if (!splitted) { cb('URL not parseable: ' + url) } else { this.resolveAndSave('localhost/installed_contracts/' + splitted[1] + '/contracts/' + splitted[2]).then((result) => cb(null, result)).catch((error) => cb(error.message)) } },
(cb) => { this.resolveAndSave('localhost/node_modules/' + url).then((result) => cb(null, result)).catch((error) => cb(error.message)) },
(cb) => { if (!splitted) { cb('URL not parseable: ' + url) } else { this.resolveAndSave('localhost/node_modules/' + splitted[1] + '/contracts/' + splitted[2]).then((result) => cb(null, result)).catch((error) => cb(error.message)) } }],
(error, result) => {
if (error) {
return this.importExternal(url, targetPath, (error, content) => {
if (error) return reject(error)
resolve(content)
})
}
resolve(result)
})
}
this.importExternal(url, targetPath, (error, content) => {
if (error) return reject(error)
resolve(content)
})
}).catch(error => {
return reject(error)
})
}
})
}
}

@ -1,21 +0,0 @@
'use strict'
module.exports = (sources, opts) => {
return JSON.stringify({
language: 'Solidity',
sources: sources,
settings: {
optimizer: {
enabled: opts.optimize === true || opts.optimize === 1,
runs: 200
},
libraries: opts.libraries,
outputSelection: {
'*': {
'': ['ast'],
'*': ['abi', 'metadata', 'devdoc', 'userdoc', 'evm.legacyAssembly', 'evm.bytecode', 'evm.deployedBytecode', 'evm.methodIdentifiers', 'evm.gasEstimates']
}
}
}
})
}

@ -1,148 +0,0 @@
'use strict'
import { Plugin } from '@remixproject/engine'
import * as packageJson from '../../../../../package.json'
import { joinPath } from '../../lib/helper'
var CompilerAbstract = require('../compiler/compiler-abstract')
const profile = {
name: 'compilerMetadata',
methods: ['deployMetadataOf'],
events: [],
version: packageJson.version
}
class CompilerMetadata extends Plugin {
constructor (blockchain, fileManager, config) {
super(profile)
this.blockchain = blockchain
this.fileManager = fileManager
this.config = config
this.networks = ['VM:-', 'main:1', 'ropsten:3', 'rinkeby:4', 'kovan:42', 'görli:5', 'Custom']
this.innerPath = 'artifacts'
}
_JSONFileName (path, contractName) {
return joinPath(path, this.innerPath, contractName + '.json')
}
_MetadataFileName (path, contractName) {
return joinPath(path, this.innerPath, contractName + '_metadata.json')
}
onActivation () {
var self = this
this.on('solidity', 'compilationFinished', (file, source, languageVersion, data) => {
if (!self.config.get('settings/generate-contract-metadata')) return
const compiler = new CompilerAbstract(languageVersion, data, source)
var provider = self.fileManager.fileProviderOf(source.target)
var path = self.fileManager.extractPathOf(source.target)
if (provider) {
compiler.visitContracts((contract) => {
if (contract.file !== source.target) return
var fileName = self._JSONFileName(path, contract.name)
var metadataFileName = self._MetadataFileName(path, contract.name)
provider.get(fileName, (error, content) => {
if (!error) {
content = content || '{}'
var metadata
try {
metadata = JSON.parse(content)
} catch (e) {
console.log(e)
}
var deploy = metadata.deploy || {}
self.networks.forEach((network) => {
deploy[network] = self._syncContext(contract, deploy[network] || {})
})
let parsedMetadata
try {
parsedMetadata = JSON.parse(contract.object.metadata)
} catch (e) {
console.log(e)
}
if (parsedMetadata) provider.set(metadataFileName, JSON.stringify(parsedMetadata, null, '\t'))
var data = {
deploy,
data: {
bytecode: contract.object.evm.bytecode,
deployedBytecode: contract.object.evm.deployedBytecode,
gasEstimates: contract.object.evm.gasEstimates,
methodIdentifiers: contract.object.evm.methodIdentifiers
},
abi: contract.object.abi
}
provider.set(fileName, JSON.stringify(data, null, '\t'))
}
})
})
}
})
}
_syncContext (contract, metadata) {
var linkReferences = metadata.linkReferences
var autoDeployLib = metadata.autoDeployLib
if (!linkReferences) linkReferences = {}
if (autoDeployLib === undefined) autoDeployLib = true
for (var libFile in contract.object.evm.bytecode.linkReferences) {
if (!linkReferences[libFile]) linkReferences[libFile] = {}
for (var lib in contract.object.evm.bytecode.linkReferences[libFile]) {
if (!linkReferences[libFile][lib]) {
linkReferences[libFile][lib] = '<address>'
}
}
}
metadata.linkReferences = linkReferences
metadata.autoDeployLib = autoDeployLib
return metadata
}
// TODO: is only called by dropdownLogic and can be moved there
deployMetadataOf (contractName, fileLocation) {
return new Promise((resolve, reject) => {
var provider
let path
if (fileLocation) {
provider = this.fileManager.fileProviderOf(fileLocation)
path = fileLocation.split('/')
path.pop()
path = path.join('/')
} else {
provider = this.fileManager.currentFileProvider()
path = this.fileManager.currentPath()
}
if (provider) {
this.blockchain.detectNetwork((err, { id, name } = {}) => {
if (err) {
console.log(err)
reject(err)
} else {
var fileName = this._JSONFileName(path, contractName)
provider.get(fileName, (error, content) => {
if (error) return reject(error)
if (!content) return resolve()
try {
var metadata = JSON.parse(content)
metadata = metadata.deploy || {}
return resolve(metadata[name + ':' + id] || metadata[name] || metadata[id] || metadata[name.toLowerCase() + ':' + id] || metadata[name.toLowerCase()])
} catch (e) {
reject(e.message)
}
})
}
})
} else {
reject(new Error(`Please select the folder in the file explorer where the metadata of ${contractName} can be found`))
}
})
}
}
module.exports = CompilerMetadata

@ -27,10 +27,10 @@ class DGitProvider extends Plugin {
constructor () {
super(profile)
this.ipfsconfig = {
host: 'ipfs.komputing.org',
host: 'ipfs.remixproject.org',
port: 443,
protocol: 'https',
ipfsurl: 'https://ipfsgw.komputing.org/ipfs/'
ipfsurl: 'https://ipfs.remixproject.org/ipfs/'
}
this.globalIPFSConfig = {
host: 'ipfs.io',
@ -206,7 +206,20 @@ class DGitProvider extends Plugin {
const commits = await this.log({ ref: 'HEAD' })
ob = {
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) {
ob = {

@ -22,7 +22,7 @@ const profile = {
icon: 'assets/img/fileManager.webp',
permission: true,
version: packageJson.version,
methods: ['file', 'exists', 'open', 'writeFile', 'readFile', 'copyFile', 'copyDir', 'rename', 'mkdir', 'readdir', 'remove', 'getCurrentFile', 'getFile', 'getFolder', 'setFile', 'switchFile', 'refresh'],
methods: ['file', 'exists', 'open', 'writeFile', 'readFile', 'copyFile', 'copyDir', 'rename', 'mkdir', 'readdir', 'remove', 'getCurrentFile', 'getFile', 'getFolder', 'setFile', 'switchFile', 'refresh', 'getProviderOf', 'getProviderByName'],
kind: 'file-system'
}
const errorMsg = {
@ -597,6 +597,32 @@ class FileManager extends Plugin {
}
}
/**
* Async API method getProviderOf
* @param {string} file
*
*/
async getProviderOf (file) {
const cancall = await this.askUserPermission('getProviderByName')
if (cancall) {
return file ? this.fileProviderOf(file) : this.currentFileProvider()
}
}
/**
* Async API method getProviderByName
* @param {string} name
*
*/
async getProviderByName (name) {
const cancall = await this.askUserPermission('getProviderByName')
if (cancall) {
return this.getProvider(name)
}
}
getProvider (name) {
return this._deps.filesProviders[name]
}

@ -1,6 +1,6 @@
'use strict'
const CompilerImport = require('../compiler/compiler-imports')
import { CompilerImports } from '@remix-project/core-plugin'
const EventManager = require('events')
const modalDialogCustom = require('../ui/modal-dialog-custom')
const tooltip = require('../ui/tooltip')
@ -44,7 +44,7 @@ class FileProvider {
discardChanges (path) {
this.remove(path)
const compilerImport = new CompilerImport()
const compilerImport = new CompilerImports()
this.providerExternalsStorage.keys().map(value => {
if (value.indexOf(path) === 0) {
compilerImport.import(

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

@ -1,28 +1,18 @@
/* 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 * as packageJson from '../../../../../package.json'
import publishToStorage from '../../publishToStorage'
import { compile } from '../compiler/compiler-helpers'
const EventEmitter = require('events')
const $ = require('jquery')
const yo = require('yo-yo')
const copy = require('copy-text-to-clipboard')
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 Renderer = require('../ui/renderer')
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 = {
name: 'solidity',
displayName: 'Solidity compiler',
@ -57,11 +47,9 @@ class CompileTab extends ViewPlugin {
// dependencies
this.editor = editor
this.config = config
this.renderer = new Renderer(this)
this.fileManager = fileManager
this.contractsDetails = {}
this.data = {
contractsDetails: {},
eventHandlers: {},
loading: false
}
@ -71,30 +59,32 @@ class CompileTab extends ViewPlugin {
this.editor,
this.config,
this.fileProvider,
this.contentImport
this.contentImport,
this.setCompileErrors.bind(this)
)
}
onActivationInternal () {
this.compiler = this.compileTabLogic.compiler
this.compileTabLogic.init()
this.compilerContainer = new CompilerContainer(
this.compileTabLogic,
this.editor,
this.config,
this.queryParams
)
this.contractMap = {}
this.isHardHatProject = false
this.compileErrors = {}
this.compiledFileName = ''
this.selectedVersion = ''
this.configurationSettings = null
this.el = document.createElement('div')
this.el.setAttribute('id', 'compileTabView')
}
resetResults () {
if (this._view.errorContainer) {
this._view.errorContainer.innerHTML = ''
}
this.compilerContainer.currentFile = ''
this.data.contractsDetails = {}
yo.update(this._view.contractSelection, this.contractSelection())
this.currentFile = ''
this.contractsDetails = {}
this.emit('statusChanged', { key: 'none' })
this.renderComponent()
}
setCompileErrors (data) {
this.compileErrors = data
this.renderComponent()
}
/************
@ -102,13 +92,6 @@ class CompileTab extends ViewPlugin {
*/
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.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.data.eventHandlers.onStartingCompilation = () => {
if (this._view.errorContainer) {
this._view.errorContainer.innerHTML = ''
}
this.emit('statusChanged', { key: 'loading', title: 'compiling...', type: 'info' })
}
@ -137,23 +117,32 @@ class CompileTab extends ViewPlugin {
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('removeAnnotations', this.data.eventHandlers.onRemoveAnnotations)
this.data.eventHandlers.onCurrentFileChanged = (name) => {
this.compilerContainer.currentFile = name
this.currentFile = name
this.renderComponent()
}
this.fileManager.events.on('currentFileChanged', this.data.eventHandlers.onCurrentFileChanged)
this.data.eventHandlers.onNoFileSelected = () => {
this.compilerContainer.currentFile = ''
this.currentFile = ''
this.renderComponent()
}
this.fileManager.events.on('noFileSelected', this.data.eventHandlers.onNoFileSelected)
this.data.eventHandlers.onCompilationFinished = (success, data, source) => {
this._view.errorContainer.appendChild(yo`<span data-id="compilationFinishedWith_${this.getCurrentVersion()}"></span>`)
this.setCompileErrors(data)
if (success) {
// forwarding the event to the appManager infra
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' })
// Store the contracts
this.data.contractsDetails = {}
this.contractsDetails = {}
this.compiler.visitContracts((contract) => {
this.data.contractsDetails[contract.name] = parseContracts(
this.contractsDetails[contract.name] = parseContracts(
contract.name,
contract.object,
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' })
}
// Update contract Selection
const contractMap = {}
if (success) this.compiler.visitContracts((contract) => { contractMap[contract.name] = contract })
const contractSelection = this.contractSelection(contractMap)
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.contractMap = {}
if (success) this.compiler.visitContracts((contract) => { this.contractMap[contract.name] = contract })
this.renderComponent()
}
this.compiler.event.register('compilationFinished', this.data.eventHandlers.onCompilationFinished)
@ -226,11 +187,19 @@ class CompileTab extends ViewPlugin {
// ctrl+s or command+s
if ((e.metaKey || e.ctrlKey) && e.keyCode === 83) {
e.preventDefault()
this.compileTabLogic.runCompiler(this.compilerContainer.hhCompilation)
this.compileTabLogic.runCompiler(this.hhCompilation)
}
})
}
setHardHatCompilation (value) {
this.hhCompilation = value
}
setSelectedVersion (version) {
this.selectedVersion = version
}
getCompilationResult () {
return this.compileTabLogic.compiler.state.lastCompilationResult
}
@ -254,20 +223,15 @@ class CompileTab extends ViewPlugin {
* @param {object} settings {evmVersion, optimize, runs, version, language}
*/
async compileWithParameters (compilationTargets, settings) {
settings.version = settings.version || this.compilerContainer.data.selectedVersion
settings.version = settings.version || this.selectedVersion
const res = await compile(compilationTargets, settings)
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'
getCurrentCompilerConfig () {
return {
currentVersion: this.compilerContainer.data.selectedVersion,
currentVersion: this.selectedVersion,
evmVersion: this.compileTabLogic.evmVersion,
optimize: this.compileTabLogic.optimize,
runs: this.compileTabLogic.runs
@ -280,96 +244,10 @@ class CompileTab extends ViewPlugin {
* @param {object} settings {evmVersion, optimize, runs, version, language}
*/
setCompilerConfig (settings) {
return new Promise((resolve, reject) => {
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.compilerContainer.setConfiguration(settings)
// @todo(#2875) should use loading compiler return value to check whether the compiler is loaded instead of "setInterval"
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
this.configurationSettings = settings
this.renderComponent()
// @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>`)
}
// TODO : Add success alert when compilation succeed
@ -390,119 +268,15 @@ class CompileTab extends ViewPlugin {
this.selectedContract = contractName
}
details () {
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'
}
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 () {
this.renderComponent()
return this.el
}
render () {
if (this._view.el) return this._view.el
this.onActivationInternal()
this._view.errorContainer = yo`<div class="${css.errorBlobs} p-4" data-id="compiledErrors" ></div>`
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
renderComponent () {
ReactDOM.render(
<SolidityCompiler plugin={this}/>
, this.el)
}
onActivation () {
@ -526,7 +300,8 @@ class CompileTab extends ViewPlugin {
}
onDeactivation () {
this.compilerContainer.deactivate()
this.editor.event.unregister('contentChanged')
this.editor.event.unregister('sessionSwitched')
this.editor.event.unregister('contentChanged', this.data.eventHandlers.onContentChanged)
this.compiler.event.unregister('loadingCompiler', this.data.eventHandlers.onLoadingCompiler)
this.compiler.event.unregister('compilerLoaded', this.data.eventHandlers.onCompilerLoaded)

@ -1,6 +1,6 @@
import toaster from '../../ui/tooltip'
import { canUseWorker, baseURLBin, baseURLWasm, urlFromVersion, pathToURL, promisedMiniXhr } from '../../compiler/compiler-utils'
import { canUseWorker, baseURLBin, baseURLWasm, urlFromVersion, pathToURL, promisedMiniXhr } from '@remix-project/remix-solidity'
const yo = require('yo-yo')
const helper = require('../../../lib/helper')
const addTooltip = require('../../ui/tooltip')
@ -112,7 +112,7 @@ class CompilerContainer {
this._view.compileIcon.setAttribute('title', 'idle')
this._view.compileIcon.classList.remove(`${css.spinningIcon}`)
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 @@
import { CompilerAbstract } from '@remix-project/remix-solidity'
const remixLib = require('@remix-project/remix-lib')
const txHelper = remixLib.execution.txHelper
const CompilerAbstract = require('../../../compiler/compiler-abstract')
const EventManager = remixLib.EventManager
const _paq = window._paq = window._paq || []

@ -8,7 +8,7 @@ const globalRegistry = require('../../global/registry')
const profile = {
name: 'settings',
displayName: 'Settings',
methods: ['getGithubAccessToken'],
methods: ['get'],
events: [],
icon: 'assets/img/settings.webp',
description: 'Remix-IDE settings',
@ -52,8 +52,8 @@ module.exports = class SettingsTab extends ViewPlugin {
)
}
getGithubAccessToken () {
return this.config.get('settings/gist-access-token')
get (key) {
return this.config.get(key)
}
updateMatomoAnalyticsChoice (isChecked) {

@ -1,7 +1,6 @@
import { ViewPlugin } from '@remixproject/engine-web'
import { canUseWorker, urlFromVersion } from '../compiler/compiler-utils'
import { removeMultipleSlashes, removeTrailingSlashes } from '../../lib/helper'
import { canUseWorker, urlFromVersion } from '@remix-project/remix-solidity'
var yo = require('yo-yo')
var async = require('async')
var tooltip = require('../ui/tooltip')

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

@ -1,12 +1,12 @@
import * as packageJson from '../../../../../../package.json'
import { ViewPlugin } from '@remixproject/engine-web'
import { migrateToWorkspace } from '../../../migrateFileSystem'
import { CompilerImports } from '@remix-project/core-plugin'
import JSZip from 'jszip'
const yo = require('yo-yo')
const csjs = require('csjs-inject')
const globalRegistry = require('../../../global/registry')
const CompilerImport = require('../../compiler/compiler-imports')
const modalDialogCustom = require('../modal-dialog-custom')
const modalDialog = require('../modaldialog')
const tooltip = require('../tooltip')
@ -240,7 +240,7 @@ export class LandingPage extends ViewPlugin {
render () {
const load = (service, item, examples, info) => {
const compilerImport = new CompilerImport()
const compilerImport = new CompilerImports()
const fileProviders = globalRegistry.get('fileproviders').api
const msg = yo`
<div class="p-2">

@ -1,9 +1,9 @@
'use strict'
import { CompilerImports } from '@remix-project/core-plugin'
var yo = require('yo-yo')
var async = require('async')
var EventManager = require('../lib/events')
var CompilerImport = require('../app/compiler/compiler-imports')
var toolTip = require('../app/ui/tooltip')
var globalRegistry = require('../global/registry')
var SourceHighlighter = require('../app/editor/sourceHighlighter')
@ -18,7 +18,7 @@ class CmdInterpreterAPI {
self._components.registry = localRegistry || globalRegistry
self._components.terminal = terminal
self._components.sourceHighlighter = new SourceHighlighter()
self._components.fileImport = new CompilerImport()
self._components.fileImport = new CompilerImports()
self._components.gistHandler = new GistHandler()
self._deps = {
fileManager: self._components.registry.get('filemanager').api,

@ -4,7 +4,7 @@ const async = require('async')
const IpfsClient = require('ipfs-mini')
const ipfsNodes = [
new IpfsClient({ host: 'ipfs.komputing.org', port: 443, protocol: 'https' }),
new IpfsClient({ host: 'ipfs.remixproject.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' })
]

@ -0,0 +1 @@
{ "extends": "../../.eslintrc", "rules": {}, "ignorePatterns": ["!**/*"] }

@ -0,0 +1,3 @@
# remix-core-plugin-core-plugin
This library was generated with [Nx](https://nx.dev).

@ -0,0 +1,12 @@
{
"name": "@remix-project/core-plugin",
"version": "0.0.1",
"description": "This library was generated with [Nx](https://nx.dev).",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "Remix Team",
"license": "ISC"
}

@ -0,0 +1,5 @@
export { OffsetToLineColumnConverter } from './lib/offset-line-to-column-converter'
export { CompilerMetadata } from './lib/compiler-metadata'
export { FetchAndCompile } from './lib/compiler-fetch-and-compile'
export { CompilerImports } from './lib/compiler-content-imports'
export { CompilerArtefacts } from './lib/compiler-artefacts'

@ -1,16 +1,17 @@
'use strict'
import { Plugin } from '@remixproject/engine'
import * as packageJson from '../../../../../package.json'
import CompilerAbstract from './compiler-abstract'
import { CompilerAbstract } from '@remix-project/remix-solidity'
const profile = {
name: 'compilerArtefacts',
methods: [],
methods: ['get', 'addResolvedContract'],
events: [],
version: packageJson.version
version: '0.0.1'
}
module.exports = class CompilerArtefacts extends Plugin {
export class CompilerArtefacts extends Plugin {
compilersArtefactsPerFile: any
compilersArtefacts: any
constructor () {
super(profile)
this.compilersArtefacts = {}

@ -0,0 +1,175 @@
'use strict'
import { Plugin } from '@remixproject/engine'
import { RemixURLResolver } from '@remix-project/remix-url-resolver'
const remixTests = require('@remix-project/remix-tests')
const async = require('async')
const profile = {
name: 'contentImport',
displayName: 'content import',
version: '0.0.1',
methods: ['resolve', 'resolveAndSave', 'isExternalUrl']
}
export class CompilerImports extends Plugin {
previouslyHandled: {}
urlResolver: any
constructor () {
super(profile)
this.urlResolver = new RemixURLResolver()
this.previouslyHandled = {} // cache import so we don't make the request at each compilation.
}
async setToken () {
const protocol = typeof window !== 'undefined' && window.location.protocol
const token = await this.call('settings', 'get', 'settings/gist-access-token')
this.urlResolver.setGistToken(token, protocol)
}
isRelativeImport (url) {
return /^([^/]+)/.exec(url)
}
isExternalUrl (url) {
const handlers = this.urlResolver.getHandlers()
return handlers.some(handler => handler.match(url))
}
/**
* resolve the content of @arg url. This only resolves external URLs.
*
* @param {String} url - external URL of the content. can be basically anything like raw HTTP, ipfs URL, github address etc...
* @returns {Promise} - { content, cleanUrl, type, url }
*/
resolve (url) {
return new Promise((resolve, reject) => {
this.import(url, null, (error, content, cleanUrl, type, url) => {
if (error) return reject(error)
resolve({ content, cleanUrl, type, url })
}, null)
})
}
async import (url, force, loadingCb, cb) {
if (typeof force !== 'boolean') {
const temp = loadingCb
loadingCb = force
cb = temp
force = false
}
if (!loadingCb) loadingCb = () => {}
if (!cb) cb = () => {}
var self = this
if (force) delete this.previouslyHandled[url]
var imported = this.previouslyHandled[url]
if (imported) {
return cb(null, imported.content, imported.cleanUrl, imported.type, url)
}
let resolved
try {
await this.setToken()
resolved = await this.urlResolver.resolve(url)
const { content, cleanUrl, type } = resolved
self.previouslyHandled[url] = {
content,
cleanUrl,
type
}
cb(null, content, cleanUrl, type, url)
} catch (e) {
return cb(new Error('not found ' + url))
}
}
importExternal (url, targetPath, cb) {
this.import(url,
// TODO: handle this event
(loadingMsg) => { this.emit('message', loadingMsg) },
async (error, content, cleanUrl, type, url) => {
if (error) return cb(error)
try {
const provider = await this.call('fileManager', 'getProviderOf', null)
const path = targetPath || type + '/' + cleanUrl
if (provider) provider.addExternal('.deps/' + path, content, url)
} catch (err) {
}
cb(null, content)
}, null)
}
/**
* import the content of @arg url.
* first look in the browser localstorage (browser explorer) or locahost explorer. if the url start with `browser/*` or `localhost/*`
* then check if the @arg url is located in the localhost, in the node_modules or installed_contracts folder
* then check if the @arg url match any external url
*
* @param {String} url - URL of the content. can be basically anything like file located in the browser explorer, in the localhost explorer, raw HTTP, github address etc...
* @param {String} targetPath - (optional) internal path where the content should be saved to
* @returns {Promise} - string content
*/
resolveAndSave (url, targetPath) {
return new Promise((resolve, reject) => {
if (url.indexOf('remix_tests.sol') !== -1) resolve(remixTests.assertLibCode)
this.call('fileManager', 'getProviderOf', url).then((provider) => {
if (provider) {
if (provider.type === 'localhost' && !provider.isConnected()) {
return reject(new Error(`file provider ${provider.type} not available while trying to resolve ${url}`))
}
provider.exists(url).then(exist => {
/*
if the path is absolute and the file does not exist, we can stop here
Doesn't make sense to try to resolve "localhost/node_modules/localhost/node_modules/<path>" and we'll end in an infinite loop.
*/
if (!exist && url.startsWith('browser/')) return reject(new Error(`not found ${url}`))
if (!exist && url.startsWith('localhost/')) return reject(new Error(`not found ${url}`))
if (exist) {
return provider.get(url, (error, content) => {
if (error) return reject(error)
resolve(content)
})
}
// try to resolve localhost modules (aka truffle imports) - e.g from the node_modules folder
this.call('fileManager', 'getProviderByName', 'localhost').then((localhostProvider) => {
if (localhostProvider.isConnected()) {
var splitted = /([^/]+)\/(.*)$/g.exec(url)
return async.tryEach([
(cb) => { this.resolveAndSave('localhost/installed_contracts/' + url, null).then((result) => cb(null, result)).catch((error) => cb(error.message)) },
// eslint-disable-next-line standard/no-callback-literal
(cb) => { if (!splitted) { cb('URL not parseable: ' + url) } else { this.resolveAndSave('localhost/installed_contracts/' + splitted[1] + '/contracts/' + splitted[2], null).then((result) => cb(null, result)).catch((error) => cb(error.message)) } },
(cb) => { this.resolveAndSave('localhost/node_modules/' + url, null).then((result) => cb(null, result)).catch((error) => cb(error.message)) },
// eslint-disable-next-line standard/no-callback-literal
(cb) => { if (!splitted) { cb('URL not parseable: ' + url) } else { this.resolveAndSave('localhost/node_modules/' + splitted[1] + '/contracts/' + splitted[2], null).then((result) => cb(null, result)).catch((error) => cb(error.message)) } }],
(error, result) => {
if (error) {
return this.importExternal(url, targetPath, (error, content) => {
if (error) return reject(error)
resolve(content)
})
}
resolve(result)
})
}
this.importExternal(url, targetPath, (error, content) => {
if (error) return reject(error)
resolve(content)
})
})
}).catch(error => {
return reject(error)
})
}
}).catch(() => {
// fallback to just resolving the file, it won't be saved in file manager
return this.importExternal(url, targetPath, (error, content) => {
if (error) return reject(error)
resolve(content)
})
})
})
}
}

@ -1,18 +1,19 @@
import * as packageJson from '../../../../../package.json'
import { Plugin } from '@remixproject/engine'
import { compile } from './compiler-helpers'
import globalRegistry from '../../global/registry'
import { compile } from '@remix-project/remix-solidity'
import { util } from '@remix-project/remix-lib'
import remixLib from '@remix-project/remix-lib'
const ethutil = require('ethereumjs-util')
const profile = {
name: 'fetchAndCompile',
methods: ['resolve'],
version: packageJson.version
version: '0.0.1'
}
export default class FetchAndCompile extends Plugin {
export class FetchAndCompile extends Plugin {
unresolvedAddresses: any[]
sourceVerifierNetWork: string[]
constructor () {
super(profile)
this.unresolvedAddresses = []
@ -32,11 +33,10 @@ export default class FetchAndCompile extends Plugin {
*/
async resolve (contractAddress, codeAtAddress, targetPath) {
contractAddress = ethutil.toChecksumAddress(contractAddress)
const compilersartefacts = globalRegistry.get('compilersartefacts').api
const localCompilation = () => compilersartefacts.get(contractAddress) ? compilersartefacts.get(contractAddress) : compilersartefacts.get('__last') ? compilersartefacts.get('__last') : null
const localCompilation = async () => await this.call('compilerArtefacts', 'get', contractAddress) ? await this.call('compilerArtefacts', 'get', contractAddress) : await this.call('compilerArtefacts', 'get', '__last') ? await this.call('compilerArtefacts', 'get', '__last') : null
const resolved = compilersartefacts.get(contractAddress)
const resolved = await this.call('compilerArtefacts', 'get', contractAddress)
if (resolved) return resolved
if (this.unresolvedAddresses.includes(contractAddress)) return localCompilation()
@ -53,15 +53,15 @@ export default class FetchAndCompile extends Plugin {
if (!this.sourceVerifierNetWork.includes(network.name)) return localCompilation()
// check if the contract if part of the local compilation result
const compilation = localCompilation()
const compilation = await localCompilation()
if (compilation) {
let found = false
compilation.visitContracts((contract) => {
found = remixLib.util.compareByteCode('0x' + contract.object.evm.deployedBytecode.object, codeAtAddress)
found = util.compareByteCode('0x' + contract.object.evm.deployedBytecode.object, codeAtAddress)
return found
})
if (found) {
compilersartefacts.addResolvedContract(contractAddress, compilation)
await this.call('compilerArtefacts', 'addResolvedContract', contractAddress, compilation)
setTimeout(_ => this.emit('usingLocalCompilation', contractAddress), 0)
return compilation
}
@ -118,8 +118,8 @@ export default class FetchAndCompile extends Plugin {
const compData = await compile(
compilationTargets,
settings,
(url, cb) => this.call('contentImport', 'resolveAndSave', url).then((result) => cb(null, result)).catch((error) => cb(error.message)))
compilersartefacts.addResolvedContract(contractAddress, compData)
async (url, cb) => await this.call('contentImport', 'resolveAndSave', url).then((result) => cb(null, result)).catch((error) => cb(error.message)))
await this.call('compilerArtefacts', 'addResolvedContract', contractAddress, compData)
return compData
} catch (e) {
this.unresolvedAddresses.push(contractAddress)

@ -0,0 +1,145 @@
'use strict'
import { Plugin } from '@remixproject/engine'
import { CompilerAbstract } from '@remix-project/remix-solidity'
const profile = {
name: 'compilerMetadata',
methods: ['deployMetadataOf'],
events: [],
version: '0.0.1'
}
export class CompilerMetadata extends Plugin {
networks: string[]
innerPath: string
constructor () {
super(profile)
this.networks = ['VM:-', 'main:1', 'ropsten:3', 'rinkeby:4', 'kovan:42', 'görli:5', 'Custom']
this.innerPath = 'artifacts'
}
_JSONFileName (path, contractName) {
return this.joinPath(path, this.innerPath, contractName + '.json')
}
_MetadataFileName (path, contractName) {
return this.joinPath(path, this.innerPath, contractName + '_metadata.json')
}
onActivation () {
var self = this
this.on('solidity', 'compilationFinished', async (file, source, languageVersion, data) => {
if (!await this.call('settings', 'get', 'settings/generate-contract-metadata')) return
const compiler = new CompilerAbstract(languageVersion, data, source)
var path = self._extractPathOf(source.target)
compiler.visitContracts((contract) => {
if (contract.file !== source.target) return
(async () => {
const fileName = self._JSONFileName(path, contract.name)
const content = await this.call('fileManager', 'exists', fileName) ? await this.call('fileManager', 'readFile', fileName) : null
await this._setArtefacts(content, contract, path)
})()
})
})
}
_extractPathOf (file) {
var reg = /(.*)(\/).*/
var path = reg.exec(file)
return path ? path[1] : '/'
}
async _setArtefacts (content, contract, path) {
content = content || '{}'
var metadata
try {
metadata = JSON.parse(content)
} catch (e) {
console.log(e)
}
var fileName = this._JSONFileName(path, contract.name)
var metadataFileName = this._MetadataFileName(path, contract.name)
var deploy = metadata.deploy || {}
this.networks.forEach((network) => {
deploy[network] = this._syncContext(contract, deploy[network] || {})
})
let parsedMetadata
try {
parsedMetadata = JSON.parse(contract.object.metadata)
} catch (e) {
console.log(e)
}
if (parsedMetadata) await this.call('fileManager', 'writeFile', metadataFileName, JSON.stringify(parsedMetadata, null, '\t'))
var data = {
deploy,
data: {
bytecode: contract.object.evm.bytecode,
deployedBytecode: contract.object.evm.deployedBytecode,
gasEstimates: contract.object.evm.gasEstimates,
methodIdentifiers: contract.object.evm.methodIdentifiers
},
abi: contract.object.abi
}
await this.call('fileManager', 'writeFile', fileName, JSON.stringify(data, null, '\t'))
}
_syncContext (contract, metadata) {
var linkReferences = metadata.linkReferences
var autoDeployLib = metadata.autoDeployLib
if (!linkReferences) linkReferences = {}
if (autoDeployLib === undefined) autoDeployLib = true
for (var libFile in contract.object.evm.bytecode.linkReferences) {
if (!linkReferences[libFile]) linkReferences[libFile] = {}
for (var lib in contract.object.evm.bytecode.linkReferences[libFile]) {
if (!linkReferences[libFile][lib]) {
linkReferences[libFile][lib] = '<address>'
}
}
}
metadata.linkReferences = linkReferences
metadata.autoDeployLib = autoDeployLib
return metadata
}
async deployMetadataOf (contractName, fileLocation) {
let path
if (fileLocation) {
path = fileLocation.split('/')
path.pop()
path = path.join('/')
} else {
try {
path = this._extractPathOf(await this.call('fileManager', 'getCurrentFile'))
} catch (err) {
console.log(err)
throw new Error(err)
}
}
try {
const { id, name } = await this.call('network', 'detectNetwork')
const fileName = this._JSONFileName(path, contractName)
try {
const content = await this.call('fileManager', 'readFile', fileName)
if (!content) return null
let metadata = JSON.parse(content)
metadata = metadata.deploy || {}
return metadata[name + ':' + id] || metadata[name] || metadata[id] || metadata[name.toLowerCase() + ':' + id] || metadata[name.toLowerCase()]
} catch (err) {
return null
}
} catch (err) {
console.log(err)
throw new Error(err)
}
}
joinPath (...paths) {
paths = paths.filter((value) => value !== '').map((path) => path.replace(/^\/|\/$/g, '')) // remove first and last slash)
if (paths.length === 1) return paths[0]
return paths.join('/')
}
}

@ -0,0 +1,77 @@
'use strict'
import { Plugin } from '@remixproject/engine'
import { sourceMappingDecoder } from '@remix-project/remix-debug'
const profile = {
name: 'offsetToLineColumnConverter',
methods: ['offsetToLineColumn'],
events: [],
version: '0.0.1'
}
export class OffsetToLineColumnConverter extends Plugin {
lineBreakPositionsByContent: {}
sourceMappingDecoder: any
constructor () {
super(profile)
this.lineBreakPositionsByContent = {}
this.sourceMappingDecoder = sourceMappingDecoder
}
/**
* Convert offset representation with line/column representation.
* This is also used to resolve the content:
* @arg file is the index of the file in the content sources array and content sources array does have filename as key and not index.
* So we use the asts (which references both index and filename) to look up the actual content targeted by the @arg file index.
* @param {{start, length}} rawLocation - offset location
* @param {number} file - The index where to find the source in the sources parameters
* @param {Object.<string, {content}>} sources - Map of content sources
* @param {Object.<string, {ast, id}>} asts - Map of content sources
*/
offsetToLineColumn (rawLocation, file, sources, asts) {
if (!this.lineBreakPositionsByContent[file]) {
const sourcesArray = Object.keys(sources)
if (!asts || (file === 0 && sourcesArray.length === 1)) {
// if we don't have ast, we process the only one available content (applicable also for compiler older than 0.4.12)
this.lineBreakPositionsByContent[file] = this.sourceMappingDecoder.getLinebreakPositions(sources[sourcesArray[0]].content)
} else {
for (var filename in asts) {
const source = asts[filename]
if (source.id === file) {
this.lineBreakPositionsByContent[file] = this.sourceMappingDecoder.getLinebreakPositions(sources[filename].content)
break
}
}
}
}
return this.sourceMappingDecoder.convertOffsetToLineColumn(rawLocation, this.lineBreakPositionsByContent[file])
}
/**
* Convert offset representation with line/column representation.
* @param {{start, length}} rawLocation - offset location
* @param {number} file - The index where to find the source in the sources parameters
* @param {string} content - source
*/
offsetToLineColumnWithContent (rawLocation, file, content) {
this.lineBreakPositionsByContent[file] = this.sourceMappingDecoder.getLinebreakPositions(content)
return this.sourceMappingDecoder.convertOffsetToLineColumn(rawLocation, this.lineBreakPositionsByContent[file])
}
/**
* Clear the cache
*/
clear () {
this.lineBreakPositionsByContent = {}
}
/**
* called by plugin API
*/
activate () {
this.on('solidity', 'compilationFinished', () => {
this.clear()
})
}
}

@ -0,0 +1,10 @@
{
"extends": "../../tsconfig.json",
"files": [],
"include": [],
"references": [
{
"path": "./tsconfig.lib.json"
}
]
}

@ -0,0 +1,12 @@
{
"extends": "./tsconfig.json",
"compilerOptions": {
"module": "commonjs",
"outDir": "../../dist/out-tsc",
"declaration": true,
"rootDir": "./src",
"types": ["node"]
},
"exclude": ["**/*.spec.ts"],
"include": ["**/*.ts"]
}

@ -147,13 +147,17 @@ export class CodeManager {
private async retrieveIndexAndTrigger (codeMananger, address, step, code) {
let result
let next
const next = []
const returnInstructionIndexes = []
const outOfGasInstructionIndexes = []
try {
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()
if (values) {

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

@ -67,7 +67,7 @@ export class EventManager {
}
for (const listener in this.registered[eventName]) {
const l = this.registered[eventName][listener]
l.func.apply(l.obj === this.anonymous ? {} : l.obj, args)
if (l.func) l.func.apply(l.obj === this.anonymous ? {} : l.obj, args)
}
}
}

@ -48,7 +48,7 @@ export async function extractHexValue (location, storageResolver, byteLength) {
try {
slotvalue = await readFromStorage(location.slot, storageResolver)
} catch (e) {
return '0x'
return ''
}
return extractHexByteSlice(slotvalue, byteLength, location.offset)
}

@ -151,7 +151,7 @@ export class TraceManager {
if (this.trace[stepIndex] && this.trace[stepIndex].stack) { // there's always a stack
const stack = this.trace[stepIndex].stack.slice(0)
stack.reverse()
return stack
return stack.map(el => el.startsWith('0x') ? el : '0x' + el)
} else {
throw new Error('no stack found')
}

@ -64,7 +64,7 @@ export class EventManager {
}
for (const listener in this.registered[eventName]) {
const l = this.registered[eventName][listener]
l.func.apply(l.obj === this.anonymous ? {} : l.obj, args)
if (l.func) l.func.apply(l.obj === this.anonymous ? {} : l.obj, args)
}
}
}

@ -12,23 +12,6 @@ export function extendWeb3 (web3) {
this.extend(web3)
}
export function setProvider (web3, url) {
web3.setProvider(new web3.providers.HttpProvider(url))
}
export function web3DebugNode (network) {
const web3DebugNodes = {
Main: 'https://gethmainnet.komputing.org',
Rinkeby: 'https://remix-rinkeby.ethdevops.io',
Ropsten: 'https://remix-ropsten.ethdevops.io',
Goerli: 'https://remix-goerli.ethdevops.io'
}
if (web3DebugNodes[network]) {
return this.loadWeb3(web3DebugNodes[network])
}
return null
}
export function extend (web3) {
if (!web3.extend) {
return

@ -0,0 +1,48 @@
'use strict'
import txHelper from './txHelper'
export class CompilerAbstract {
languageversion: any
data: any
source: 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
}
}

@ -1,7 +1,7 @@
'use strict'
import { canUseWorker, urlFromVersion } from './compiler-utils'
import { Compiler } from '@remix-project/remix-solidity'
import CompilerAbstract from './compiler-abstract'
import { CompilerAbstract } from './compiler-abstract'
import { Compiler } from './compiler'
export const compile = async (compilationTargets, settings, contentResolverCallback) => {
const res = await (() => {

@ -1,3 +1,6 @@
export { Compiler } from './compiler/compiler'
export { compile } from './compiler/compiler-helpers'
export { default as CompilerInput } from './compiler/compiler-input'
export { CompilerAbstract } from './compiler/compiler-abstract'
export * from './compiler/types'
export * from './compiler/compiler-utils'

@ -62,7 +62,7 @@ export default class EventManager {
}
for (const listener in this.registered[eventName]) {
const l = this.registered[eventName][listener]
l.func.apply(l.obj === this.anonymous ? {} : l.obj, args)
if (l.func) l.func.apply(l.obj === this.anonymous ? {} : l.obj, args)
}
}
}

@ -1,27 +1,37 @@
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 { Placement } from 'react-bootstrap/esm/Overlay'
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 handleClick = (event) => {
const handleClick = (e) => {
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 {
if (typeof content !== 'string') {
content = JSON.stringify(content, null, '\t')
}
copy(content)
setMessage('Copied')
} catch (e) {
console.error(e)
}
copy(content)
setMessage('Copied')
} else {
setMessage('Cannot copy empty content!')
}
event.preventDefault()
e.preventDefault()
return false
}
@ -31,14 +41,16 @@ export const CopyToClipboard = ({ content, tip='Copy', icon='fa-copy', ...otherP
return (
<a href='#' onClick={handleClick} onMouseLeave={reset}>
<OverlayTrigger placement="right" overlay={
<OverlayTrigger placement={direction} overlay={
<Tooltip id="overlay-tooltip">
{ message }
</Tooltip>
}>
<i className={`far ${icon} ml-1 p-2`} aria-hidden="true"
{...otherProps}
></i>
{
children || (<i className={`far ${icon} ml-1 p-2`} aria-hidden="true"
{...otherProps}
></i>)
}
</OverlayTrigger>
</a>
)

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

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

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

@ -1,8 +1,13 @@
'use strict'
var remixLib = require('@remix-project/remix-lib')
var txHelper = remixLib.execution.txHelper
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>
module.exports = class CompilerAbstract {
constructor (languageversion, data, source) {
this.languageversion = languageversion
this.data = data

@ -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"]
}

@ -30,6 +30,10 @@ export class RemixURLResolver {
constructor (gistToken?: string, protocol = 'http:') {
this.previouslyHandled = {}
this.setGistToken(gistToken, protocol)
}
async setGistToken (gistToken?: string, protocol = 'http:') {
this.gistAccessToken = gistToken || ''
this.protocol = protocol
}
@ -109,7 +113,7 @@ export class RemixURLResolver {
url = url.replace(/^ipfs:\/\/?/, 'ipfs/')
// eslint-disable-next-line no-useless-catch
try {
const req = 'https://ipfsgw.komputing.org/' + url
const req = 'https://ipfs.remixproject.org/' + url
// If you don't find greeter.sol on ipfs gateway use local
// const req = 'http://localhost:8080/' + url
const response: AxiosResponse = await axios.get(req)

@ -6,6 +6,7 @@
"https://remix.ethereum.org",
"package://a7df6d3c223593f3550b35e90d7b0b1f.mod",
"package://6fd22d6fe5549ad4c4d8fd3ca0b7816b.mod",
"https://ipfsgw.komputing.org"
"https://ipfsgw.komputing.org",
"https://ipfs.remixproject.org"
]
}

@ -105,7 +105,19 @@
"remix-ui-checkbox": {
"tags": []
},
"remix-ui-terminal": {
"remix-ui-terminal": {
},
"remix-core-plugin": {
"tags": []
},
"remix-ui-solidity-compiler": {
"tags": []
},
"remix-ui-publish-to-storage": {
"tags": []
},
"remix-ui-renderer": {
"tags": []
}
}
}

31
package-lock.json generated

@ -13250,11 +13250,14 @@
"is-plain-object": "^2.0.1"
}
},
"copy-text-to-clipboard": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/copy-text-to-clipboard/-/copy-text-to-clipboard-1.0.4.tgz",
"integrity": "sha512-4hDE+0bgqm4G/nXnt91CP3rc0vOptaePPU5WfVZuhv2AYNJogdLHR4pF1XPgXDAGY4QCzj9pD7zKATa+50sQPg==",
"dev": true
"copy-to-clipboard": {
"version": "3.3.1",
"resolved": "https://registry.npmjs.org/copy-to-clipboard/-/copy-to-clipboard-3.3.1.tgz",
"integrity": "sha512-i13qo6kIHTTpCm8/Wup+0b1mVWETvu2kIMzKoK8FpkLkFxlt0znUAHcMzox+T8sPlqtZXq3CulEjQHsYiGFJUw==",
"dev": true,
"requires": {
"toggle-selection": "^1.0.6"
}
},
"copy-webpack-plugin": {
"version": "5.1.1",
@ -17562,12 +17565,6 @@
"resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
"integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8="
},
"fsevents": {
"version": "2.3.2",
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
"integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
"optional": true
},
"fstream": {
"version": "1.0.11",
"resolved": "https://registry.npmjs.org/fstream/-/fstream-1.0.11.tgz",
@ -21935,6 +21932,12 @@
"which": "^2.0.2"
},
"dependencies": {
"fsevents": {
"version": "2.3.2",
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
"integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
"optional": true
},
"has-flag": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
@ -37193,6 +37196,12 @@
"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": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz",

@ -41,7 +41,7 @@
"workspace-schematic": "nx workspace-schematic",
"dep-graph": "nx dep-graph",
"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",
"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",
"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",
@ -234,7 +234,7 @@
"browserify": "^16.2.3",
"browserify-reload": "^1.0.3",
"component-type": "^1.2.1",
"copy-text-to-clipboard": "^1.0.4",
"copy-to-clipboard": "^3.3.1",
"csjs-inject": "^1.0.1",
"csslint": "^1.0.2",
"cypress": "^4.1.0",

@ -20,14 +20,10 @@
"@remix-project/remix-astwalker": ["dist/libs/remix-astwalker/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-simulator": [
"dist/libs/remix-simulator/src/index.js"
],
"@remix-project/remix-simulator": ["dist/libs/remix-simulator/src/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-url-resolver": [
"dist/libs/remix-url-resolver/index.js"
],
"@remix-project/remix-url-resolver": ["dist/libs/remix-url-resolver/index.js"],
"@remixproject/debugger-plugin": ["apps/debugger/src/index.ts"],
"@remix-project/remixd": ["dist/libs/remixd/index.js"],
"@remix-ui/tree-view": ["libs/remix-ui/tree-view/src/index.ts"],
@ -39,10 +35,14 @@
"@remix-ui/toaster": ["libs/remix-ui/toaster/src/index.ts"],
"@remix-ui/file-explorer": ["libs/remix-ui/file-explorer/src/index.ts"],
"@remix-ui/workspace": ["libs/remix-ui/workspace/src/index.ts"],
"@remix-ui/settings": ["libs/remix-ui/settings/src/index.ts"],
"@remix-ui/static-analyser": ["libs/remix-ui/static-analyser/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-project/core-plugin": ["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"]

@ -811,6 +811,90 @@
}
}
}
},
"remix-core-plugin": {
"root": "libs/remix-core-plugin",
"sourceRoot": "libs/remix-core-plugin/src",
"projectType": "library",
"schematics": {},
"architect": {
"lint": {
"builder": "@nrwl/linter:lint",
"options": {
"linter": "eslint",
"tsConfig": [
"libs/remix-core-plugin/tsconfig.lib.json"
],
"exclude": [
"**/node_modules/**",
"!libs/remix-core-plugin/**/*"
]
}
},
"build": {
"builder": "@nrwl/node:package",
"options": {
"outputPath": "dist/libs/core-plugin",
"tsConfig": "libs/remix-core-plugin/tsconfig.lib.json",
"packageJson": "libs/remix-core-plugin/package.json",
"main": "libs/remix-core-plugin/src/index.ts"
}
}
}
},
"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": {

Loading…
Cancel
Save