diff --git a/apps/remix-ide-e2e/src/tests/editor.test.ts b/apps/remix-ide-e2e/src/tests/editor.test.ts
index 53d2a22ab5..993f43039f 100644
--- a/apps/remix-ide-e2e/src/tests/editor.test.ts
+++ b/apps/remix-ide-e2e/src/tests/editor.test.ts
@@ -92,13 +92,13 @@ module.exports = {
.executeScript('remix.exeCurrent()')
.scrollToLine(32)
.waitForElementPresent('.highlightLine33', 60000)
- .checkElementStyle('.highlightLine33', 'background-color', 'rgb(52, 152, 219)')
+ .checkElementStyle('.highlightLine33', 'background-color', 'rgb(44, 62, 80)')
.scrollToLine(40)
.waitForElementPresent('.highlightLine41', 60000)
- .checkElementStyle('.highlightLine41', 'background-color', 'rgb(52, 152, 219)')
+ .checkElementStyle('.highlightLine41', 'background-color', 'rgb(44, 62, 80)')
.scrollToLine(50)
.waitForElementPresent('.highlightLine51', 60000)
- .checkElementStyle('.highlightLine51', 'background-color', 'rgb(52, 152, 219)')
+ .checkElementStyle('.highlightLine51', 'background-color', 'rgb(44, 62, 80)')
},
'Should remove 1 highlight from source code #group1': '' + function (browser: NightwatchBrowser) {
@@ -111,8 +111,8 @@ module.exports = {
.waitForElementVisible('li[data-id="treeViewLitreeViewItemcontracts/3_Ballot.sol"]')
.click('li[data-id="treeViewLitreeViewItemcontracts/3_Ballot.sol"]')
.waitForElementNotPresent('.highlightLine33', 60000)
- .checkElementStyle('.highlightLine41', 'background-color', 'rgb(52, 152, 219)')
- .checkElementStyle('.highlightLine51', 'background-color', 'rgb(52, 152, 219)')
+ .checkElementStyle('.highlightLine41', 'background-color', 'rgb(44, 62, 80)')
+ .checkElementStyle('.highlightLine51', 'background-color', 'rgb(44, 62, 80)')
},
'Should remove all highlights from source code #group1': function (browser: NightwatchBrowser) {
diff --git a/apps/remix-ide/src/app/editor/editor.js b/apps/remix-ide/src/app/editor/editor.js
index a50b5fd8b6..875835d8ff 100644
--- a/apps/remix-ide/src/app/editor/editor.js
+++ b/apps/remix-ide/src/app/editor/editor.js
@@ -139,7 +139,11 @@ class Editor extends Plugin {
this.on('sidePanel', 'pluginDisabled', (name) => {
this.clearAllDecorationsFor(name)
})
-
+ this.on('fileManager', 'fileClosed', (name) => {
+ if (name === this.currentFile) {
+ this.currentFile = null
+ }
+ })
this.on('theme', 'themeLoaded', (theme) => {
this.currentThemeType = theme.quality
this.renderComponent()
diff --git a/apps/remix-ide/src/app/plugins/config.ts b/apps/remix-ide/src/app/plugins/config.ts
index e44d403a12..3102d555df 100644
--- a/apps/remix-ide/src/app/plugins/config.ts
+++ b/apps/remix-ide/src/app/plugins/config.ts
@@ -18,7 +18,7 @@ export class ConfigPlugin extends Plugin {
const queryParams = new QueryParams()
const params = queryParams.get()
const config = Registry.getInstance().get('config').api
- const param = params[name] ? params[name] : config.get(name)
+ let param = params[name] || config.get(name) || config.get('settings/' + name)
if (param === 'true') return true
if (param === 'false') return false
return param
diff --git a/libs/remix-core-plugin/src/lib/compiler-fetch-and-compile.ts b/libs/remix-core-plugin/src/lib/compiler-fetch-and-compile.ts
index c6a8a6156b..bf8b87bf55 100644
--- a/libs/remix-core-plugin/src/lib/compiler-fetch-and-compile.ts
+++ b/libs/remix-core-plugin/src/lib/compiler-fetch-and-compile.ts
@@ -3,6 +3,8 @@ import { Plugin } from '@remixproject/engine'
import { compile } from '@remix-project/remix-solidity'
import { util } from '@remix-project/remix-lib'
import { toChecksumAddress } from 'ethereumjs-util'
+import { fetchContractFromEtherscan } from './helpers/fetch-etherscan'
+import { fetchContractFromSourcify } from './helpers/fetch-sourcify'
const profile = {
name: 'fetchAndCompile',
@@ -68,48 +70,31 @@ export class FetchAndCompile extends Plugin {
let data
try {
- data = await this.call('sourcify', 'fetchByNetwork', contractAddress, network.id)
+ data = await fetchContractFromSourcify(this, network, contractAddress, targetPath)
} catch (e) {
- setTimeout(_ => this.emit('notFound', contractAddress), 0) // plugin framework returns a time out error although it actually didn't find the source...
- this.unresolvedAddresses.push(contractAddress)
- return localCompilation()
- }
- if (!data || !data.metadata) {
- setTimeout(_ => this.emit('notFound', contractAddress), 0)
- this.unresolvedAddresses.push(contractAddress)
- return localCompilation()
+ this.call('notification', 'toast', e.message)
+ console.log(e) // and fallback to getting the compilation result from etherscan
}
- // set the solidity contract code using metadata
- await this.call('fileManager', 'setFile', `${targetPath}/${network.id}/${contractAddress}/metadata.json`, JSON.stringify(data.metadata, null, '\t'))
- const compilationTargets = {}
- for (let file in data.metadata.sources) {
- const urls = data.metadata.sources[file].urls
- for (const url of urls) {
- if (url.includes('ipfs')) {
- const stdUrl = `ipfs://${url.split('/')[2]}`
- const source = await this.call('contentImport', 'resolve', stdUrl)
- if (await this.call('contentImport', 'isExternalUrl', file)) {
- // nothing to do, the compiler callback will handle those
- } else {
- file = file.replace('browser/', '') // should be fixed in the remix IDE end.
- const path = `${targetPath}/${network.id}/${contractAddress}/${file}`
- await this.call('fileManager', 'setFile', path, source.content)
- compilationTargets[path] = { content: source.content }
- }
- break
- }
+ if (!data) {
+ this.call('notification', 'toast', `contract ${contractAddress} not found in Sourcify, checking in Etherscan..`)
+ try {
+ data = await fetchContractFromEtherscan(this, network, contractAddress, targetPath)
+ } catch (e) {
+ this.call('notification', 'toast', e.message)
+ setTimeout(_ => this.emit('notFound', contractAddress), 0) // plugin framework returns a time out error although it actually didn't find the source...
+ this.unresolvedAddresses.push(contractAddress)
+ return localCompilation()
}
}
- // compile
- const settings = {
- version: data.metadata.compiler.version,
- language: data.metadata.language,
- evmVersion: data.metadata.settings.evmVersion,
- optimize: data.metadata.settings.optimizer.enabled,
- runs: data.metadata.settings.optimizer.runs
+ if (!data) {
+ setTimeout(_ => this.emit('notFound', contractAddress), 0)
+ this.unresolvedAddresses.push(contractAddress)
+ return localCompilation()
}
+ const { settings, compilationTargets } = data
+
try {
setTimeout(_ => this.emit('compiling', settings), 0)
const compData = await compile(
diff --git a/libs/remix-core-plugin/src/lib/helpers/fetch-etherscan.ts b/libs/remix-core-plugin/src/lib/helpers/fetch-etherscan.ts
new file mode 100644
index 0000000000..647547ba47
--- /dev/null
+++ b/libs/remix-core-plugin/src/lib/helpers/fetch-etherscan.ts
@@ -0,0 +1,59 @@
+export const fetchContractFromEtherscan = async (plugin, network, contractAddress, targetPath) => {
+ let data
+ const compilationTargets = {}
+
+ const etherscanKey = await plugin.call('config', 'getAppParameter', 'etherscan-access-token')
+ if (etherscanKey) {
+ const endpoint = network.id == 1 ? 'api.etherscan.io' : 'api-' + network.name + '.etherscan.io'
+ data = await fetch('https://' + endpoint + '/api?module=contract&action=getsourcecode&address=' + contractAddress + '&apikey=' + etherscanKey)
+ data = await data.json()
+ // etherscan api doc https://docs.etherscan.io/api-endpoints/contracts
+ if (data.message === 'OK' && data.status === "1") {
+ if (data.result.length) {
+ if (data.result[0].SourceCode === '') throw new Error('contract not verified')
+ if (data.result[0].SourceCode.startsWith('{')) {
+ data.result[0].SourceCode = JSON.parse(data.result[0].SourceCode.replace(/(?:\r\n|\r|\n)/g, '').replace(/^{{/,'{').replace(/}}$/,'}'))
+ }
+ }
+ } else throw new Error('unable to retrieve contract data ' + data.message)
+ } else throw new Error('unable to try fetching the source code from etherscan: etherscan access token not found. please go to the Remix settings page and provide an access token.')
+
+ if (!data || !data.result) {
+ return null
+ }
+
+ if (typeof data.result[0].SourceCode === 'string') {
+ const fileName = `${targetPath}/${network.id}/${contractAddress}/${data.result[0].ContractName}.sol`
+ await plugin.call('fileManager', 'setFile', fileName , data.result[0].SourceCode)
+ compilationTargets[fileName] = { content: data.result[0].SourceCode }
+ } else if (data.result[0].SourceCode && typeof data.result[0].SourceCode == 'object') {
+ const sources = data.result[0].SourceCode.sources
+ for (let [file, source] of Object.entries(sources)) { // eslint-disable-line
+ file = file.replace('browser/', '') // should be fixed in the remix IDE end.
+ file = file.replace(/^\//g, '') // remove first slash.
+ if (await plugin.call('contentImport', 'isExternalUrl', file)) {
+ // nothing to do, the compiler callback will handle those
+ } else {
+ const path = `${targetPath}/${network.id}/${contractAddress}/${file}`
+ const content = (source as any).content
+ await plugin.call('fileManager', 'setFile', path, content)
+ compilationTargets[path] = { content }
+ }
+ }
+ }
+ let runs = 0
+ try {
+ runs = parseInt(data.result[0].Runs)
+ } catch (e) {}
+ const settings = {
+ version: data.result[0].CompilerVersion.replace(/^v/, ''),
+ language: 'Solidity',
+ evmVersion: data.result[0].EVMVersion.toLowerCase(),
+ optimize: data.result[0].OptimizationUsed === '1',
+ runs
+ }
+ return {
+ settings,
+ compilationTargets
+ }
+}
\ No newline at end of file
diff --git a/libs/remix-core-plugin/src/lib/helpers/fetch-sourcify.ts b/libs/remix-core-plugin/src/lib/helpers/fetch-sourcify.ts
new file mode 100644
index 0000000000..698c062b44
--- /dev/null
+++ b/libs/remix-core-plugin/src/lib/helpers/fetch-sourcify.ts
@@ -0,0 +1,46 @@
+export const fetchContractFromSourcify = async (plugin, network, contractAddress, targetPath) => {
+ let data
+ const compilationTargets = {}
+
+ try {
+ data = await plugin.call('sourcify', 'fetchByNetwork', contractAddress, network.id)
+ } catch (e) {
+ console.log(e)
+ }
+
+ if (!data || !data.metadata) {
+ return null
+ }
+
+ // set the solidity contract code using metadata
+ await plugin.call('fileManager', 'setFile', `${targetPath}/${network.id}/${contractAddress}/metadata.json`, JSON.stringify(data.metadata, null, '\t'))
+ for (let file in data.metadata.sources) {
+ const urls = data.metadata.sources[file].urls
+ for (const url of urls) {
+ if (url.includes('ipfs')) {
+ const stdUrl = `ipfs://${url.split('/')[2]}`
+ const source = await plugin.call('contentImport', 'resolve', stdUrl)
+ file = file.replace('browser/', '') // should be fixed in the remix IDE end.
+ if (await plugin.call('contentImport', 'isExternalUrl', file)) {
+ // nothing to do, the compiler callback will handle those
+ } else {
+ const path = `${targetPath}/${network.id}/${contractAddress}/${file}`
+ await plugin.call('fileManager', 'setFile', path, source.content)
+ compilationTargets[path] = { content: source.content }
+ }
+ break
+ }
+ }
+ }
+ const settings = {
+ version: data.metadata.compiler.version,
+ language: data.metadata.language,
+ evmVersion: data.metadata.settings.evmVersion,
+ optimize: data.metadata.settings.optimizer.enabled,
+ runs: data.metadata.settings.optimizer.runs
+ }
+ return {
+ settings,
+ compilationTargets
+ }
+}
diff --git a/libs/remix-solidity/src/compiler/compiler-input.ts b/libs/remix-solidity/src/compiler/compiler-input.ts
index 8b00c647ee..6e426ddaab 100644
--- a/libs/remix-solidity/src/compiler/compiler-input.ts
+++ b/libs/remix-solidity/src/compiler/compiler-input.ts
@@ -19,9 +19,13 @@ export default (sources: Source, opts: CompilerInputOptions): string => {
}
}
}
- }
+ }
if (opts.evmVersion) {
- o.settings.evmVersion = opts.evmVersion
+ if (opts.evmVersion.toLowerCase() == 'default') {
+ opts.evmVersion = null
+ } else {
+ o.settings.evmVersion = opts.evmVersion
+ }
}
if (opts.language) {
o.language = opts.language
diff --git a/libs/remix-ui/debugger-ui/src/lib/debugger-ui.tsx b/libs/remix-ui/debugger-ui/src/lib/debugger-ui.tsx
index de26b84af9..0524f22170 100644
--- a/libs/remix-ui/debugger-ui/src/lib/debugger-ui.tsx
+++ b/libs/remix-ui/debugger-ui/src/lib/debugger-ui.tsx
@@ -91,7 +91,7 @@ export const DebuggerUI = (props: DebuggerUIProps) => {
if (!lineColumnPos) {
await debuggerModule.discardHighlight()
setState(prevState => {
- return { ...prevState, sourceLocationStatus: 'Source location not available.' }
+ return { ...prevState, sourceLocationStatus: 'Source location not available, neither in Sourcify nor in Etherscan. Please make sure the Etherscan api key is provided in the settings.' }
})
return
}
@@ -334,6 +334,15 @@ export const DebuggerUI = (props: DebuggerUIProps) => {
{ gitAccessTokenText }
-{ gitAccessTokenText2 }
- +{ labels[type].message1 }
+{ labels[type].message2 }
+