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

pull/1342/head
davidzagi93@gmail.com 3 years ago
commit 213bad77ff
  1. 3
      apps/remix-ide-e2e/src/commands/verifyContracts.ts
  2. 2
      apps/remix-ide-e2e/src/tests/ballot.test.ts
  3. 2
      apps/remix-ide-e2e/src/tests/ballot_0_4_11.spec.ts
  4. 4
      apps/remix-ide-e2e/src/tests/debugger.spec.ts
  5. 6
      apps/remix-ide/src/app.js
  6. 702
      apps/remix-ide/src/app/files/file-explorer.js
  7. 16
      apps/remix-ide/src/app/files/remixd-handle.js
  8. 1
      apps/remix-ide/src/app/tabs/compile-tab.js
  9. 135
      apps/remix-ide/src/app/tabs/compileTab/compileTab.js
  10. 580
      apps/remix-ide/src/app/tabs/compileTab/compilerContainer.js
  11. 122
      apps/remix-ide/src/app/tabs/compileTab/contractParser.js
  12. 2
      apps/remix-ide/src/app/tabs/hardhat-provider.js
  13. 7
      apps/remix-ide/src/app/tabs/runTab/contractDropdown.js
  14. 7
      apps/remix-ide/src/app/tabs/runTab/recorder.js
  15. 8
      apps/remix-ide/src/app/tabs/runTab/settings.js
  16. 88
      apps/remix-ide/src/app/ui/confirmDialog.js
  17. 28
      apps/remix-ide/src/app/ui/landing-page/landing-page.js
  18. 7
      apps/remix-ide/src/app/ui/sendTxCallbacks.js
  19. BIN
      apps/remix-ide/src/assets/img/optimismLogo.webp
  20. 8
      apps/remix-ide/src/assets/js/editor/darkTheme.js
  21. 10
      apps/remix-ide/src/blockchain/execution-context.js
  22. 14
      libs/remix-analyzer/package.json
  23. 14
      libs/remix-astwalker/package.json
  24. 11
      libs/remix-core-plugin/src/lib/compiler-content-imports.ts
  25. 14
      libs/remix-debug/package.json
  26. 2
      libs/remix-debug/src/code/codeUtils.ts
  27. 2
      libs/remix-debug/src/trace/traceManager.ts
  28. 2
      libs/remix-debug/test/decoder/vmCall.ts
  29. 2
      libs/remix-debug/test/vmCall.ts
  30. 10
      libs/remix-lib/package.json
  31. 6
      libs/remix-lib/src/execution/forkAt.ts
  32. 2
      libs/remix-lib/src/execution/txRunnerVM.ts
  33. 18
      libs/remix-lib/src/execution/txRunnerWeb3.ts
  34. 14
      libs/remix-simulator/package.json
  35. 2
      libs/remix-simulator/src/vm-context.ts
  36. 14
      libs/remix-solidity/package.json
  37. 18
      libs/remix-tests/package.json
  38. 1
      libs/remix-ui/clipboard/src/lib/copy-to-clipboard/copy-to-clipboard.tsx
  39. 12
      libs/remix-ui/file-explorer/src/lib/file-explorer-context-menu.tsx
  40. 26
      libs/remix-ui/modal-dialog/src/lib/remix-ui-modal-dialog.tsx
  41. 2
      libs/remix-ui/solidity-compiler/src/lib/actions/compiler.ts
  42. 51
      libs/remix-ui/solidity-compiler/src/lib/compiler-container.tsx
  43. 30
      libs/remix-ui/solidity-compiler/src/lib/contract-selection.tsx
  44. 51
      libs/remix-ui/solidity-compiler/src/lib/logic/compiler-abstract.ts
  45. 19
      libs/remix-ui/solidity-compiler/src/lib/logic/compiler-helpers.ts
  46. 46
      libs/remix-ui/solidity-compiler/src/lib/logic/compiler-utils.ts
  47. 3
      libs/remix-ui/solidity-compiler/src/lib/logic/index.ts
  48. 58
      libs/remix-ui/static-analyser/src/lib/remix-ui-static-analyser.tsx
  49. 4
      libs/remix-url-resolver/package.json
  50. 2
      libs/remixd/package.json
  51. 6
      libs/remixd/src/services/slitherClient.ts
  52. 816
      package-lock.json
  53. 12
      package.json
  54. 11
      release-process.md

@ -22,6 +22,9 @@ function verifyContracts (browser: NightwatchBrowser, compiledContractNames: str
if (opts.version) { if (opts.version) {
browser browser
.click('*[data-id="compilation-details"]') .click('*[data-id="compilation-details"]')
.waitForElementVisible('*[data-id="remixui_treeviewitem_metadata"]')
.pause(2000)
.click('*[data-id="remixui_treeviewitem_metadata"]')
.waitForElementVisible('*[data-id="treeViewDivtreeViewItemcompiler"]') .waitForElementVisible('*[data-id="treeViewDivtreeViewItemcompiler"]')
.pause(2000) .pause(2000)
.click('*[data-id="treeViewDivtreeViewItemcompiler"]') .click('*[data-id="treeViewDivtreeViewItemcompiler"]')

@ -49,7 +49,7 @@ module.exports = {
'Debug Ballot / delegate': function (browser: NightwatchBrowser) { 'Debug Ballot / delegate': function (browser: NightwatchBrowser) {
browser.pause(500) browser.pause(500)
.click('*[data-id="txLoggerDebugButton0xf88bc0ac0761f78d8c883b32550c68dadcdb095595c30e1a1b7c583e5e958dcb"]') .debugTransaction(1)
.waitForElementVisible('*[data-id="buttonNavigatorJumpPreviousBreakpoint"]') .waitForElementVisible('*[data-id="buttonNavigatorJumpPreviousBreakpoint"]')
.click('*[data-id="buttonNavigatorJumpPreviousBreakpoint"]') .click('*[data-id="buttonNavigatorJumpPreviousBreakpoint"]')
.pause(2000) .pause(2000)

@ -45,7 +45,7 @@ module.exports = {
'Debug Ballot / delegate': function (browser: NightwatchBrowser) { 'Debug Ballot / delegate': function (browser: NightwatchBrowser) {
browser.pause(500) browser.pause(500)
.click('*[data-id="txLoggerDebugButton0xf88bc0ac0761f78d8c883b32550c68dadcdb095595c30e1a1b7c583e5e958dcb"]') .debugTransaction(1)
.pause(2000) .pause(2000)
.waitForElementVisible('*[data-id="buttonNavigatorJumpPreviousBreakpoint"]') .waitForElementVisible('*[data-id="buttonNavigatorJumpPreviousBreakpoint"]')
.click('*[data-id="buttonNavigatorJumpPreviousBreakpoint"]') .click('*[data-id="buttonNavigatorJumpPreviousBreakpoint"]')

@ -423,7 +423,7 @@ const localVariable_step717_ABIEncoder = { // eslint-disable-line
const jsGetTrace = `(async () => { const jsGetTrace = `(async () => {
try { try {
const result = await remix.call('debugger', 'getTrace', '0xa38bff6f06e7c4fc91df1db6aa31a69ab5d5882faa953b1e7a88bfa523268ed7') const result = await remix.call('debugger', 'getTrace', '0x9341be49e911afe99bf1abc67cbcf36739d2e6470a08a69511c205a0737d7332')
console.log('result ', result) console.log('result ', result)
} catch (e) { } catch (e) {
console.log(e.message) console.log(e.message)
@ -432,7 +432,7 @@ const jsGetTrace = `(async () => {
const jsDebug = `(async () => { const jsDebug = `(async () => {
try { try {
const result = await remix.call('debugger', 'debug', '0xa38bff6f06e7c4fc91df1db6aa31a69ab5d5882faa953b1e7a88bfa523268ed7') const result = await remix.call('debugger', 'debug', '0x9341be49e911afe99bf1abc67cbcf36739d2e6470a08a69511c205a0737d7332')
console.log('result ', result) console.log('result ', result)
} catch (e) { } catch (e) {
console.log(e.message) console.log(e.message)

@ -342,7 +342,7 @@ Please make a backup of your contracts and start using http://remix.ethereum.org
const hiddenPanel = new HiddenPanel() const hiddenPanel = new HiddenPanel()
const pluginManagerComponent = new PluginManagerComponent(appManager, engine) const pluginManagerComponent = new PluginManagerComponent(appManager, engine)
const filePanel = new FilePanel(appManager) const filePanel = new FilePanel(appManager)
const landingPage = new LandingPage(appManager, menuicons, fileManager, filePanel) const landingPage = new LandingPage(appManager, menuicons, fileManager, filePanel, contentImport)
const settings = new SettingsTab( const settings = new SettingsTab(
registry.get('config').api, registry.get('config').api,
editor, editor,
@ -423,6 +423,8 @@ Please make a backup of your contracts and start using http://remix.ethereum.org
fn: null fn: null
} }
) )
} else {
startWalkthroughService()
} }
// CONTENT VIEWS & DEFAULT PLUGINS // CONTENT VIEWS & DEFAULT PLUGINS
@ -518,6 +520,4 @@ Please make a backup of your contracts and start using http://remix.ethereum.org
if (params.embed) framingService.embed() if (params.embed) framingService.embed()
framingService.start(params) framingService.start(params)
startWalkthroughService()
} }

@ -1,702 +0,0 @@
/* global FileReader */
/* global fetch */
const async = require('async')
const Gists = require('gists')
const modalDialogCustom = require('../ui/modal-dialog-custom')
const tooltip = require('../ui/tooltip')
const QueryParams = require('../../lib/query-params')
const helper = require('../../lib/helper')
const yo = require('yo-yo')
const Treeview = require('../ui/TreeView')
const modalDialog = require('../ui/modaldialog')
const EventManager = require('events')
const contextMenu = require('../ui/contextMenu')
const css = require('./styles/file-explorer-styles')
const globalRegistry = require('../../global/registry')
const queryParams = new QueryParams()
let MENU_HANDLE
function fileExplorer (localRegistry, files, menuItems, plugin) {
var self = this
this.events = new EventManager()
// file provider backend
this.files = files
// element currently focused on
this.focusElement = null
// path currently focused on
this.focusPath = null
const allItems =
[
{
action: 'createNewFile',
title: 'Create New File',
icon: 'fas fa-plus-circle'
},
{
action: 'publishToGist',
title: 'Publish all [browser] explorer files to a github gist',
icon: 'fab fa-github'
},
{
action: 'uploadFile',
title: 'Add Local file to the Browser Storage Explorer',
icon: 'far fa-folder-open'
},
{
action: 'updateGist',
title: 'Update the current [gist] explorer',
icon: 'fab fa-github'
}
]
// menu items
this.menuItems = allItems.filter(
(item) => {
if (menuItems) return menuItems.find((name) => { return name === item.action })
}
)
self._components = {}
self._components.registry = localRegistry || globalRegistry
self._deps = {
config: self._components.registry.get('config').api,
editor: self._components.registry.get('editor').api,
fileManager: self._components.registry.get('filemanager').api
}
self.events.register('focus', function (path) {
self._deps.fileManager.open(path)
})
self._components.registry.put({ api: self, name: `fileexplorer/${self.files.type}` })
// warn if file changed outside of Remix
function remixdDialog () {
return yo`<div>This file has been changed outside of Remix IDE.</div>`
}
this.files.event.register('fileExternallyChanged', (path, file) => {
if (self._deps.config.get('currentFile') === path && self._deps.editor.currentContent() && self._deps.editor.currentContent() !== file.content) {
if (this.files.isReadOnly(path)) return self._deps.editor.setText(file.content)
modalDialog(path + ' changed', remixdDialog(),
{
label: 'Replace by the new content',
fn: () => {
self._deps.editor.setText(file.content)
}
},
{
label: 'Keep the content displayed in Remix',
fn: () => {}
}
)
}
})
// register to event of the file provider
files.event.on('fileRemoved', fileRemoved)
files.event.on('fileRenamed', fileRenamed)
files.event.on('fileRenamedError', fileRenamedError)
files.event.on('fileAdded', fileAdded)
files.event.on('folderAdded', folderAdded)
function fileRenamedError (error) {
modalDialogCustom.alert(error)
}
function fileAdded (filepath) {
self.ensureRoot(() => {
const folderpath = filepath.split('/').slice(0, -1).join('/')
const currentTree = self.treeView.nodeAt(folderpath)
if (!self.treeView.isExpanded(folderpath)) self.treeView.expand(folderpath)
if (currentTree) {
self.files.resolveDirectory(folderpath, (error, fileTree) => {
if (error) console.error(error)
if (!fileTree) return
fileTree = normalize(folderpath, fileTree)
self.treeView.updateNodeFromJSON(folderpath, fileTree, true)
self.focusElement = self.treeView.labelAt(self.focusPath)
// TODO: here we update the selected file (it applicable)
// cause we are refreshing the interface of the whole directory when there's a new file.
if (self.focusElement && !self.focusElement.classList.contains('bg-secondary')) {
self.focusElement.classList.add('bg-secondary')
}
})
}
})
}
function extractNameFromKey (key) {
const keyPath = key.split('/')
return keyPath[keyPath.length - 1]
}
function folderAdded (folderpath) {
self.ensureRoot(() => {
folderpath = folderpath.split('/').slice(0, -1).join('/')
self.files.resolveDirectory(folderpath, (error, fileTree) => {
if (error) console.error(error)
if (!fileTree) return
fileTree = normalize(folderpath, fileTree)
self.treeView.updateNodeFromJSON(folderpath, fileTree, true)
if (!self.treeView.isExpanded(folderpath)) self.treeView.expand(folderpath)
})
})
}
function fileRemoved (filepath) {
const label = self.treeView.labelAt(filepath)
filepath = filepath.split('/').slice(0, -1).join('/')
if (label && label.parentElement) {
label.parentElement.removeChild(label)
}
self.updatePath(filepath)
}
function fileRenamed (oldName, newName, isFolder) {
fileRemoved(oldName)
fileAdded(newName)
}
// make interface and register to nodeClick, leafClick
self.treeView = new Treeview({
extractData: function extractData (value, tree, key) {
var newValue = {}
// var isReadOnly = false
var isFile = false
Object.keys(value).filter(function keep (x) {
if (x === '/content') isFile = true
if (x[0] !== '/') return true
}).forEach(function (x) { newValue[x] = value[x] })
return {
path: (tree || {}).path ? tree.path + '/' + key : key,
children: isFile ? undefined
: value instanceof Array ? value.map((item, index) => ({
key: index, value: item
})) : value instanceof Object ? Object.keys(value).map(subkey => ({
key: subkey, value: value[subkey]
})) : undefined
}
},
formatSelf: function formatSelf (key, data, li) {
const isRoot = data.path === self.files.type
const isFolder = !!data.children
return yo`
<div class="${css.items}">
<span
title="${data.path}"
class="${css.label} ${!isRoot ? !isFolder ? css.leaf : css.folder : ''}"
data-path="${data.path}"
style="${isRoot ? 'font-weight:bold;' : ''}"
onkeydown=${editModeOff}
onblur=${editModeOff}
>
${key.split('/').pop()}
</span>
${isRoot ? self.renderMenuItems() : ''}
</div>
`
}
})
/**
* Extracts first two folders as a subpath from the path.
**/
function extractExternalFolder (path) {
const firstSlIndex = path.indexOf('/', 1)
if (firstSlIndex === -1) return ''
const secondSlIndex = path.indexOf('/', firstSlIndex + 1)
if (secondSlIndex === -1) return ''
return path.substring(0, secondSlIndex)
}
self.treeView.event.register('nodeRightClick', function (key, data, label, event) {
if (self.files.readonly) return
if (key === self.files.type) return
MENU_HANDLE && MENU_HANDLE.hide(null, true)
const actions = {}
const provider = self._deps.fileManager.fileProviderOf(key)
actions['Create File'] = () => self.createNewFile(key)
actions['Create Folder'] = () => self.createNewFolder(key)
// @todo(#2386) not fully implemented. Readd later when fixed
if (provider.isExternalFolder(key)) {
/* actions['Discard changes'] = () => {
modalDialogCustom.confirm(
'Discard changes',
'Are you sure you want to discard all your changes?',
() => { self.files.discardChanges(key) },
() => {}
)
} */
} else {
const folderPath = extractExternalFolder(key)
actions.Rename = () => {
if (self.files.isReadOnly(key)) { return tooltip('cannot rename folder. ' + self.files.type + ' is a read only explorer') }
var name = label.querySelector('span[data-path="' + key + '"]')
if (name) editModeOn(name)
}
actions.Delete = () => {
if (self.files.isReadOnly(key)) { return tooltip('cannot delete folder. ' + self.files.type + ' is a read only explorer') }
const currentFoldername = extractNameFromKey(key)
modalDialogCustom.confirm('Confirm to delete folder', `Are you sure you want to delete ${currentFoldername} folder?`,
async () => {
const fileManager = self._deps.fileManager
const removeFolder = await fileManager.remove(key)
if (!removeFolder) {
tooltip(`failed to remove ${key}. Make sure the directory is empty before removing it.`)
}
}, () => {})
}
if (folderPath === 'browser/gists') {
actions['Push changes to gist'] = () => {
const id = key.substr(key.lastIndexOf('/') + 1, key.length - 1)
modalDialogCustom.confirm(
'Push back to Gist',
'Are you sure you want to push all your changes back to Gist?',
() => { self.toGist(id) },
() => {}
)
}
}
}
MENU_HANDLE = contextMenu(event, actions)
})
self.treeView.event.register('leafRightClick', function (key, data, label, event) {
if (key === self.files.type) return
MENU_HANDLE && MENU_HANDLE.hide(null, true)
const actions = {}
const provider = self._deps.fileManager.fileProviderOf(key)
if (!provider.isExternalFolder(key)) {
actions['Create Folder'] = () => self.createNewFolder(self._deps.fileManager.extractPathOf(key))
actions.Rename = () => {
if (self.files.isReadOnly(key)) { return tooltip('cannot rename file. ' + self.files.type + ' is a read only explorer') }
var name = label.querySelector('span[data-path="' + key + '"]')
if (name) editModeOn(name)
}
actions.Delete = () => {
if (self.files.isReadOnly(key)) { return tooltip('cannot delete file. ' + self.files.type + ' is a read only explorer') }
const currentFilename = extractNameFromKey(key)
modalDialogCustom.confirm(
'Delete file', `Are you sure you want to delete ${currentFilename} file?`,
async () => {
const fileManager = self._deps.fileManager
const removeFile = await fileManager.remove(key)
if (!removeFile) {
tooltip(`Failed to remove file ${key}.`)
}
},
() => {}
)
}
if (key.endsWith('.js')) {
actions.Run = async () => {
provider.get(key, (error, content) => {
if (error) return console.log(error)
plugin.call('scriptRunner', 'execute', content)
})
}
}
}
MENU_HANDLE = contextMenu(event, actions)
})
self.treeView.event.register('leafClick', function (key, data, label) {
self.events.trigger('focus', [key])
})
self.treeView.event.register('nodeClick', function (path, childrenContainer) {
if (!childrenContainer) return
if (childrenContainer.style.display === 'none') return
self.updatePath(path)
})
// register to main app, trigger when the current file in the editor changed
self._deps.fileManager.events.on('currentFileChanged', (newFile) => {
const provider = self._deps.fileManager.fileProviderOf(newFile)
if (self.focusElement && self.focusPath !== newFile) {
self.focusElement.classList.remove('bg-secondary')
self.focusElement = null
self.focusPath = null
}
if (provider && (provider.type === files.type)) {
self.focusElement = self.treeView.labelAt(newFile)
if (self.focusElement) {
self.focusElement.classList.add('bg-secondary')
self.focusPath = newFile
}
}
})
self._deps.fileManager.events.on('noFileSelected', () => {
if (self.focusElement) {
self.focusElement.classList.remove('bg-secondary')
self.focusElement = null
self.focusPath = null
}
})
var textUnderEdit = null
function selectElementContents (el) {
var range = document.createRange()
range.selectNodeContents(el)
var sel = window.getSelection()
sel.removeAllRanges()
sel.addRange(range)
}
function editModeOn (label) {
textUnderEdit = label.innerText
label.setAttribute('contenteditable', true)
label.classList.add('bg-light')
label.focus()
selectElementContents(label)
}
function editModeOff (event) {
const label = this
const isFolder = label.className.indexOf('folder') !== -1
function rename () {
var newPath = label.dataset.path
newPath = newPath.split('/')
newPath[newPath.length - 1] = label.innerText
newPath = newPath.join('/')
if (label.innerText === '') {
modalDialogCustom.alert('File name cannot be empty')
label.innerText = textUnderEdit
} else if (helper.checkSpecialChars(label.innerText)) {
modalDialogCustom.alert('Special characters are not allowed')
label.innerText = textUnderEdit
} else {
files.exists(newPath, (error, exist) => {
if (error) return modalDialogCustom.alert('Unexpected error while renaming: ' + error)
if (!exist) {
files.rename(label.dataset.path, newPath, isFolder)
} else {
modalDialogCustom.alert('File already exists.')
label.innerText = textUnderEdit
}
})
}
}
if (event.which === 13) event.preventDefault()
if ((event.type === 'blur' || event.which === 13) && label.getAttribute('contenteditable')) {
var save = textUnderEdit !== label.innerText
if (save) {
modalDialogCustom.confirm(
'Confirm to rename a ' + (isFolder ? 'folder' : 'file'),
'Are you sure you want to rename ' + textUnderEdit + '?',
() => { rename() },
() => { label.innerText = textUnderEdit }
)
}
label.removeAttribute('contenteditable')
label.classList.remove('bg-light')
}
}
}
fileExplorer.prototype.updatePath = function (path) {
this.files.resolveDirectory(path, (error, fileTree) => {
if (error) console.error(error)
if (!fileTree) return
var newTree = normalize(path, fileTree)
this.treeView.updateNodeFromJSON(path, newTree, true)
})
}
fileExplorer.prototype.hide = function () {
if (this.container) this.container.style.display = 'none'
}
fileExplorer.prototype.show = function () {
if (this.container) this.container.style.display = 'block'
}
fileExplorer.prototype.init = function () {
this.container = yo`<div></div>`
return this.container
}
fileExplorer.prototype.publishToGist = function () {
modalDialogCustom.confirm(
'Create a public gist',
'Are you sure you want to publish all your files in browser directory anonymously as a public gist on github.com? Note: this will not include directories.',
() => { this.toGist() }
)
}
fileExplorer.prototype.uploadFile = function (event) {
// TODO The file explorer is merely a view on the current state of
// the files module. Please ask the user here if they want to overwrite
// a file and then just use `files.add`. The file explorer will
// pick that up via the 'fileAdded' event from the files module.
const self = this
;[...event.target.files].forEach((file) => {
const files = this.files
function loadFile () {
var fileReader = new FileReader()
fileReader.onload = async function (event) {
if (helper.checkSpecialChars(file.name)) {
modalDialogCustom.alert('Special characters are not allowed')
return
}
var success = await files.set(name, event.target.result)
if (!success) {
modalDialogCustom.alert('Failed to create file ' + name)
} else {
self.events.trigger('focus', [name])
}
}
fileReader.readAsText(file)
}
var name = files.type + '/' + file.name
files.exists(name, (error, exist) => {
if (error) console.log(error)
if (!exist) {
loadFile()
} else {
modalDialogCustom.confirm('Confirm overwrite', `The file ${name} already exists! Would you like to overwrite it?`, () => { loadFile() })
}
})
})
}
fileExplorer.prototype.toGist = function (id) {
const proccedResult = function (error, data) {
if (error) {
modalDialogCustom.alert('Failed to manage gist: ' + error)
console.log('Failed to manage gist: ' + error)
} else {
if (data.html_url) {
modalDialogCustom.confirm('Gist is ready', `The gist is at ${data.html_url}. Would you like to open it in a new window?`, () => {
window.open(data.html_url, '_blank')
})
} else {
modalDialogCustom.alert(data.message + ' ' + data.documentation_url + ' ' + JSON.stringify(data.errors, null, '\t'))
}
}
}
/**
* This function is to get the original content of given gist
* @params id is the gist id to fetch
*/
async function getOriginalFiles (id) {
if (!id) {
return []
}
const url = `https://api.github.com/gists/${id}`
const res = await fetch(url)
const data = await res.json()
return data.files || []
}
// If 'id' is not defined, it is not a gist update but a creation so we have to take the files from the browser explorer.
const folder = id ? '/gists/' + id : '/'
this.packageFiles(this.files, folder, (error, packaged) => {
if (error) {
console.log(error)
modalDialogCustom.alert('Failed to create gist: ' + error.message)
} else {
// check for token
var tokenAccess = this._deps.config.get('settings/gist-access-token')
if (!tokenAccess) {
modalDialogCustom.alert(
'Remix requires an access token (which includes gists creation permission). Please go to the settings tab to create one.'
)
} else {
const description = 'Created using remix-ide: Realtime Ethereum Contract Compiler and Runtime. \n Load this file by pasting this gists URL or ID at https://remix.ethereum.org/#version=' +
queryParams.get().version + '&optimize=' + queryParams.get().optimize + '&runs=' + queryParams.get().runs + '&gist='
const gists = new Gists({ token: tokenAccess })
if (id) {
const originalFileList = getOriginalFiles(id)
// Telling the GIST API to remove files
const updatedFileList = Object.keys(packaged)
const allItems = Object.keys(originalFileList)
.filter(fileName => updatedFileList.indexOf(fileName) === -1)
.reduce((acc, deleteFileName) => ({
...acc,
[deleteFileName]: null
}), originalFileList)
// adding new files
updatedFileList.forEach((file) => {
const _items = file.split('/')
const _fileName = _items[_items.length - 1]
allItems[_fileName] = packaged[file]
})
tooltip('Saving gist (' + id + ') ...')
gists.edit({
description: description,
public: true,
files: allItems,
id: id
}, (error, result) => {
proccedResult(error, result)
if (!error) {
for (const key in allItems) {
if (allItems[key] === null) delete allItems[key]
}
}
})
} else {
// id is not existing, need to create a new gist
tooltip('Creating a new gist ...')
gists.create({
description: description,
public: true,
files: packaged
}, (error, result) => {
proccedResult(error, result)
})
}
}
}
})
}
// return all the files, except the temporary/readonly ones..
fileExplorer.prototype.packageFiles = function (filesProvider, directory, callback) {
const ret = {}
filesProvider.resolveDirectory(directory, (error, files) => {
if (error) callback(error)
else {
async.eachSeries(Object.keys(files), (path, cb) => {
if (filesProvider.isDirectory(path)) {
cb()
} else {
filesProvider.get(path, (error, content) => {
if (error) return cb(error)
if (/^\s+$/.test(content) || !content.length) {
content = '// this line is added to create a gist. Empty file is not allowed.'
}
ret[path] = { content }
cb()
})
}
}, (error) => {
callback(error, ret)
})
}
})
}
fileExplorer.prototype.createNewFile = function (parentFolder = '/') {
const self = this
modalDialogCustom.prompt('Create new file', 'File Name (e.g Untitled.sol)', 'Untitled.sol', (input) => {
if (!input) input = 'New file'
helper.createNonClashingName(parentFolder + '/' + input, self.files, async (error, newName) => {
if (error) return tooltip('Failed to create file ' + newName + ' ' + error)
const fileManager = self._deps.fileManager
const createFile = await fileManager.writeFile(newName, '')
if (!createFile) {
tooltip('Failed to create file ' + newName)
} else {
await fileManager.open(newName)
if (newName.includes('_test.sol')) {
self.events.trigger('newTestFileCreated', [newName])
}
}
})
}, null, true)
}
fileExplorer.prototype.createNewFolder = function (parentFolder) {
const self = this
modalDialogCustom.prompt('Create new folder', '', 'New folder', (input) => {
if (!input) {
return tooltip('Failed to create folder. The name can not be empty')
}
const currentPath = !parentFolder ? self._deps.fileManager.currentPath() : parentFolder
let newName = currentPath ? currentPath + '/' + input : self.files.type + '/' + input
newName = newName + '/'
self.files.exists(newName, (error, exist) => {
if (error) return tooltip('Unexpected error while creating folder: ' + error)
if (!exist) {
self.files.set(newName, '')
} else {
tooltip('Folder already exists.', () => {})
}
})
}, null, true)
}
fileExplorer.prototype.renderMenuItems = function () {
let items = ''
if (this.menuItems) {
items = this.menuItems.map(({ action, title, icon }) => {
if (action === 'uploadFile') {
return yo`
<label
id=${action}
data-id="fileExplorerUploadFile${action}"
class="${icon} mb-0 ${css.newFile}"
title="${title}"
>
<input id="fileUpload" data-id="fileExplorerFileUpload" type="file" onchange=${(event) => {
event.stopPropagation()
this.uploadFile(event)
}} multiple />
</label>
`
} else {
return yo`
<span
id=${action}
data-id="fileExplorerNewFile${action}"
onclick=${(event) => { event.stopPropagation(); this[action]() }}
class="newFile ${icon} ${css.newFile}"
title=${title}
>
</span>
`
}
})
}
return yo`<span class=" ${css.menu}">${items}</span>`
}
fileExplorer.prototype.ensureRoot = function (cb) {
cb = cb || (() => {})
var self = this
if (self.element) return cb()
const root = {}
root[this.files.type] = {}
var element = self.treeView.render(root, false)
element.classList.add(css.fileexplorer)
element.events = self.events
element.api = self.api
self.container.appendChild(element)
self.element = element
if (cb) cb()
self.treeView.expand(self.files.type)
}
function normalize (path, filesList) {
var prefix = path.split('/')[0]
var newList = {}
Object.keys(filesList).forEach(key => {
newList[prefix + '/' + key] = filesList[key].isDirectory ? {} : { '/content': true }
})
return newList
}
module.exports = fileExplorer

@ -136,23 +136,25 @@ export class RemixdHandle extends WebsocketPlugin {
} }
function remixdDialog () { function remixdDialog () {
const commandText = 'remixd -s path-to-the-shared-folder --remix-ide remix-ide-instance-URL' const commandText = 'remixd -s <path-to-the-shared-folder> -u <remix-ide-instance-URL>'
return yo` return yo`
<div class=${css.dialog}> <div class=${css.dialog}>
<div class=${css.dialogParagraph}> <div class=${css.dialogParagraph}>
Access your local file system from Remix IDE using <a target="_blank" href="https://www.npmjs.com/package/@remix-project/remixd">Remixd NPM package</a>.<br/><br/> Access your local file system from Remix IDE using <a target="_blank" href="https://www.npmjs.com/package/@remix-project/remixd">Remixd NPM package</a>.<br/><br/>
Remixd needs to be running in the background to load the files in localhost workspace. For more info, please check the <a target="_blank" href="https://remix-ide.readthedocs.io/en/latest/remixd.html">Remixd tutorial</a>. Remixd needs to be running in the background to load the files in localhost workspace. For more info, please check the <a target="_blank" href="https://remix-ide.readthedocs.io/en/latest/remixd.html">Remixd tutorial</a>.
</div> </div>
<div class=${css.dialogParagraph}>If you are just looking for the remixd command, here it is: <div class=${css.dialogParagraph}>
If you are just looking for the remixd command, here it is:
<br><br><b>${commandText}</b> <br><br><b>${commandText}</b>
<span class="">${copyToClipboard(() => commandText)}</span> <span class="">${copyToClipboard(() => commandText)}</span>
</div> </div>
<div class=${css.dialogParagraph}>When connected, a session will be started between <em>${window.location.origin}</em> and your local file system at <i>ws://127.0.0.1:65520</i> <div class=${css.dialogParagraph}>
and the shared folder will be in the File Explorers workspace named "localhost". When connected, a session will be started between <em>${window.location.origin}</em> and your local file system at <i>ws://127.0.0.1:65520</i>.
<br/>Note, if the shared folder is a Hardhat project, an additional Hardhat websocket plugin will be listening at <i>ws://127.0.0.1:65522</i> The shared folder will be in the "File Explorers" workspace named "localhost".
<br/>Read more about other <a target="_blank" href="https://remix-ide.readthedocs.io/en/latest/remixd.html#ports-usage">Remixd ports usage</a>
</div> </div>
<div class=${css.dialogParagraph}>Please make sure your system is secured enough and ports 65520, 65522 are not opened nor forwarded. <div class=${css.dialogParagraph}>
This feature is still in Alpha, so we recommend to keep a copy of the shared folder. This feature is still in Alpha. We recommend to keep a backup of the shared folder.
</div> </div>
<div class=${css.dialogParagraph}> <div class=${css.dialogParagraph}>
<h6 class="text-danger"> <h6 class="text-danger">

@ -24,7 +24,6 @@ const profile = {
documentation: 'https://remix-ide.readthedocs.io/en/latest/solidity_editor.html', documentation: 'https://remix-ide.readthedocs.io/en/latest/solidity_editor.html',
version: packageJson.version, version: packageJson.version,
methods: ['getCompilationResult', 'compile', 'compileWithParameters', 'setCompilerConfig', 'compileFile'] methods: ['getCompilationResult', 'compile', 'compileWithParameters', 'setCompilerConfig', 'compileFile']
} }
// EditorApi: // EditorApi:

@ -1,135 +0,0 @@
import * as packageJson from '../../../../../../package.json'
import { Plugin } from '@remixproject/engine'
const EventEmitter = require('events')
var Compiler = require('@remix-project/remix-solidity').Compiler
const profile = {
name: 'solidity-logic',
displayName: 'Solidity compiler logic',
description: 'Compile solidity contracts - Logic',
methods: ['getCompilerState'],
version: packageJson.version
}
class CompileTab extends Plugin {
constructor (queryParams, fileManager, editor, config, fileProvider, contentImport) {
super(profile)
this.event = new EventEmitter()
this.queryParams = queryParams
this.compilerImport = contentImport
this.compiler = new Compiler((url, cb) => this.compilerImport.resolveAndSave(url).then((result) => cb(null, result)).catch((error) => cb(error.message)))
this.fileManager = fileManager
this.editor = editor
this.config = config
this.fileProvider = fileProvider
}
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 || 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)
}
getCompilerState () {
return this.compiler.state
}
/**
* 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() }, 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)
}
}
}
module.exports = CompileTab

@ -1,580 +0,0 @@
import toaster from '../../ui/tooltip'
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')
const semver = require('semver')
const modalDialogCustom = require('../../ui/modal-dialog-custom')
const css = require('../styles/compile-tab-styles')
const _paq = window._paq = window._paq || []
class CompilerContainer {
constructor (compileTabLogic, editor, config, queryParams) {
this._view = {}
this.compileTabLogic = compileTabLogic
this.editor = editor
this.config = config
this.queryParams = queryParams
this.hhCompilation = false
this.data = {
hideWarnings: config.get('hideWarnings') || false,
autoCompile: config.get('autoCompile'),
compileTimeout: null,
timeout: 300,
allversions: null,
selectedVersion: null,
defaultVersion: 'soljson-v0.8.4+commit.c7e474f2.js' // this default version is defined: in makeMockCompiler (for browser test)
}
}
/**
* Update the compilation button with the name of the current file
*/
set currentFile (name = '') {
if (name && name !== '') {
this._setCompilerVersionFromPragma(name)
}
if (!this._view.compilationButton) return
const button = this.compilationButton(name.split('/').pop())
this._disableCompileBtn(!name || (name && !this.isSolFileSelected(name)))
yo.update(this._view.compilationButton, button)
}
isSolFileSelected (currentFile = '') {
if (!currentFile) currentFile = this.config.get('currentFile')
if (!currentFile) return false
const extention = currentFile.substr(currentFile.length - 3, currentFile.length)
return extention.toLowerCase() === 'sol' || extention.toLowerCase() === 'yul'
}
deactivate () {
// deactivate editor listeners
this.editor.event.unregister('contentChanged')
this.editor.event.unregister('sessionSwitched')
}
activate () {
this.currentFile = this.config.get('currentFile')
this.listenToEvents()
}
listenToEvents () {
this.editor.event.register('sessionSwitched', () => {
if (!this._view.compileIcon) return
this.scheduleCompilation()
})
this.compileTabLogic.event.on('startingCompilation', () => {
if (!this._view.compileIcon) return
this._view.compileIcon.setAttribute('title', 'compiling...')
this._view.compileIcon.classList.remove(`${css.bouncingIcon}`)
this._view.compileIcon.classList.add(`${css.spinningIcon}`)
})
this.compileTabLogic.compiler.event.register('compilationDuration', (speed) => {
if (!this._view.warnCompilationSlow) return
if (speed > 1000) {
const msg = `Last compilation took ${speed}ms. We suggest to turn off autocompilation.`
this._view.warnCompilationSlow.setAttribute('title', msg)
this._view.warnCompilationSlow.style.visibility = 'visible'
} else {
this._view.warnCompilationSlow.style.visibility = 'hidden'
}
})
this.editor.event.register('contentChanged', () => {
if (!this._view.compileIcon) return
this.scheduleCompilation()
this._view.compileIcon.classList.add(`${css.bouncingIcon}`) // @TODO: compileView tab
})
this.compileTabLogic.compiler.event.register('loadingCompiler', () => {
if (!this._view.compileIcon) return
this._disableCompileBtn(true)
this._view.compileIcon.setAttribute('title', 'compiler is loading, please wait a few moments.')
this._view.compileIcon.classList.add(`${css.spinningIcon}`)
this._view.warnCompilationSlow.style.visibility = 'hidden'
this._updateLanguageSelector()
})
this.compileTabLogic.compiler.event.register('compilerLoaded', () => {
if (!this._view.compileIcon) return
this._disableCompileBtn(false)
this._view.compileIcon.setAttribute('title', '')
this._view.compileIcon.classList.remove(`${css.spinningIcon}`)
if (this.data.autoCompile) this.compileIfAutoCompileOn()
})
this.compileTabLogic.compiler.event.register('compilationFinished', (success, data, source) => {
if (!this._view.compileIcon) return
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_version', this._retrieveVersion()])
})
}
/**************
* SUBCOMPONENT
*/
compilationButton (name = '') {
const displayed = name || '<no file selected>'
const disabled = name && this.isSolFileSelected() ? '' : 'disabled'
return yo`
<button id="compileBtn" data-id="compilerContainerCompileBtn" class="btn btn-primary btn-block ${disabled} mt-3" title="Compile" onclick="${this.compile.bind(this)}">
<span>${this._view.compileIcon} Compile ${displayed}</span>
</button>
`
}
_disableCompileBtn (shouldDisable) {
const btn = document.getElementById('compileBtn')
if (!btn) return
if (shouldDisable) {
btn.classList.add('disabled')
} else if (this.isSolFileSelected()) {
btn.classList.remove('disabled')
}
}
// Load solc compiler version according to pragma in contract file
_setCompilerVersionFromPragma (filename) {
if (!this.data.allversions) return
this.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 = this.data.allversions.filter(obj => !obj.prerelease).map(obj => obj.version)
const allVersions = this.data.allversions.map(obj => this._retrieveVersion(obj.version))
const currentCompilerName = this._retrieveVersion(this._view.versionSelector.selectedOptions[0].label)
// contains only numbers part, for example '0.4.22'
const pureVersion = this._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 = this.data.allversions.filter(obj => !obj.prerelease && obj.version === compilerToLoad)[0].path
if (this.data.selectedVersion !== compilerPath) {
this.data.selectedVersion = compilerPath
this._updateVersionSelector()
}
}
}
})
}
_retrieveVersion (version) {
if (!version) version = this._view.versionSelector.value
if (version === 'builtin') version = this.data.defaultVersion
return semver.coerce(version) ? semver.coerce(version).version : ''
}
render () {
this.compileTabLogic.compiler.event.register('compilerLoaded', (version) => this.setVersionText(version))
this.fetchAllVersion((allversions, selectedVersion, isURL) => {
this.data.allversions = allversions
if (isURL) this._updateVersionSelector(selectedVersion)
else {
this.data.selectedVersion = selectedVersion
if (this._view.versionSelector) this._updateVersionSelector()
}
})
this.hardhatCompilation = yo`<div class="mt-2 ${css.compilerConfig} custom-control custom-checkbox" style="display:none">
<input class="${css.autocompile} custom-control-input" onchange=${(e) => this.updatehhCompilation(e)} id="enableHardhat" type="checkbox" title="Enable Hardhat Compilation">
<label class="form-check-label custom-control-label" for="enableHardhat">Enable Hardhat Compilation</label>
</div>`
this._view.warnCompilationSlow = yo`<i title="Compilation Slow" style="visibility:hidden" class="${css.warnCompilationSlow} fas fa-exclamation-triangle" aria-hidden="true"></i>`
this._view.compileIcon = yo`<i class="fas fa-sync ${css.icon}" aria-hidden="true"></i>`
this._view.autoCompile = yo`<input class="${css.autocompile} custom-control-input" onchange=${() => this.updateAutoCompile()} data-id="compilerContainerAutoCompile" id="autoCompile" type="checkbox" title="Auto compile">`
this._view.hideWarningsBox = yo`<input class="${css.autocompile} custom-control-input" onchange=${() => this.hideWarnings()} id="hideWarningsBox" type="checkbox" title="Hide warnings">`
if (this.data.autoCompile) this._view.autoCompile.setAttribute('checked', '')
if (this.data.hideWarnings) this._view.hideWarningsBox.setAttribute('checked', '')
this._view.optimize = yo`<input onchange=${() => this.onchangeOptimize()} class="custom-control-input" id="optimize" type="checkbox">`
if (this.compileTabLogic.optimize) this._view.optimize.setAttribute('checked', '')
this._view.runs = yo`<input
min="1"
class="custom-select ml-2 ${css.runs}"
id="runs"
placeholder="200"
value="200"
type="number"
title="Estimated number of times each opcode of the deployed code will be executed across the life-time of the contract."
onchange=${() => this.onchangeRuns()}
>`
if (this.compileTabLogic.optimize) {
this._view.runs.removeAttribute('disabled')
this._view.runs.value = this.compileTabLogic.runs
} else {
this._view.runs.setAttribute('disabled', '')
}
this._view.versionSelector = yo`
<select onchange="${() => this.onchangeLoadVersion()}" class="custom-select" id="versionSelector" disabled>
<option disabled selected>${this.data.defaultVersion}</option>
<option disabled>builtin</option>
</select>`
this._view.languageSelector = yo`
<select onchange="${() => this.onchangeLanguage()}" class="custom-select" id="compilierLanguageSelector" title="Available since v0.5.7">
<option>Solidity</option>
<option>Yul</option>
</select>`
this._view.version = yo`<span id="version"></span>`
this._view.evmVersionSelector = yo`
<select onchange="${() => this.onchangeEvmVersion()}" class="custom-select" id="evmVersionSelector">
<option value="default" selected="selected">compiler default</option>
<option>berlin</option>
<option>muirGlacier</option>
<option>istanbul</option>
<option>petersburg</option>
<option>constantinople</option>
<option>byzantium</option>
<option>spuriousDragon</option>
<option>tangerineWhistle</option>
<option>homestead</option>
</select>`
if (this.compileTabLogic.evmVersion) {
const s = this._view.evmVersionSelector
let i
for (i = 0; i < s.options.length; i++) {
if (s.options[i].value === this.compileTabLogic.evmVersion) {
break
}
}
if (i === s.options.length) { // invalid evmVersion from queryParams
s.selectedIndex = 0 // compiler default
this.onchangeEvmVersion()
} else {
s.selectedIndex = i
this.onchangeEvmVersion()
}
}
this._view.compilationButton = this.compilationButton()
this._view.includeNightlies = yo`
<input class="mr-2 custom-control-input" id="nightlies" type="checkbox" onchange=${() => this._updateVersionSelector()}>
`
this._view.compileContainer = yo`
<section>
<!-- Select Compiler Version -->
<article>
<header class="${css.compilerSection} border-bottom">
<div class="mb-2">
<label class="${css.compilerLabel} form-check-label" for="versionSelector">
Compiler
<button class="far fa-plus-square border-0 p-0 mx-2 btn-sm" onclick="${(e) => this.promtCompiler(e)}" title="Add a custom compiler with URL"></button>
</label>
${this._view.versionSelector}
</div>
<div class="mb-2 ${css.nightlyBuilds} custom-control custom-checkbox">
${this._view.includeNightlies}
<label for="nightlies" class="form-check-label custom-control-label">Include nightly builds</label>
</div>
<div class="mb-2">
<label class="${css.compilerLabel} form-check-label" for="compilierLanguageSelector">Language</label>
${this._view.languageSelector}
</div>
<div class="mb-2">
<label class="${css.compilerLabel} form-check-label" for="evmVersionSelector">EVM Version</label>
${this._view.evmVersionSelector}
</div>
<div class="mt-3">
<p class="mt-2 ${css.compilerLabel}">Compiler Configuration</p>
<div class="mt-2 ${css.compilerConfig} custom-control custom-checkbox">
${this._view.autoCompile}
<label class="form-check-label custom-control-label" for="autoCompile">Auto compile</label>
</div>
<div class="mt-2 ${css.compilerConfig} custom-control custom-checkbox">
<div class="justify-content-between align-items-center d-flex">
${this._view.optimize}
<label class="form-check-label custom-control-label" for="optimize">Enable optimization</label>
${this._view.runs}
</div>
</div>
<div class="mt-2 ${css.compilerConfig} custom-control custom-checkbox">
${this._view.hideWarningsBox}
<label class="form-check-label custom-control-label" for="hideWarningsBox">Hide warnings</label>
</div>
</div>
${this.hardhatCompilation}
${this._view.compilationButton}
</header>
</article>
<!-- Config -->
</section>`
return this._view.compileContainer
}
promtCompiler () {
modalDialogCustom.prompt(
'Add a custom compiler',
'URL',
'',
(url) => this.addCustomCompiler(url)
)
}
addCustomCompiler (url) {
this.data.selectedVersion = this._view.versionSelector.value
this._updateVersionSelector(url)
}
updateAutoCompile (event) {
this.config.set('autoCompile', this._view.autoCompile.checked)
}
updatehhCompilation (event) {
this.hhCompilation = event.target.checked
}
compile (event) {
const currentFile = this.config.get('currentFile')
if (!this.isSolFileSelected()) return
this._setCompilerVersionFromPragma(currentFile)
this.compileTabLogic.runCompiler(this.hhCompilation)
}
compileIfAutoCompileOn () {
if (this.config.get('autoCompile')) {
this.compile()
}
}
hideWarnings (event) {
this.config.set('hideWarnings', this._view.hideWarningsBox.checked)
this.compileIfAutoCompileOn()
}
/*
The following functions are handlers for internal events.
*/
onchangeOptimize () {
this.compileTabLogic.setOptimize(!!this._view.optimize.checked)
if (this.compileTabLogic.optimize) {
this._view.runs.removeAttribute('disabled')
this.compileTabLogic.setRuns(parseInt(this._view.runs.value))
} else {
this.compileTabLogic.setRuns(200)
this._view.runs.setAttribute('disabled', '')
}
this.compileIfAutoCompileOn()
}
onchangeRuns () {
this.compileTabLogic.setRuns(parseInt(this._view.runs.value))
this.compileIfAutoCompileOn()
}
onchangeLanguage () {
this.compileTabLogic.setLanguage(this._view.languageSelector.value)
this.compileIfAutoCompileOn()
}
onchangeEvmVersion () {
const s = this._view.evmVersionSelector
let v = s.value
if (v === 'default') {
v = null
}
this.compileTabLogic.setEvmVersion(v)
for (let i = 0; i < s.options.length; i++) {
if (i === s.selectedIndex) {
s.options[s.selectedIndex].setAttribute('selected', 'selected')
} else {
s.options[i].removeAttribute('selected')
}
}
this.compileIfAutoCompileOn()
}
onchangeLoadVersion () {
this.data.selectedVersion = this._view.versionSelector.value
this._updateVersionSelector()
this._updateLanguageSelector()
}
/*
The following functions map with the above event handlers.
They are an external API for modifying the compiler configuration.
*/
setConfiguration (settings) {
this.setLanguage(settings.language)
this.setEvmVersion(settings.evmVersion)
this.setOptimize(settings.optimize)
this.setRuns(settings.runs)
this.setVersion(settings.version)
}
setOptimize (enabled) {
this._view.optimize.checked = enabled
this.onchangeOptimize()
}
setRuns (value) {
if (value) {
this._view.runs.value = value
this.onchangeRuns()
}
}
setLanguage (lang) {
this._view.languageSelector.value = lang
this.onchangeLanguage()
}
setEvmVersion (version) {
this._view.evmVersionSelector.value = version || 'default'
this.onchangeEvmVersion()
}
setVersion (version) {
this._view.versionSelector.value = `soljson-v${version}.js`
this.onchangeLoadVersion()
}
_shouldBeAdded (version) {
return !version.includes('nightly') ||
(version.includes('nightly') && this._view.includeNightlies.checked)
}
_updateVersionSelector (customUrl = '') {
// update selectedversion if previous one got filtered out
if (!this.data.selectedVersion || !this._shouldBeAdded(this.data.selectedVersion)) {
this.data.selectedVersion = this.data.defaultVersion
}
this._view.versionSelector.innerHTML = ''
this._view.versionSelector.removeAttribute('disabled')
this.queryParams.update({ version: this.data.selectedVersion })
let url
if (customUrl !== '') {
this.data.selectedVersion = customUrl
this._view.versionSelector.appendChild(yo`<option value="${customUrl}" selected>custom</option>`)
url = customUrl
this.queryParams.update({ version: this.data.selectedVersion })
} else if (this.data.selectedVersion === 'builtin') {
let 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 (this.data.selectedVersion.indexOf('soljson') !== 0 || helper.checkSpecialChars(this.data.selectedVersion)) {
return console.log('loading ' + this.data.selectedVersion + ' not allowed')
}
url = `${urlFromVersion(this.data.selectedVersion)}`
}
this.data.allversions.forEach(build => {
const option = build.path === this.data.selectedVersion
? yo`<option value="${build.path}" selected>${build.longVersion}</option>`
: yo`<option value="${build.path}">${build.longVersion}</option>`
if (this._shouldBeAdded(option.innerText)) {
this._view.versionSelector.appendChild(option)
}
})
if (this.data.selectedVersion !== 'builtin' && semver.lt(this._retrieveVersion(), 'v0.4.12+commit.194ff033.js')) {
toaster(yo`
<div>
<b>Old compiler usage detected.</b>
<p>You are using a compiler older than v0.4.12.</p>
<p>Some functionality may not work.</p>
</div>`
)
}
// 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 (canUseWorker(this._retrieveVersion())) {
this.compileTabLogic.compiler.loadVersion(true, url)
this.setVersionText('(loading using worker)')
} else {
this.compileTabLogic.compiler.loadVersion(false, url)
this.setVersionText('(loading)')
}
}
_updateLanguageSelector () {
// This is the first version when Yul is available
if (!semver.valid(this._retrieveVersion()) || semver.lt(this._retrieveVersion(), 'v0.5.7+commit.6da8b019.js')) {
this._view.languageSelector.setAttribute('disabled', '')
this._view.languageSelector.value = 'Solidity'
this.compileTabLogic.setLanguage('Solidity')
} else {
this._view.languageSelector.removeAttribute('disabled')
}
}
setVersionText (text) {
if (this._view.version) this._view.version.innerText = text
}
// fetching both normal and wasm builds and creating a [version, baseUrl] map
async fetchAllVersion (callback) {
let selectedVersion, allVersionsWasm, isURL
let allVersions = [{ path: 'builtin', longVersion: 'Stable local version - 0.8.4' }]
// fetch normal builds
const binRes = await promisedMiniXhr(`${baseURLBin}/list.json`)
// fetch wasm builds
const wasmRes = 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 = this.data.defaultVersion
if (this.queryParams.get().version) selectedVersion = this.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) {
addTooltip('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)
}
scheduleCompilation () {
if (!this.config.get('autoCompile')) return
if (this.data.compileTimeout) window.clearTimeout(this.data.compileTimeout)
this.data.compileTimeout = window.setTimeout(() => this.compileIfAutoCompileOn(), this.data.timeout)
}
}
module.exports = CompilerContainer

@ -1,122 +0,0 @@
'use strict'
var solcTranslate = require('solc/translate')
var remixLib = require('@remix-project/remix-lib')
var txHelper = remixLib.execution.txHelper
module.exports = (contractName, contract, compiledSource) => {
return getDetails(contractName, contract, compiledSource)
}
var getDetails = function (contractName, contract, source) {
var detail = {}
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 (var 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
}
var 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]
}
}
var gethDeploy = function (contractName, jsonInterface, bytecode) {
var code = ''
var 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
}
var formatGasEstimates = function (data) {
if (!data) return {}
if (data.creation === undefined && data.external === undefined && data.internal === undefined) return {}
var gasToText = function (g) {
return g === null ? 'unknown' : g
}
var ret = {}
var 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
}

@ -65,7 +65,7 @@ export default class HardhatProvider extends Plugin {
if (error) { if (error) {
this.blocked = true this.blocked = true
modalDialogCustom.alert('Hardhat Provider', `Error while connecting to the hardhat provider: ${error.message}`) modalDialogCustom.alert('Hardhat Provider', `Error while connecting to the hardhat provider: ${error.message}`)
await this.call('udapp', 'setEnvironmentMode', { context: 'vm', fork: 'berlin' }) await this.call('udapp', 'setEnvironmentMode', { context: 'vm', fork: 'london' })
this.provider = null this.provider = null
setTimeout(_ => { this.blocked = false }, 1000) // we wait 1 second for letting remix to switch to vm setTimeout(_ => { this.blocked = false }, 1000) // we wait 1 second for letting remix to switch to vm
return reject(error) return reject(error)

@ -361,7 +361,7 @@ class ContractDropdownUI {
return continueTxExecution(null) return continueTxExecution(null)
} }
const amount = this.blockchain.fromWei(tx.value, true, 'ether') const amount = this.blockchain.fromWei(tx.value, true, 'ether')
const content = confirmDialog(tx, amount, gasEstimation, null, this.blockchain.determineGasFees(tx), this.blockchain.determineGasPrice.bind(this.blockchain)) const content = confirmDialog(tx, network, amount, gasEstimation, this.blockchain.determineGasFees(tx), this.blockchain.determineGasPrice.bind(this.blockchain))
modalDialog('Confirm transaction', content, modalDialog('Confirm transaction', content,
{ {
@ -370,10 +370,9 @@ class ContractDropdownUI {
this.blockchain.config.setUnpersistedProperty('doNotShowTransactionConfirmationAgain', content.querySelector('input#confirmsetting').checked) this.blockchain.config.setUnpersistedProperty('doNotShowTransactionConfirmationAgain', content.querySelector('input#confirmsetting').checked)
// TODO: check if this is check is still valid given the refactor // TODO: check if this is check is still valid given the refactor
if (!content.gasPriceStatus) { if (!content.gasPriceStatus) {
cancelCb('Given gas price is not correct') cancelCb('Given transaction fee is not correct')
} else { } else {
var gasPrice = this.blockchain.toWei(content.querySelector('#gasprice').value, 'gwei') continueTxExecution(content.txFee)
continueTxExecution(gasPrice)
} }
} }
}, { }, {

@ -104,7 +104,7 @@ class RecorderUI extends Plugin {
return continueTxExecution(null) return continueTxExecution(null)
} }
const amount = this.blockchain.fromWei(tx.value, true, 'ether') const amount = this.blockchain.fromWei(tx.value, true, 'ether')
const content = confirmDialog(tx, amount, gasEstimation, null, this.blockchain.determineGasFees(tx), this.blockchain.determineGasPrice.bind(this.blockchain)) const content = confirmDialog(tx, network, amount, gasEstimation, this.blockchain.determineGasFees(tx), this.blockchain.determineGasPrice.bind(this.blockchain))
modalDialog('Confirm transaction', content, modalDialog('Confirm transaction', content,
{ {
@ -113,10 +113,9 @@ class RecorderUI extends Plugin {
this.config.setUnpersistedProperty('doNotShowTransactionConfirmationAgain', content.querySelector('input#confirmsetting').checked) this.config.setUnpersistedProperty('doNotShowTransactionConfirmationAgain', content.querySelector('input#confirmsetting').checked)
// TODO: check if this is check is still valid given the refactor // TODO: check if this is check is still valid given the refactor
if (!content.gasPriceStatus) { if (!content.gasPriceStatus) {
cancelCb('Given gas price is not correct') cancelCb('Given transaction fee is not correct')
} else { } else {
var gasPrice = this.blockchain.toWei(content.querySelector('#gasprice').value, 'gwei') continueTxExecution(content.txFee)
continueTxExecution(gasPrice)
} }
} }
}, { }, {

@ -98,14 +98,14 @@ class SettingsUI {
</label> </label>
<div class="${css.environment}"> <div class="${css.environment}">
<select id="selectExEnvOptions" data-id="settingsSelectEnvOptions" class="form-control ${css.select} custom-select"> <select id="selectExEnvOptions" data-id="settingsSelectEnvOptions" class="form-control ${css.select} custom-select">
<option id="vm-mode-berlin" data-id="settingsVMBerlinMode"
title="Execution environment does not connect to any node, everything is local and in memory only."
value="vm-berlin" name="executionContext" fork="berlin" > JavaScript VM (Berlin)
</option>
<option id="vm-mode-london" data-id="settingsVMLondonMode" <option id="vm-mode-london" data-id="settingsVMLondonMode"
title="Execution environment does not connect to any node, everything is local and in memory only." title="Execution environment does not connect to any node, everything is local and in memory only."
value="vm-london" name="executionContext" fork="london"> JavaScript VM (London) value="vm-london" name="executionContext" fork="london"> JavaScript VM (London)
</option> </option>
<option id="vm-mode-berlin" data-id="settingsVMBerlinMode"
title="Execution environment does not connect to any node, everything is local and in memory only."
value="vm-berlin" name="executionContext" fork="berlin" > JavaScript VM (Berlin)
</option>
<option id="injected-mode" data-id="settingsInjectedMode" <option id="injected-mode" data-id="settingsInjectedMode"
title="Execution environment has been provided by Metamask or similar provider." title="Execution environment has been provided by Metamask or similar provider."
value="injected" name="executionContext"> Injected Web3 value="injected" name="executionContext"> Injected Web3

@ -1,6 +1,7 @@
var yo = require('yo-yo') var yo = require('yo-yo')
var csjs = require('csjs-inject') var csjs = require('csjs-inject')
const copyToClipboard = require('./copy-to-clipboard') const copyToClipboard = require('./copy-to-clipboard')
const Web3 = require('web3')
var css = csjs` var css = csjs`
.txInfoBox { .txInfoBox {
@ -14,20 +15,47 @@ var css = csjs`
} }
` `
// TODO: self is not actually used and can be removed function confirmDialog (tx, network, amount, gasEstimation, newGasPriceCb, initialParamsCb) {
function confirmDialog (tx, amount, gasEstimation, self, newGasPriceCb, initialParamsCb) { const onGasPriceChange = function () {
var onGasPriceChange = function () {
var gasPrice = el.querySelector('#gasprice').value var gasPrice = el.querySelector('#gasprice').value
newGasPriceCb(gasPrice, (txFeeText, priceStatus) => { newGasPriceCb(gasPrice, (txFeeText, priceStatus) => {
el.querySelector('#txfee').innerHTML = txFeeText el.querySelector('#txfee').innerHTML = txFeeText
el.gasPriceStatus = priceStatus el.gasPriceStatus = priceStatus
el.txFee = { gasPrice }
}) })
} }
var el = yo` const onMaxFeeChange = function () {
var maxFee = el.querySelector('#maxfee').value
var confirmBtn = document.querySelector('#modal-footer-ok')
var maxPriorityFee = el.querySelector('#maxpriorityfee').value
if (parseInt(network.lastBlock.baseFeePerGas, 16) > Web3.utils.toWei(maxFee, 'Gwei')) {
el.querySelector('#txfee').innerHTML = 'Transaction is invalid. Max fee should not be less than Base fee'
el.gasPriceStatus = false
confirmBtn.hidden = true
return
} else {
el.gasPriceStatus = true
confirmBtn.hidden = false
}
newGasPriceCb(maxFee, (txFeeText, priceStatus) => {
el.querySelector('#txfee').innerHTML = txFeeText
if (priceStatus) {
confirmBtn.hidden = false
} else {
confirmBtn.hidden = true
}
el.gasPriceStatus = priceStatus
el.txFee = { maxFee, maxPriorityFee, baseFeePerGas: network.lastBlock.baseFeePerGas }
})
}
const el = yo`
<div> <div>
<div>You are about to create a transaction on the Main Network. Confirm the details to send the info to your provider. <div class="text-dark">You are about to create a transaction on ${network.name} Network. Confirm the details to send the info to your provider.
<br>The provider for many users is MetaMask. The provider will ask you to sign the transaction before it is sent to the Main Network.</div> <br>The provider for many users is MetaMask. The provider will ask you to sign the transaction before it is sent to ${network.name} Network.
</div>
<div class="mt-3 ${css.txInfoBox}"> <div class="mt-3 ${css.txInfoBox}">
<div> <div>
<span class="text-dark mr-2">From:</span> <span class="text-dark mr-2">From:</span>
@ -37,7 +65,11 @@ function confirmDialog (tx, amount, gasEstimation, self, newGasPriceCb, initialP
<span class="text-dark mr-2">To:</span> <span class="text-dark mr-2">To:</span>
<span>${tx.to ? tx.to : '(Contract Creation)'}</span> <span>${tx.to ? tx.to : '(Contract Creation)'}</span>
</div> </div>
<div> <div class="d-flex align-items-center">
<span class="text-dark mr-2">Data:</span>
<pre class="${css.wrapword} mb-0">${tx.data && tx.data.length > 50 ? tx.data.substring(0, 49) + '...' : tx.data} ${copyToClipboard(() => { return tx.data })}</pre>
</div>
<div class="mb-3">
<span class="text-dark mr-2">Amount:</span> <span class="text-dark mr-2">Amount:</span>
<span>${amount} Ether</span> <span>${amount} Ether</span>
</div> </div>
@ -49,18 +81,32 @@ function confirmDialog (tx, amount, gasEstimation, self, newGasPriceCb, initialP
<span class="text-dark mr-2">Gas limit:</span> <span class="text-dark mr-2">Gas limit:</span>
<span>${tx.gas}</span> <span>${tx.gas}</span>
</div> </div>
<div> ${
network.lastBlock.baseFeePerGas ? yo`
<div class="align-items-center my-1" title="Represents the part of the tx fee that goes to the miner.">
<div class='d-flex'>
<span class="text-dark mr-2 text-nowrap">Max Priority fee:</span>
<input class="form-control mr-1 text-right" style='height: 1.2rem; width: 6rem;' value="0" id='maxpriorityfee' />
<span title="visit https://ethgasstation.info for current gas price info.">Gwei</span>
</div>
</div>
<div class="align-items-center my-1" title="Represents the maximum amount of fee that you will pay for this transaction. The minimun needs to be set to base fee.">
<div class='d-flex'>
<span class="text-dark mr-2 text-nowrap">Max fee (Not less than base fee ${Web3.utils.fromWei(Web3.utils.toBN(parseInt(network.lastBlock.baseFeePerGas, 16)), 'Gwei')} Gwei):</span>
<input class="form-control mr-1 text-right" style='height: 1.2rem; width: 6rem;' id='maxfee' oninput=${onMaxFeeChange} />
<span>Gwei</span>
<span class="text-dark ml-2"></span>
</div>
</div>`
: yo`<div class="d-flex align-items-center my-1">
<span class="text-dark mr-2 text-nowrap">Gas price:</span>
<input class="form-control mr-1 text-right" style='width: 40px; height: 28px;' id='gasprice' oninput=${onGasPriceChange} />
<span>Gwei (visit <a target='_blank' href='https://ethgasstation.info'>ethgasstation.info</a> for current gas price info.)</span>
</div>`
}
<div class="mb-3">
<span class="text-dark mr-2">Max transaction fee:</span> <span class="text-dark mr-2">Max transaction fee:</span>
<span id='txfee'></span> <span class="text-warning" id='txfee'></span>
</div>
<div class="d-flex align-items-center my-1">
<span class="text-dark mr-2">Gas price:</span>
<input class="form-control mr-1" style='width: 40px; height: 28px;'id='gasprice' oninput=${onGasPriceChange} />
<span>Gwei (visit <a target='_blank' href='https://ethgasstation.info'>ethgasstation.info</a> for current gas price info.)</span>
</div>
<div class="d-flex align-items-center">
<span class="text-dark mr-2 mb-3">Data:</span>
<pre class=${css.wrapword}>${tx.data && tx.data.length > 50 ? tx.data.substring(0, 49) + '...' : tx.data} ${copyToClipboard(() => { return tx.data })}</pre>
</div> </div>
</div> </div>
<div class="d-flex py-1 align-items-center custom-control custom-checkbox ${css.checkbox}"> <div class="d-flex py-1 align-items-center custom-control custom-checkbox ${css.checkbox}">
@ -74,10 +120,14 @@ function confirmDialog (tx, amount, gasEstimation, self, newGasPriceCb, initialP
if (txFeeText) { if (txFeeText) {
el.querySelector('#txfee').innerHTML = txFeeText el.querySelector('#txfee').innerHTML = txFeeText
} }
if (gasPriceValue) { if (el.querySelector('#gasprice') && gasPriceValue) {
el.querySelector('#gasprice').value = gasPriceValue el.querySelector('#gasprice').value = gasPriceValue
onGasPriceChange() onGasPriceChange()
} }
if (el.querySelector('#maxfee') && network && network.lastBlock && network.lastBlock.baseFeePerGas) {
el.querySelector('#maxfee').value = Web3.utils.fromWei(Web3.utils.toBN(parseInt(network.lastBlock.baseFeePerGas, 16)), 'Gwei')
onMaxFeeChange()
}
if (gasPriceStatus !== undefined) { if (gasPriceStatus !== undefined) {
el.gasPriceStatus = gasPriceStatus el.gasPriceStatus = gasPriceStatus
} }

@ -1,7 +1,6 @@
import * as packageJson from '../../../../../../package.json' import * as packageJson from '../../../../../../package.json'
import { ViewPlugin } from '@remixproject/engine-web' import { ViewPlugin } from '@remixproject/engine-web'
import { migrateToWorkspace } from '../../../migrateFileSystem' import { migrateToWorkspace } from '../../../migrateFileSystem'
import { CompilerImports } from '@remix-project/core-plugin'
import JSZip from 'jszip' import JSZip from 'jszip'
const yo = require('yo-yo') const yo = require('yo-yo')
@ -119,11 +118,12 @@ const profile = {
} }
export class LandingPage extends ViewPlugin { export class LandingPage extends ViewPlugin {
constructor (appManager, verticalIcons, fileManager, filePanel) { constructor (appManager, verticalIcons, fileManager, filePanel, contentImport) {
super(profile) super(profile)
this.profile = profile this.profile = profile
this.fileManager = fileManager this.fileManager = fileManager
this.filePanel = filePanel this.filePanel = filePanel
this.contentImport = contentImport
this.appManager = appManager this.appManager = appManager
this.verticalIcons = verticalIcons this.verticalIcons = verticalIcons
this.gistHandler = new GistHandler() this.gistHandler = new GistHandler()
@ -216,7 +216,7 @@ export class LandingPage extends ViewPlugin {
const invertNum = (themeQuality === 'dark') ? 1 : 0 const invertNum = (themeQuality === 'dark') ? 1 : 0
if (this.solEnv.getElementsByTagName('img')[0]) this.solEnv.getElementsByTagName('img')[0].style.filter = `invert(${invertNum})` if (this.solEnv.getElementsByTagName('img')[0]) this.solEnv.getElementsByTagName('img')[0].style.filter = `invert(${invertNum})`
if (this.debuggerEnv.getElementsByTagName('img')[0]) this.debuggerEnv.getElementsByTagName('img')[0].style.filter = `invert(${invertNum})` if (this.optimismEnv.getElementsByTagName('img')[0]) this.optimismEnv.getElementsByTagName('img')[0].style.filter = `invert(${invertNum})`
if (this.solhintEnv.getElementsByTagName('img')[0]) this.solhintEnv.getElementsByTagName('img')[0].style.filter = `invert(${invertNum})` if (this.solhintEnv.getElementsByTagName('img')[0]) this.solhintEnv.getElementsByTagName('img')[0].style.filter = `invert(${invertNum})`
if (this.learnEthEnv.getElementsByTagName('img')[0]) this.learnEthEnv.getElementsByTagName('img')[0].style.filter = `invert(${invertNum})` if (this.learnEthEnv.getElementsByTagName('img')[0]) this.learnEthEnv.getElementsByTagName('img')[0].style.filter = `invert(${invertNum})`
if (this.sourcifyEnv.getElementsByTagName('img')[0]) this.sourcifyEnv.getElementsByTagName('img')[0].style.filter = `invert(${invertNum})` if (this.sourcifyEnv.getElementsByTagName('img')[0]) this.sourcifyEnv.getElementsByTagName('img')[0].style.filter = `invert(${invertNum})`
@ -240,7 +240,7 @@ export class LandingPage extends ViewPlugin {
render () { render () {
const load = (service, item, examples, info) => { const load = (service, item, examples, info) => {
const compilerImport = new CompilerImports() const contentImport = this.contentImport
const fileProviders = globalRegistry.get('fileproviders').api const fileProviders = globalRegistry.get('fileproviders').api
const msg = yo` const msg = yo`
<div class="p-2"> <div class="p-2">
@ -252,7 +252,7 @@ export class LandingPage extends ViewPlugin {
const title = `Import from ${service}` const title = `Import from ${service}`
modalDialogCustom.prompt(title, msg, null, (target) => { modalDialogCustom.prompt(title, msg, null, (target) => {
if (target !== '') { if (target !== '') {
compilerImport.import( contentImport.import(
target, target,
(loadingMsg) => { tooltip(loadingMsg) }, (loadingMsg) => { tooltip(loadingMsg) },
(error, content, cleanUrl, type, url) => { (error, content, cleanUrl, type, url) => {
@ -277,10 +277,10 @@ export class LandingPage extends ViewPlugin {
this.verticalIcons.select('solidity') this.verticalIcons.select('solidity')
_paq.push(['trackEvent', 'pluginManager', 'userActivate', 'solidity']) _paq.push(['trackEvent', 'pluginManager', 'userActivate', 'solidity'])
} }
const startDebugger = async () => { const startOptimism = async () => {
await this.appManager.activatePlugin('debugger') await this.appManager.activatePlugin('optimism-compiler')
this.verticalIcons.select('debugger') this.verticalIcons.select('optimism-compiler')
_paq.push(['trackEvent', 'pluginManager', 'userActivate', 'debugger']) _paq.push(['trackEvent', 'pluginManager', 'userActivate', 'optimism-compiler'])
} }
const startSolhint = async () => { const startSolhint = async () => {
await this.appManager.activatePlugin(['solidity', 'solhint']) await this.appManager.activatePlugin(['solidity', 'solhint'])
@ -385,8 +385,8 @@ export class LandingPage extends ViewPlugin {
// main // main
this.solEnv = createLargeButton('assets/img/solidityLogo.webp', 'solidityLogo', 'Solidity', startSolidity) this.solEnv = createLargeButton('assets/img/solidityLogo.webp', 'solidityLogo', 'Solidity', startSolidity)
// Featured // Featured
this.debuggerEnv = createLargeButton('assets/img/debuggerLogo.webp', 'debuggerLogo', 'Debugger', startDebugger) this.optimismEnv = createLargeButton('assets/img/optimismLogo.webp', 'optimismLogo', 'Optimism', startOptimism)
this.solhintEnv = createLargeButton('assets/img/solhintLogo.png', 'solhintLogo', 'solhint linter', startSolhint) this.solhintEnv = createLargeButton('assets/img/solhintLogo.png', 'solhintLogo', 'Solhint linter', startSolhint)
this.learnEthEnv = createLargeButton('assets/img/learnEthLogo.webp', 'learnEthLogo', 'LearnEth', startLearnEth) this.learnEthEnv = createLargeButton('assets/img/learnEthLogo.webp', 'learnEthLogo', 'LearnEth', startLearnEth)
this.sourcifyEnv = createLargeButton('assets/img/sourcifyLogo.webp', 'sourcifyLogo', 'Sourcify', startSourceVerify) this.sourcifyEnv = createLargeButton('assets/img/sourcifyLogo.webp', 'sourcifyLogo', 'Sourcify', startSourceVerify)
this.moreEnv = createLargeButton('assets/img/moreLogo.webp', 'moreLogo', 'More', startPluginManager) this.moreEnv = createLargeButton('assets/img/moreLogo.webp', 'moreLogo', 'More', startPluginManager)
@ -395,7 +395,7 @@ export class LandingPage extends ViewPlugin {
const themeQuality = globalRegistry.get('themeModule').api.currentTheme().quality const themeQuality = globalRegistry.get('themeModule').api.currentTheme().quality
const invertNum = (themeQuality === 'dark') ? 1 : 0 const invertNum = (themeQuality === 'dark') ? 1 : 0
this.solEnv.getElementsByTagName('img')[0].style.filter = `invert(${invertNum})` this.solEnv.getElementsByTagName('img')[0].style.filter = `invert(${invertNum})`
this.debuggerEnv.getElementsByTagName('img')[0].style.filter = `invert(${invertNum})` this.optimismEnv.getElementsByTagName('img')[0].style.filter = `invert(${invertNum})`
this.solhintEnv.getElementsByTagName('img')[0].style.filter = `invert(${invertNum})` this.solhintEnv.getElementsByTagName('img')[0].style.filter = `invert(${invertNum})`
this.learnEthEnv.getElementsByTagName('img')[0].style.filter = `invert(${invertNum})` this.learnEthEnv.getElementsByTagName('img')[0].style.filter = `invert(${invertNum})`
this.sourcifyEnv.getElementsByTagName('img')[0].style.filter = `invert(${invertNum})` this.sourcifyEnv.getElementsByTagName('img')[0].style.filter = `invert(${invertNum})`
@ -505,10 +505,10 @@ export class LandingPage extends ViewPlugin {
<h4>Featured Plugins</h4> <h4>Featured Plugins</h4>
<div class="d-flex flex-row pt-2"> <div class="d-flex flex-row pt-2">
${this.solEnv} ${this.solEnv}
${this.optimismEnv}
${this.learnEthEnv} ${this.learnEthEnv}
${this.solhintEnv} ${this.solhintEnv}
${this.sourcifyEnv} ${this.sourcifyEnv}
${this.debuggerEnv}
${this.moreEnv} ${this.moreEnv}
</div> </div>
</div> </div>

@ -51,7 +51,7 @@ const confirmationCb = function (network, tx, gasEstimation, continueTxExecution
return continueTxExecution(null) return continueTxExecution(null)
} }
var amount = Web3.utils.fromWei(typeConversion.toInt(tx.value), 'ether') var amount = Web3.utils.fromWei(typeConversion.toInt(tx.value), 'ether')
var content = confirmDialog(tx, amount, gasEstimation, self.udappUI, var content = confirmDialog(tx, network, amount, gasEstimation,
(gasPrice, cb) => { (gasPrice, cb) => {
let txFeeText, priceStatus let txFeeText, priceStatus
// TODO: this try catch feels like an anti pattern, can/should be // TODO: this try catch feels like an anti pattern, can/should be
@ -93,10 +93,9 @@ const confirmationCb = function (network, tx, gasEstimation, continueTxExecution
) )
// TODO: check if this is check is still valid given the refactor // TODO: check if this is check is still valid given the refactor
if (!content.gasPriceStatus) { if (!content.gasPriceStatus) {
cancelCb('Given gas price is not correct') cancelCb('Given transaction fee is not correct')
} else { } else {
var gasPrice = Web3.utils.toWei(content.querySelector('#gasprice').value, 'gwei') continueTxExecution(content.txFee)
continueTxExecution(gasPrice)
} }
} }
}, },

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

@ -30,7 +30,7 @@ ace.define("ace/theme/remixDark",["require","exports","module","ace/lib/dom"], f
}\ }\
.ace-remixDark {\ .ace-remixDark {\
background-color: #222336;\ background-color: #222336;\
color: #a2a3bd;\ color: #d5d5e9;\
}\ }\
.ace-remixDark .ace_cursor {\ .ace-remixDark .ace_cursor {\
border-left: 2px solid #FFFFFF;\ border-left: 2px solid #FFFFFF;\
@ -109,10 +109,10 @@ ace.define("ace/theme/remixDark",["require","exports","module","ace/lib/dom"], f
}\ }\
.ace-remixDark .ace_type {\ .ace-remixDark .ace_type {\
color:#75ceef;\ color:#75ceef;\
}]\ }\
.ace-remixDark .ace_visibility (\ .ace-remixDark .ace_visibility {\
color:#f7d777;\ color:#f7d777;\
)\ }\
.ace-remixDark .ace_identifier {\ .ace-remixDark .ace_identifier {\
color:#bec1dd;\ color:#bec1dd;\
}\ }\

@ -20,9 +20,10 @@ export class ExecutionContext {
constructor () { constructor () {
this.event = new EventManager() this.event = new EventManager()
this.executionContext = null this.executionContext = null
this.lastBlock = null
this.blockGasLimitDefault = 4300000 this.blockGasLimitDefault = 4300000
this.blockGasLimit = this.blockGasLimitDefault this.blockGasLimit = this.blockGasLimitDefault
this.currentFork = 'berlin' this.currentFork = 'london'
this.mainNetGenesisHash = '0xd4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3' this.mainNetGenesisHash = '0xd4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3'
this.customNetWorks = {} this.customNetWorks = {}
this.blocks = {} this.blocks = {}
@ -86,10 +87,10 @@ export class ExecutionContext {
web3.eth.getBlock(0, (error, block) => { web3.eth.getBlock(0, (error, block) => {
if (error) console.log('cant query first block') if (error) console.log('cant query first block')
if (block && block.hash !== this.mainNetGenesisHash) name = 'Custom' if (block && block.hash !== this.mainNetGenesisHash) name = 'Custom'
callback(err, { id, name }) callback(err, { id, name, lastBlock: this.lastBlock, currentFork: this.currentFork })
}) })
} else { } else {
callback(err, { id, name }) callback(err, { id, name, lastBlock: this.lastBlock, currentFork: this.currentFork })
} }
}) })
} }
@ -176,10 +177,11 @@ export class ExecutionContext {
const block = await web3.eth.getBlock('latest') const block = await web3.eth.getBlock('latest')
// we can't use the blockGasLimit cause the next blocks could have a lower limit : https://github.com/ethereum/remix/issues/506 // we can't use the blockGasLimit cause the next blocks could have a lower limit : https://github.com/ethereum/remix/issues/506
this.blockGasLimit = (block && block.gasLimit) ? Math.floor(block.gasLimit - (5 * block.gasLimit) / 1024) : this.blockGasLimitDefault this.blockGasLimit = (block && block.gasLimit) ? Math.floor(block.gasLimit - (5 * block.gasLimit) / 1024) : this.blockGasLimitDefault
this.lastBlock = block
try { try {
this.currentFork = execution.forkAt(await web3.eth.net.getId(), block.number) this.currentFork = execution.forkAt(await web3.eth.net.getId(), block.number)
} catch (e) { } catch (e) {
this.currentFork = 'berlin' this.currentFork = 'london'
console.log(`unable to detect fork, defaulting to ${this.currentFork}..`) console.log(`unable to detect fork, defaulting to ${this.currentFork}..`)
console.error(e) console.error(e)
} }

@ -1,6 +1,6 @@
{ {
"name": "@remix-project/remix-analyzer", "name": "@remix-project/remix-analyzer",
"version": "0.5.12", "version": "0.5.14",
"description": "Tool to perform static analysis on Solidity smart contracts", "description": "Tool to perform static analysis on Solidity smart contracts",
"main": "index.js", "main": "index.js",
"types": ".index.d.ts", "types": ".index.d.ts",
@ -19,14 +19,14 @@
} }
], ],
"dependencies": { "dependencies": {
"@ethereumjs/block": "^3.3.0", "@ethereumjs/block": "^3.4.0",
"@ethereumjs/tx": "^3.2.1", "@ethereumjs/tx": "^3.3.0",
"@ethereumjs/vm": "^5.4.1", "@ethereumjs/vm": "^5.5.0",
"@remix-project/remix-astwalker": "^0.0.26", "@remix-project/remix-astwalker": "^0.0.26",
"@remix-project/remix-lib": "^0.5.3", "@remix-project/remix-lib": "^0.5.5",
"async": "^2.6.2", "async": "^2.6.2",
"ethereumjs-util": "^7.0.10", "ethereumjs-util": "^7.0.10",
"ethers": "^5.1.4", "ethers": "^5.4.2",
"string-similarity": "^4.0.4", "string-similarity": "^4.0.4",
"web3": "1.2.4" "web3": "1.2.4"
}, },
@ -51,5 +51,5 @@
"typescript": "^3.7.5" "typescript": "^3.7.5"
}, },
"typings": "index.d.ts", "typings": "index.d.ts",
"gitHead": "5e91f3010a7198f1b2d90cc7796bda750c58f1ea" "gitHead": "df7abe1c219e361a947031d620f4ae6e3786a4d7"
} }

@ -1,6 +1,6 @@
{ {
"name": "@remix-project/remix-astwalker", "name": "@remix-project/remix-astwalker",
"version": "0.0.33", "version": "0.0.35",
"description": "Tool to walk through Solidity AST", "description": "Tool to walk through Solidity AST",
"main": "index.js", "main": "index.js",
"scripts": { "scripts": {
@ -34,14 +34,14 @@
] ]
}, },
"dependencies": { "dependencies": {
"@ethereumjs/block": "^3.3.0", "@ethereumjs/block": "^3.4.0",
"@ethereumjs/tx": "^3.2.1", "@ethereumjs/tx": "^3.3.0",
"@ethereumjs/vm": "^5.4.1", "@ethereumjs/vm": "^5.5.0",
"@remix-project/remix-lib": "^0.5.3", "@remix-project/remix-lib": "^0.5.5",
"@types/tape": "^4.2.33", "@types/tape": "^4.2.33",
"async": "^2.6.2", "async": "^2.6.2",
"ethereumjs-util": "^7.0.10", "ethereumjs-util": "^7.0.10",
"ethers": "^5.1.4", "ethers": "^5.4.2",
"nyc": "^13.3.0", "nyc": "^13.3.0",
"string-similarity": "^4.0.4", "string-similarity": "^4.0.4",
"tape": "^4.10.1", "tape": "^4.10.1",
@ -53,5 +53,5 @@
"tap-spec": "^5.0.0" "tap-spec": "^5.0.0"
}, },
"typings": "index.d.ts", "typings": "index.d.ts",
"gitHead": "5e91f3010a7198f1b2d90cc7796bda750c58f1ea" "gitHead": "df7abe1c219e361a947031d620f4ae6e3786a4d7"
} }

@ -21,9 +21,14 @@ export class CompilerImports extends Plugin {
} }
async setToken () { async setToken () {
const protocol = typeof window !== 'undefined' && window.location.protocol try {
const token = await this.call('settings', 'get', 'settings/gist-access-token') const protocol = typeof window !== 'undefined' && window.location.protocol
this.urlResolver.setGistToken(token, protocol) const token = await this.call('settings', 'get', 'settings/gist-access-token')
this.urlResolver.setGistToken(token, protocol)
} catch (error) {
console.log(error)
}
} }
isRelativeImport (url) { isRelativeImport (url) {

@ -1,6 +1,6 @@
{ {
"name": "@remix-project/remix-debug", "name": "@remix-project/remix-debug",
"version": "0.5.3", "version": "0.5.5",
"description": "Tool to debug Ethereum transactions", "description": "Tool to debug Ethereum transactions",
"contributors": [ "contributors": [
{ {
@ -18,17 +18,17 @@
], ],
"main": "src/index.js", "main": "src/index.js",
"dependencies": { "dependencies": {
"@ethereumjs/block": "^3.3.0", "@ethereumjs/block": "^3.4.0",
"@ethereumjs/common": "^2.2.0", "@ethereumjs/common": "^2.2.0",
"@ethereumjs/tx": "^3.2.1", "@ethereumjs/tx": "^3.3.0",
"@ethereumjs/vm": "^5.4.1", "@ethereumjs/vm": "^5.5.0",
"@remix-project/remix-astwalker": "^0.0.26", "@remix-project/remix-astwalker": "^0.0.26",
"@remix-project/remix-lib": "^0.5.3", "@remix-project/remix-lib": "^0.5.5",
"async": "^2.6.2", "async": "^2.6.2",
"commander": "^2.19.0", "commander": "^2.19.0",
"deep-equal": "^1.0.1", "deep-equal": "^1.0.1",
"ethereumjs-util": "^7.0.10", "ethereumjs-util": "^7.0.10",
"ethers": "^5.1.4", "ethers": "^5.4.2",
"string-similarity": "^4.0.4", "string-similarity": "^4.0.4",
"web3": "^1.2.4" "web3": "^1.2.4"
}, },
@ -61,5 +61,5 @@
}, },
"homepage": "https://github.com/ethereum/remix-project/tree/master/libs/remix-debug#readme", "homepage": "https://github.com/ethereum/remix-project/tree/master/libs/remix-debug#readme",
"typings": "src/index.d.ts", "typings": "src/index.d.ts",
"gitHead": "5e91f3010a7198f1b2d90cc7796bda750c58f1ea" "gitHead": "df7abe1c219e361a947031d620f4ae6e3786a4d7"
} }

@ -46,7 +46,7 @@ type Opcode = {
* information about the opcode. * information about the opcode.
*/ */
export function parseCode (raw) { export function parseCode (raw) {
const common = new Common({ chain: 'mainnet', hardfork: 'berlin' }) const common = new Common({ chain: 'mainnet', hardfork: 'london' })
const opcodes = getOpcodesForHF(common) const opcodes = getOpcodesForHF(common)
const code = [] const code = []

@ -40,7 +40,7 @@ export class TraceManager {
const networkId = await this.web3.eth.net.getId() const networkId = await this.web3.eth.net.getId()
this.fork = execution.forkAt(networkId, tx.blockNumber) this.fork = execution.forkAt(networkId, tx.blockNumber)
} catch (e) { } catch (e) {
this.fork = 'berlin' this.fork = 'london'
console.log(`unable to detect fork, defaulting to ${this.fork}..`) console.log(`unable to detect fork, defaulting to ${this.fork}..`)
console.error(e) console.error(e)
} }

@ -11,7 +11,7 @@ export function sendTx (vm, from, to, value, data, cb?) {
return new Promise ((resolve, reject) => { return new Promise ((resolve, reject) => {
var tx = new Tx({ var tx = new Tx({
nonce: new BN(from.nonce++), nonce: new BN(from.nonce++),
gasPrice: new BN(1), // gasPrice: new BN(1),
gasLimit: new BN(3000000, 10), gasLimit: new BN(3000000, 10),
to: to, to: to,
value: new BN(value, 10), value: new BN(value, 10),

@ -9,7 +9,7 @@ var remixLib = require('@remix-project/remix-lib')
function sendTx (vm, from, to, value, data, cb) { function sendTx (vm, from, to, value, data, cb) {
var tx = new Tx({ var tx = new Tx({
nonce: new BN(from.nonce++), nonce: new BN(from.nonce++),
gasPrice: new BN(1), // gasPrice: new BN(1),
gasLimit: new BN(3000000, 10), gasLimit: new BN(3000000, 10),
to: to, to: to,
value: new BN(value, 10), value: new BN(value, 10),

@ -1,6 +1,6 @@
{ {
"name": "@remix-project/remix-lib", "name": "@remix-project/remix-lib",
"version": "0.5.3", "version": "0.5.5",
"description": "Library to various Remix tools", "description": "Library to various Remix tools",
"contributors": [ "contributors": [
{ {
@ -14,9 +14,9 @@
], ],
"main": "src/index.js", "main": "src/index.js",
"dependencies": { "dependencies": {
"@ethereumjs/block": "^3.3.0", "@ethereumjs/block": "^3.4.0",
"@ethereumjs/tx": "^3.2.1", "@ethereumjs/tx": "^3.3.0",
"@ethereumjs/vm": "^5.4.1", "@ethereumjs/vm": "^5.5.0",
"async": "^2.1.2", "async": "^2.1.2",
"ethereumjs-util": "^7.0.10", "ethereumjs-util": "^7.0.10",
"ethers": "^4.0.40", "ethers": "^4.0.40",
@ -53,5 +53,5 @@
}, },
"homepage": "https://github.com/ethereum/remix-project/tree/master/libs/remix-lib#readme", "homepage": "https://github.com/ethereum/remix-project/tree/master/libs/remix-lib#readme",
"typings": "src/index.d.ts", "typings": "src/index.d.ts",
"gitHead": "5e91f3010a7198f1b2d90cc7796bda750c58f1ea" "gitHead": "df7abe1c219e361a947031d620f4ae6e3786a4d7"
} }

@ -17,7 +17,7 @@ export function forkAt (networkId, blockNumber) {
} }
return currentForkName return currentForkName
} }
return 'berlin' return 'london'
} }
// see https://github.com/ethereum/go-ethereum/blob/master/params/config.go // see https://github.com/ethereum/go-ethereum/blob/master/params/config.go
@ -46,6 +46,10 @@ const forks = {
{ {
number: 12244000, number: 12244000,
name: 'berlin' name: 'berlin'
},
{
number: 12965000,
name: 'london'
} }
], ],
3: [ 3: [

@ -72,7 +72,7 @@ export class TxRunnerVM {
} }
} }
const EIP1559 = this.commonContext.hardfork() !== 'berlin' const EIP1559 = this.commonContext.hardfork() !== 'berlin' // berlin is the only pre eip1559 fork that we handle.
let tx let tx
if (!EIP1559) { if (!EIP1559) {
tx = Transaction.fromTxData({ tx = Transaction.fromTxData({

@ -15,8 +15,18 @@ export class TxRunnerWeb3 {
this._api = api this._api = api
} }
_executeTx (tx, gasPrice, api, promptCb, callback) { _executeTx (tx, txFee, api, promptCb, callback) {
if (gasPrice) tx.gasPrice = this.getWeb3().utils.toHex(gasPrice) if (txFee) {
if (txFee.baseFeePerGas) {
tx.maxPriorityFee = this.getWeb3().utils.toHex(this.getWeb3().utils.toWei(txFee.maxPriorityFee, 'gwei'))
tx.maxFee = this.getWeb3().utils.toHex(this.getWeb3().utils.toWei(txFee.maxFee, 'gwei'))
tx.type = 2
} else {
tx.gasPrice = this.getWeb3().utils.toHex(this.getWeb3().utils.toWei(txFee.gasPrice, 'gwei'))
tx.type = 1
}
}
if (api.personalMode()) { if (api.personalMode()) {
promptCb( promptCb(
(value) => { (value) => {
@ -100,8 +110,8 @@ export class TxRunnerWeb3 {
return return
} }
confirmCb(network, tx, tx['gas'], (gasPrice) => { confirmCb(network, tx, tx['gas'], (txFee) => {
return this._executeTx(tx, gasPrice, this._api, promptCb, callback) return this._executeTx(tx, txFee, this._api, promptCb, callback)
}, (error) => { }, (error) => {
callback(error) callback(error)
}) })

@ -1,6 +1,6 @@
{ {
"name": "@remix-project/remix-simulator", "name": "@remix-project/remix-simulator",
"version": "0.2.3", "version": "0.2.5",
"description": "Ethereum IDE and tools for the web", "description": "Ethereum IDE and tools for the web",
"contributors": [ "contributors": [
{ {
@ -14,11 +14,11 @@
], ],
"main": "src/index.js", "main": "src/index.js",
"dependencies": { "dependencies": {
"@ethereumjs/block": "^3.3.0", "@ethereumjs/block": "^3.4.0",
"@ethereumjs/common": "^2.2.0", "@ethereumjs/common": "^2.2.0",
"@ethereumjs/tx": "^3.2.1", "@ethereumjs/tx": "^3.3.0",
"@ethereumjs/vm": "^5.4.1", "@ethereumjs/vm": "^5.5.0",
"@remix-project/remix-lib": "^0.5.3", "@remix-project/remix-lib": "^0.5.5",
"ansi-gray": "^0.1.1", "ansi-gray": "^0.1.1",
"async": "^3.1.0", "async": "^3.1.0",
"body-parser": "^1.18.2", "body-parser": "^1.18.2",
@ -26,7 +26,7 @@
"commander": "^2.19.0", "commander": "^2.19.0",
"cors": "^2.8.5", "cors": "^2.8.5",
"ethereumjs-util": "^7.0.10", "ethereumjs-util": "^7.0.10",
"ethers": "^5.1.4", "ethers": "^5.4.2",
"express": "^4.16.3", "express": "^4.16.3",
"express-ws": "^4.0.0", "express-ws": "^4.0.0",
"merge": "^1.2.0", "merge": "^1.2.0",
@ -66,5 +66,5 @@
}, },
"homepage": "https://github.com/ethereum/remix-project/tree/master/libs/remix-simulator#readme", "homepage": "https://github.com/ethereum/remix-project/tree/master/libs/remix-simulator#readme",
"typings": "src/index.d.ts", "typings": "src/index.d.ts",
"gitHead": "5e91f3010a7198f1b2d90cc7796bda750c58f1ea" "gitHead": "df7abe1c219e361a947031d620f4ae6e3786a4d7"
} }

@ -99,7 +99,7 @@ export class VMContext {
constructor (fork?) { constructor (fork?) {
this.blockGasLimitDefault = 4300000 this.blockGasLimitDefault = 4300000
this.blockGasLimit = this.blockGasLimitDefault this.blockGasLimit = this.blockGasLimitDefault
this.currentFork = fork || 'berlin' this.currentFork = fork || 'london'
this.currentVm = this.createVm(this.currentFork) this.currentVm = this.createVm(this.currentFork)
this.blocks = {} this.blocks = {}
this.latestBlockNumber = 0 this.latestBlockNumber = 0

@ -1,6 +1,6 @@
{ {
"name": "@remix-project/remix-solidity", "name": "@remix-project/remix-solidity",
"version": "0.4.3", "version": "0.4.5",
"description": "Tool to load and run Solidity compiler", "description": "Tool to load and run Solidity compiler",
"main": "index.js", "main": "index.js",
"types": "./index.d.ts", "types": "./index.d.ts",
@ -15,14 +15,14 @@
} }
], ],
"dependencies": { "dependencies": {
"@ethereumjs/block": "^3.3.0", "@ethereumjs/block": "^3.4.0",
"@ethereumjs/tx": "^3.2.1", "@ethereumjs/tx": "^3.3.0",
"@ethereumjs/vm": "^5.4.1", "@ethereumjs/vm": "^5.5.0",
"@remix-project/remix-lib": "^0.5.3", "@remix-project/remix-lib": "^0.5.5",
"async": "^2.6.2", "async": "^2.6.2",
"eslint-scope": "^5.0.0", "eslint-scope": "^5.0.0",
"ethereumjs-util": "^7.0.10", "ethereumjs-util": "^7.0.10",
"ethers": "^5.1.4", "ethers": "^5.4.2",
"solc": "^0.7.4", "solc": "^0.7.4",
"string-similarity": "^4.0.4", "string-similarity": "^4.0.4",
"web3": "1.2.4", "web3": "1.2.4",
@ -59,5 +59,5 @@
}, },
"homepage": "https://github.com/ethereum/remix-project/tree/master/libs/remix-solidity#readme", "homepage": "https://github.com/ethereum/remix-project/tree/master/libs/remix-solidity#readme",
"typings": "index.d.ts", "typings": "index.d.ts",
"gitHead": "5e91f3010a7198f1b2d90cc7796bda750c58f1ea" "gitHead": "df7abe1c219e361a947031d620f4ae6e3786a4d7"
} }

@ -1,6 +1,6 @@
{ {
"name": "@remix-project/remix-tests", "name": "@remix-project/remix-tests",
"version": "0.2.3", "version": "0.2.5",
"description": "Tool to test Solidity smart contracts", "description": "Tool to test Solidity smart contracts",
"main": "src/index.js", "main": "src/index.js",
"types": "./src/index.d.ts", "types": "./src/index.d.ts",
@ -35,13 +35,13 @@
}, },
"homepage": "https://github.com/ethereum/remix-project/tree/master/libs/remix-tests#readme", "homepage": "https://github.com/ethereum/remix-project/tree/master/libs/remix-tests#readme",
"dependencies": { "dependencies": {
"@ethereumjs/block": "^3.3.0", "@ethereumjs/block": "^3.4.0",
"@ethereumjs/common": "^2.2.0", "@ethereumjs/common": "^2.2.0",
"@ethereumjs/tx": "^3.2.1", "@ethereumjs/tx": "^3.3.0",
"@ethereumjs/vm": "^5.4.1", "@ethereumjs/vm": "^5.5.0",
"@remix-project/remix-lib": "^0.5.3", "@remix-project/remix-lib": "^0.5.5",
"@remix-project/remix-simulator": "^0.2.3", "@remix-project/remix-simulator": "^0.2.5",
"@remix-project/remix-solidity": "^0.4.3", "@remix-project/remix-solidity": "^0.4.5",
"ansi-gray": "^0.1.1", "ansi-gray": "^0.1.1",
"async": "^2.6.0", "async": "^2.6.0",
"axios": ">=0.21.1", "axios": ">=0.21.1",
@ -50,7 +50,7 @@
"colors": "^1.1.2", "colors": "^1.1.2",
"commander": "^2.13.0", "commander": "^2.13.0",
"ethereumjs-util": "^7.0.10", "ethereumjs-util": "^7.0.10",
"ethers": "^5.1.4", "ethers": "^5.4.2",
"express-ws": "^4.0.0", "express-ws": "^4.0.0",
"merge": "^1.2.0", "merge": "^1.2.0",
"signale": "^1.4.0", "signale": "^1.4.0",
@ -77,5 +77,5 @@
"typescript": "^3.3.1" "typescript": "^3.3.1"
}, },
"typings": "src/index.d.ts", "typings": "src/index.d.ts",
"gitHead": "5e91f3010a7198f1b2d90cc7796bda750c58f1ea" "gitHead": "df7abe1c219e361a947031d620f4ae6e3786a4d7"
} }

@ -40,6 +40,7 @@ export const CopyToClipboard = (props: ICopyToClipboard) => {
} }
return ( return (
// eslint-disable-next-line jsx-a11y/anchor-is-valid
<a href='#' onClick={handleClick} onMouseLeave={reset}> <a href='#' onClick={handleClick} onMouseLeave={reset}>
<OverlayTrigger placement={direction} overlay={ <OverlayTrigger placement={direction} overlay={
<Tooltip id="overlay-tooltip"> <Tooltip id="overlay-tooltip">

@ -4,6 +4,13 @@ import { action, FileExplorerContextMenuProps } from './types'
import './css/file-explorer-context-menu.css' import './css/file-explorer-context-menu.css'
import { customAction } from '@remixproject/plugin-api/lib/file-system/file-panel' import { customAction } from '@remixproject/plugin-api/lib/file-system/file-panel'
declare global {
interface Window {
_paq: any
}
}
const _paq = window._paq = window._paq || [] //eslint-disable-line
export const FileExplorerContextMenu = (props: FileExplorerContextMenuProps) => { export const FileExplorerContextMenu = (props: FileExplorerContextMenuProps) => {
const { actions, createNewFile, createNewFolder, deletePath, renamePath, hideContextMenu, pushChangesToGist, publishFileToGist, publishFolderToGist, copy, paste, runScript, emit, pageX, pageY, path, type, focus, ...otherProps } = props const { actions, createNewFile, createNewFolder, deletePath, renamePath, hideContextMenu, pushChangesToGist, publishFileToGist, publishFolderToGist, copy, paste, runScript, emit, pageX, pageY, path, type, focus, ...otherProps } = props
const contextMenuRef = useRef(null) const contextMenuRef = useRef(null)
@ -76,15 +83,19 @@ export const FileExplorerContextMenu = (props: FileExplorerContextMenuProps) =>
deletePath(getPath()) deletePath(getPath())
break break
case 'Push changes to gist': case 'Push changes to gist':
_paq.push(['trackEvent', 'fileExplorer', 'pushToChangesoGist'])
pushChangesToGist(path, type) pushChangesToGist(path, type)
break break
case 'Publish folder to gist': case 'Publish folder to gist':
_paq.push(['trackEvent', 'fileExplorer', 'publishFolderToGist'])
publishFolderToGist(path, type) publishFolderToGist(path, type)
break break
case 'Publish file to gist': case 'Publish file to gist':
_paq.push(['trackEvent', 'fileExplorer', 'publishFileToGist'])
publishFileToGist(path, type) publishFileToGist(path, type)
break break
case 'Run': case 'Run':
_paq.push(['trackEvent', 'fileExplorer', 'runScript'])
runScript(path) runScript(path)
break break
case 'Copy': case 'Copy':
@ -97,6 +108,7 @@ export const FileExplorerContextMenu = (props: FileExplorerContextMenuProps) =>
deletePath(getPath()) deletePath(getPath())
break break
default: default:
_paq.push(['trackEvent', 'fileExplorer', 'customAction', item.name])
emit && emit({ ...item, path: [path] } as customAction) emit && emit({ ...item, path: [path] } as customAction)
break break
} }

@ -16,6 +16,24 @@ export const ModalDialog = (props: ModalDialogProps) => {
modal.current.focus() modal.current.focus()
}, [props.hide]) }, [props.hide])
useEffect(() => {
function handleBlur (e) {
if (!e.currentTarget.contains(e.relatedTarget)) {
e.stopPropagation()
if (document.activeElement !== this) {
handleHide()
}
}
}
if (modal.current) {
modal.current.addEventListener('blur', handleBlur)
return () => {
modal.current.removeEventListener('blur', handleBlur)
}
}
}, [modal.current])
const modalKeyEvent = (keyCode) => { const modalKeyEvent = (keyCode) => {
if (keyCode === 27) { // Esc if (keyCode === 27) { // Esc
if (props.cancelFn) props.cancelFn() if (props.cancelFn) props.cancelFn()
@ -40,13 +58,6 @@ export const ModalDialog = (props: ModalDialogProps) => {
handleHide() handleHide()
} }
const handleBlur = (e) => {
if (!e.currentTarget.contains(e.relatedTarget)) {
e.stopPropagation()
handleHide()
}
}
return ( return (
<div <div
data-id={`${props.id}ModalDialogContainer-react`} data-id={`${props.id}ModalDialogContainer-react`}
@ -58,7 +69,6 @@ export const ModalDialog = (props: ModalDialogProps) => {
> >
<div className="modal-dialog" role="document"> <div className="modal-dialog" role="document">
<div <div
onBlur={handleBlur}
ref={modal} ref={modal}
tabIndex={-1} tabIndex={-1}
className={'modal-content remixModalContent ' + (props.modalClass ? props.modalClass : '')} className={'modal-content remixModalContent ' + (props.modalClass ? props.modalClass : '')}

@ -44,7 +44,7 @@ export const listenToEvents = (editor, compileTabLogic) => (dispatch: React.Disp
}) })
compileTabLogic.compiler.event.register('loadingCompiler', () => { compileTabLogic.compiler.event.register('loadingCompiler', () => {
dispatch(setCompilerMode('compilationDuration')) dispatch(setCompilerMode('loadingCompiler'))
}) })
compileTabLogic.compiler.event.register('compilerLoaded', () => { compileTabLogic.compiler.event.register('compilerLoaded', () => {

@ -5,9 +5,18 @@ import * as helper from '../../../../../apps/remix-ide/src/lib/helper'
import { canUseWorker, baseURLBin, baseURLWasm, urlFromVersion, pathToURL, promisedMiniXhr } from '@remix-project/remix-solidity' import { canUseWorker, baseURLBin, baseURLWasm, urlFromVersion, pathToURL, promisedMiniXhr } from '@remix-project/remix-solidity'
import { compilerReducer, compilerInitialState } from './reducers/compiler' import { compilerReducer, compilerInitialState } from './reducers/compiler'
import { resetEditorMode, listenToEvents } from './actions/compiler' import { resetEditorMode, listenToEvents } from './actions/compiler'
import { OverlayTrigger, Tooltip } from 'react-bootstrap' // eslint-disable-line
import './css/style.css' import './css/style.css'
declare global {
interface Window {
_paq: any
}
}
const _paq = window._paq = window._paq || [] //eslint-disable-line
export const CompilerContainer = (props: CompilerContainerProps) => { export const CompilerContainer = (props: CompilerContainerProps) => {
const { editor, config, queryParams, compileTabLogic, tooltip, modal, compiledFileName, setHardHatCompilation, updateCurrentVersion, isHardHatProject, configurationSettings } = props // eslint-disable-line const { editor, config, queryParams, compileTabLogic, tooltip, modal, compiledFileName, setHardHatCompilation, updateCurrentVersion, isHardHatProject, configurationSettings } = props // eslint-disable-line
const [state, setState] = useState({ const [state, setState] = useState({
@ -27,8 +36,8 @@ export const CompilerContainer = (props: CompilerContainerProps) => {
language: '', language: '',
evmVersion: '' evmVersion: ''
}) })
const [disableCompileButton, setDisableCompileButton] = useState<boolean>(false)
const compileIcon = useRef(null) const compileIcon = useRef(null)
const warningIcon = useRef(null)
const promptMessageInput = useRef(null) const promptMessageInput = useRef(null)
const [hhCompilation, sethhCompilation] = useState(false) const [hhCompilation, sethhCompilation] = useState(false)
const [compilerContainer, dispatch] = useReducer(compilerReducer, compilerInitialState) const [compilerContainer, dispatch] = useReducer(compilerReducer, compilerInitialState)
@ -75,6 +84,9 @@ export const CompilerContainer = (props: CompilerContainerProps) => {
}, [compileTabLogic]) }, [compileTabLogic])
useEffect(() => { useEffect(() => {
const isDisabled = !compiledFileName || (compiledFileName && !isSolFileSelected(compiledFileName))
setDisableCompileButton(isDisabled)
setState(prevState => { setState(prevState => {
return { ...prevState, compiledFileName } return { ...prevState, compiledFileName }
}) })
@ -235,14 +247,8 @@ export const CompilerContainer = (props: CompilerContainerProps) => {
} }
const compilationDuration = (speed: number) => { const compilationDuration = (speed: number) => {
if (!warningIcon.current) return
if (speed > 1000) { if (speed > 1000) {
const msg = `Last compilation took ${speed}ms. We suggest to turn off autocompilation.` console.log(`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'
} }
} }
@ -256,8 +262,8 @@ export const CompilerContainer = (props: CompilerContainerProps) => {
if (!compileIcon.current) return if (!compileIcon.current) return
compileIcon.current.setAttribute('title', 'compiler is loading, please wait a few moments.') compileIcon.current.setAttribute('title', 'compiler is loading, please wait a few moments.')
compileIcon.current.classList.add('remixui_spinningIcon') compileIcon.current.classList.add('remixui_spinningIcon')
warningIcon.current.style.visibility = 'hidden'
_updateLanguageSelector() _updateLanguageSelector()
setDisableCompileButton(true)
} }
const compilerLoaded = () => { const compilerLoaded = () => {
@ -265,6 +271,9 @@ export const CompilerContainer = (props: CompilerContainerProps) => {
compileIcon.current.setAttribute('title', '') compileIcon.current.setAttribute('title', '')
compileIcon.current.classList.remove('remixui_spinningIcon') compileIcon.current.classList.remove('remixui_spinningIcon')
if (state.autoCompile) compile() if (state.autoCompile) compile()
const isDisabled = !compiledFileName || (compiledFileName && !isSolFileSelected(compiledFileName))
setDisableCompileButton(isDisabled)
} }
const compilationFinished = () => { const compilationFinished = () => {
@ -272,6 +281,7 @@ export const CompilerContainer = (props: CompilerContainerProps) => {
compileIcon.current.setAttribute('title', 'idle') compileIcon.current.setAttribute('title', 'idle')
compileIcon.current.classList.remove('remixui_spinningIcon') compileIcon.current.classList.remove('remixui_spinningIcon')
compileIcon.current.classList.remove('remixui_bouncingIcon') compileIcon.current.classList.remove('remixui_bouncingIcon')
_paq.push(['trackEvent', 'compiler', 'compiled_with_version', _retrieveVersion()])
} }
const scheduleCompilation = () => { const scheduleCompilation = () => {
@ -292,7 +302,7 @@ export const CompilerContainer = (props: CompilerContainerProps) => {
if (!isSolFileSelected()) return if (!isSolFileSelected()) return
_setCompilerVersionFromPragma(currentFile) _setCompilerVersionFromPragma(currentFile)
compileTabLogic.runCompiler() compileTabLogic.runCompiler(hhCompilation)
} }
const _retrieveVersion = (version?) => { const _retrieveVersion = (version?) => {
@ -440,6 +450,7 @@ export const CompilerContainer = (props: CompilerContainerProps) => {
const handleNightliesChange = (e) => { const handleNightliesChange = (e) => {
const checked = e.target.checked const checked = e.target.checked
if (!checked) handleLoadVersion(state.defaultVersion)
config.set('includeNightlies', checked) config.set('includeNightlies', checked)
setState(prevState => { setState(prevState => {
return { ...prevState, includeNightlies: checked } return { ...prevState, includeNightlies: checked }
@ -522,6 +533,7 @@ export const CompilerContainer = (props: CompilerContainerProps) => {
<label className="remixui_compilerLabel form-check-label" htmlFor="evmVersionSelector">EVM Version</label> <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"> <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 === 'default' ? 'selected' : ''} value="default">compiler default</option>
<option data-id={state.evmVersion === 'berlin' ? 'selected' : ''} value="berlin">berlin</option>
<option data-id={state.evmVersion === 'muirGlacier' ? 'selected' : ''} value="muirGlacier">muirGlacier</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 === 'istanbul' ? 'selected' : ''} value="istanbul">istanbul</option>
<option data-id={state.evmVersion === 'petersburg' ? 'selected' : ''} value="petersburg">petersburg</option> <option data-id={state.evmVersion === 'petersburg' ? 'selected' : ''} value="petersburg">petersburg</option>
@ -561,16 +573,25 @@ export const CompilerContainer = (props: CompilerContainerProps) => {
</div> </div>
</div> </div>
{ {
isHardHatProject && <div className="mt-2 remixui_compilerConfig custom-control custom-checkbox"> isHardHatProject &&
<div className="mt-3 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} /> <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> <label className="form-check-label custom-control-label" htmlFor="enableHardhat">Enable Hardhat Compilation</label>
<a className="mt-1 text-nowrap" href='https://remix-ide.readthedocs.io/en/latest/hardhat.html#enable-hardhat-compilation' target={'_blank'}>
<OverlayTrigger placement={'right'} overlay={
<Tooltip className="text-nowrap" id="overlay-tooltip">
<span className="p-1 pr-3" style={{ backgroundColor: 'black', minWidth: '230px' }}>Learn how to use Hardhat Compilation</span>
</Tooltip>
}>
<i style={{ fontSize: 'medium' }} className={'ml-2 fal fa-info-circle'} aria-hidden="true"></i>
</OverlayTrigger>
</a>
</div> </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))}> <button id="compileBtn" data-id="compilerContainerCompileBtn" className="btn btn-primary btn-block remixui_disabled mt-3" title="Compile" onClick={compile} disabled={disableCompileButton}>
<span> <span>
<i ref={warningIcon} title="Compilation Slow" style={{ visibility: 'hidden' }} className="remixui_warnCompilationSlow fas fa-exclamation-triangle" aria-hidden="true"></i> { <i ref={compileIcon} className="fas fa-sync remixui_icon" 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 { typeof state.compiledFileName === 'string' ? helper.extractNameFromKey(state.compiledFileName) || '<no file selected>' : '<no file selected>' }
Compile { state.compiledFileName || '<no file selected>' }
</span> </span>
</button> </button>
</header> </header>

@ -162,17 +162,25 @@ export const ContractSelection = (props: ContractSelectionProps) => {
} }
const contractProperties = contractsDetails[selectedContract] || {} const contractProperties = contractsDetails[selectedContract] || {}
const log = <div className="remixui_detailsJSON"> const log = <div className="remixui_detailsJSON">
{ <TreeView>
Object.keys(contractProperties).map((propertyName, index) => { {
const copyDetails = <span className="remixui_copyDetails"><CopyToClipboard content={contractProperties[propertyName]} direction='top' /></span> Object.keys(contractProperties).map((propertyName, index) => {
const questionMark = <span className="remixui_questionMark"><i title={ help[propertyName] } className="fas fa-question-circle" aria-hidden="true"></i></span> 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> return (
{ insertValue(contractProperties, propertyName) } <div className="remixui_log" key={index}>
</div>) <TreeViewItem
}) label={
} <div data-id={`remixui_treeviewitem_${propertyName}`} className="remixui_key">{ propertyName } { copyDetails } { questionMark }</div>
}>
{ insertValue(contractProperties, propertyName) }
</TreeViewItem>
</div>
)
})
}
</TreeView>
</div> </div>
modal(selectedContract, log, 'Close', null) modal(selectedContract, log, 'Close', null)

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

@ -1,19 +0,0 @@
'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, ''))
})
}

@ -1,46 +0,0 @@
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 })
})
})
}

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

@ -7,6 +7,7 @@ import { RemixUiCheckbox } from '@remix-ui/checkbox' // eslint-disable-line
import ErrorRenderer from './ErrorRenderer' // eslint-disable-line import ErrorRenderer from './ErrorRenderer' // eslint-disable-line
import { compilation } from './actions/staticAnalysisActions' import { compilation } from './actions/staticAnalysisActions'
import { initialState, analysisReducer } from './reducers/staticAnalysisReducer' import { initialState, analysisReducer } from './reducers/staticAnalysisReducer'
import { OverlayTrigger, Tooltip } from 'react-bootstrap'// eslint-disable-line
const StaticAnalysisRunner = require('@remix-project/remix-analyzer').CodeAnalysis const StaticAnalysisRunner = require('@remix-project/remix-analyzer').CodeAnalysis
const utils = remixLib.util const utils = remixLib.util
@ -56,7 +57,7 @@ export const RemixUiStaticAnalyser = (props: RemixUiStaticAnalyserProps) => {
} }
const [autoRun, setAutoRun] = useState(true) const [autoRun, setAutoRun] = useState(true)
const [slitherEnabled, setSlitherEnabled] = useState(false) const [slitherEnabled, setSlitherEnabled] = useState(false)
const [showSlither, setShowSlither] = useState('hidden') const [showSlither, setShowSlither] = useState(false)
const [categoryIndex, setCategoryIndex] = useState(groupedModuleIndex(groupedModules)) const [categoryIndex, setCategoryIndex] = useState(groupedModuleIndex(groupedModules))
const warningContainer = React.useRef(null) const warningContainer = React.useRef(null)
@ -88,7 +89,24 @@ export const RemixUiStaticAnalyser = (props: RemixUiStaticAnalyserProps) => {
// Reset state // Reset state
dispatch({ type: '', payload: {} }) dispatch({ type: '', payload: {} })
// Show 'Enable Slither Analysis' checkbox // Show 'Enable Slither Analysis' checkbox
if (currentWorkspace && currentWorkspace.isLocalhost === true) setShowSlither('visible') if (currentWorkspace && currentWorkspace.isLocalhost === true) setShowSlither(true)
else {
setShowSlither(false)
setSlitherEnabled(false)
}
})
props.analysisModule.on('manager', 'pluginDeactivated', (plugin) => {
// Hide 'Enable Slither Analysis' checkbox
if (plugin.name === 'remixd') {
// Reset warning state
setWarningState([])
// Reset badge
props.event.trigger('staticAnaysisWarning', [])
// Reset state
dispatch({ type: '', payload: {} })
setShowSlither(false)
setSlitherEnabled(false)
}
}) })
return () => { } return () => { }
}, [props]) }, [props])
@ -245,8 +263,7 @@ export const RemixUiStaticAnalyser = (props: RemixUiStaticAnalyserProps) => {
showWarnings(warningMessage, 'warningModuleName') showWarnings(warningMessage, 'warningModuleName')
props.event.trigger('staticAnaysisWarning', [warningCount]) props.event.trigger('staticAnaysisWarning', [warningCount])
} }
}).catch((error) => { }).catch(() => {
console.log('Error found:', error) // This should be removed once testing done
props.analysisModule.call('terminal', 'log', { type: 'error', value: '[Slither Analysis]: Error occured! See remixd console for details.' }) props.analysisModule.call('terminal', 'log', { type: 'error', value: '[Slither Analysis]: Error occured! See remixd console for details.' })
showWarnings(warningMessage, 'warningModuleName') showWarnings(warningMessage, 'warningModuleName')
}) })
@ -398,17 +415,28 @@ export const RemixUiStaticAnalyser = (props: RemixUiStaticAnalyserProps) => {
/> />
<Button buttonText="Run" onClick={() => run(state.data, state.source, state.file)} disabled={(state.data === null || categoryIndex.length === 0) && !slitherEnabled }/> <Button buttonText="Run" onClick={() => run(state.data, state.source, state.file)} disabled={(state.data === null || categoryIndex.length === 0) && !slitherEnabled }/>
</div> </div>
<div className="d-flex" id="enableSlitherAnalysis"> { showSlither &&
<RemixUiCheckbox <div className="d-flex mt-2" id="enableSlitherAnalysis">
id="enableSlither" <RemixUiCheckbox
inputType="checkbox" id="enableSlither"
onClick={handleSlitherEnabled} inputType="checkbox"
checked={slitherEnabled} onClick={handleSlitherEnabled}
label="Enable Slither Analysis" checked={slitherEnabled}
onChange={() => {}} label="Enable Slither Analysis"
visibility = {showSlither} onChange={() => {}}
/> />
</div>
<a className="mt-1 text-nowrap" href='https://remix-ide.readthedocs.io/en/latest/slither.html#enable-slither-analysis' target={'_blank'}>
<OverlayTrigger placement={'right'} overlay={
<Tooltip className="text-nowrap" id="overlay-tooltip">
<span className="p-1 pr-3" style={{ backgroundColor: 'black', minWidth: '230px' }}>Learn how to use Slither Analysis</span>
</Tooltip>
}>
<i style={{ fontSize: 'medium' }} className={'fal fa-info-circle ml-3'} aria-hidden="true"></i>
</OverlayTrigger>
</a>
</div>
}
</div> </div>
<div id="staticanalysismodules" className="list-group list-group-flush"> <div id="staticanalysismodules" className="list-group list-group-flush">
{Object.keys(groupedModules).map((categoryId, i) => { {Object.keys(groupedModules).map((categoryId, i) => {

@ -1,6 +1,6 @@
{ {
"name": "@remix-project/remix-url-resolver", "name": "@remix-project/remix-url-resolver",
"version": "0.0.24", "version": "0.0.26",
"description": "Solidity import url resolver engine", "description": "Solidity import url resolver engine",
"main": "index.js", "main": "index.js",
"types": "./index.d.ts", "types": "./index.d.ts",
@ -42,5 +42,5 @@
"typescript": "^3.1.6" "typescript": "^3.1.6"
}, },
"typings": "index.d.ts", "typings": "index.d.ts",
"gitHead": "5e91f3010a7198f1b2d90cc7796bda750c58f1ea" "gitHead": "df7abe1c219e361a947031d620f4ae6e3786a4d7"
} }

@ -1,6 +1,6 @@
{ {
"name": "@remix-project/remixd", "name": "@remix-project/remixd",
"version": "0.4.2", "version": "0.5.0",
"description": "remix server: allow accessing file system from remix.ethereum.org and start a dev environment (see help section)", "description": "remix server: allow accessing file system from remix.ethereum.org and start a dev environment (see help section)",
"main": "index.js", "main": "index.js",
"types": "./index.d.ts", "types": "./index.d.ts",

@ -120,9 +120,9 @@ export class SlitherClient extends PluginClient {
allowPaths = allowPathString allowPaths = allowPathString
remaps = remapString.trim() remaps = remapString.trim()
} }
const allowPathsOption: string = allowPaths ? `--allow-paths ${allowPaths}` : '' const allowPathsOption: string = allowPaths ? `--allow-paths ${allowPaths} ` : ''
const optimizeOption: string = optimize ? ' --optimize ' : '' const optimizeOption: string = optimize ? '--optimize ' : ''
const evmOption: string = evmVersion ? ` --evm-version ${evmVersion}` : '' const evmOption: string = evmVersion ? `--evm-version ${evmVersion}` : ''
const solcArgs: string = optimizeOption || evmOption || allowPathsOption ? `--solc-args '${allowPathsOption}${optimizeOption}${evmOption}'` : '' const solcArgs: string = optimizeOption || evmOption || allowPathsOption ? `--solc-args '${allowPathsOption}${optimizeOption}${evmOption}'` : ''
const solcRemaps = remaps ? `--solc-remaps "${remaps}"` : '' const solcRemaps = remaps ? `--solc-remaps "${remaps}"` : ''

816
package-lock.json generated

File diff suppressed because it is too large Load Diff

@ -1,6 +1,6 @@
{ {
"name": "remix-project", "name": "remix-project",
"version": "0.15.0-dev", "version": "0.17.0-dev",
"license": "MIT", "license": "MIT",
"description": "Ethereum Remix Monorepo", "description": "Ethereum Remix Monorepo",
"keywords": [ "keywords": [
@ -130,10 +130,10 @@
}, },
"dependencies": { "dependencies": {
"@erebos/bzz-node": "^0.13.0", "@erebos/bzz-node": "^0.13.0",
"@ethereumjs/block": "^3.3.0", "@ethereumjs/block": "^3.4.0",
"@ethereumjs/common": "^2.3.1", "@ethereumjs/common": "^2.4.0",
"@ethereumjs/tx": "^3.2.1", "@ethereumjs/tx": "^3.3.0",
"@ethereumjs/vm": "^5.4.1", "@ethereumjs/vm": "^5.5.0",
"@remixproject/engine": "^0.3.17", "@remixproject/engine": "^0.3.17",
"@remixproject/engine-web": "^0.3.17", "@remixproject/engine-web": "^0.3.17",
"@remixproject/plugin": "^0.3.17", "@remixproject/plugin": "^0.3.17",
@ -153,7 +153,7 @@
"deep-equal": "^1.0.1", "deep-equal": "^1.0.1",
"document-register-element": "1.13.1", "document-register-element": "1.13.1",
"ethereumjs-util": "^7.0.10", "ethereumjs-util": "^7.0.10",
"ethers": "^5.1.4", "ethers": "^5.4.2",
"express-ws": "^4.0.0", "express-ws": "^4.0.0",
"file-saver": "^2.0.5", "file-saver": "^2.0.5",
"form-data": "^4.0.0", "form-data": "^4.0.0",

@ -1,13 +1,22 @@
# Release process # Release process
This document includes: This document includes:
- how to release the remixd
- how to publish remix libs to NPM - how to publish remix libs to NPM
- how to update remix.ethereum.org - how to update remix.ethereum.org
- how to update remix-alpha.ethereum.org - how to update remix-alpha.ethereum.org
- how to update remix-beta.ethereum.org - how to update remix-beta.ethereum.org
- how to release remix IDE - how to release remix IDE
## RemixD release
- update new version number in remixd libs/remixd/package.json
- nx build remixd
- cd into ./dist/libs/remixd
- npm publish
- create bump PR to master.
## Remix libs release ## Remix libs release
(remove dist and login to npm)
- git fetch origin master - git fetch origin master
- git checkout origin/master - git checkout origin/master
- git checkout -b bumpLibsVersion - git checkout -b bumpLibsVersion
@ -76,4 +85,4 @@ This is not strictly speaking a release. Updating the remix site is done through
## remix-alpha.ethereum.org update ## remix-alpha.ethereum.org update
remix-alpha.ethereum.org is automaticaly updated every time commits are pushed to master remix-alpha.ethereum.org is automaticaly updated every time commits are pushed to master

Loading…
Cancel
Save